Build a GitHub agent with the eve extension
eve is Vercel's filesystem-first agent framework: an agent is a folder with instructions, a model config, and tools. @github-tools/eve-extension packages all 42 GitHub tools as a mountable eve extension — a single pnpm add and a one-line mount under agent/extensions/, no CLI setup, and no direct SDK import in agent/tools/.
@github-tools/sdk/eve, is deprecated in its favor — it keeps working, but new agents should mount the extension instead.Add GitHub tools to an eve agent via the extension
The whole agent
Three files. That's the entire thing:
You are a GitHub assistant. Use the GitHub tools to inspect repos, PRs, and issues.
Ask before merging or closing anything destructive.
import { defineAgent } from 'eve'
export default defineAgent({
model: 'anthropic/claude-sonnet-5',
})
import githubExtension from '@github-tools/eve-extension'
export default githubExtension({
preset: 'maintainer',
})
Run it:
npx eve dev
You now have a GitHub agent that can read repos, review PRs, triage issues, and manage CI — with every write operation gated behind durable approval by default. The full example lives in examples/eve-extension-agent/ (or pnpm dev:eve-extension-agent from the monorepo root).
Install
pnpm add @github-tools/eve-extension
eve is a required peer dependency (which itself requires ai v7):
pnpm add eve
You still need GITHUB_TOKEN (or a Vercel Connect connector, below). See Installation.
Mount it
Drop a file under agent/extensions/ — the filename becomes the tool namespace:
import githubExtension from '@github-tools/eve-extension'
export default githubExtension({
connector: 'github/my-connector', // or token: process.env.GITHUB_TOKEN
preset: 'code-review',
requireApproval: {
addPullRequestComment: ({ toolInput }) => toolInput?.owner !== 'vercel-labs',
},
})
Tools are exposed to the model as <namespace>__<toolName>, where <namespace> comes from the mount file's name — agent/extensions/github.ts yields github__listPullRequests, github__createIssue, and so on.
code-review is used above (rather than maintainer) because it pairs cleanly with a Connect connector — maintainer and repo-explorer include gist tools, and GitHub only grants gist access to user access tokens, never the installation tokens Connect mints, so gist calls 403 over Connect (see Tokens & Auth). The requireApproval predicate above is a real gate, not a no-op: write tools already require approval by always() by default, so { mergePullRequest: true } would change nothing — a predicate is what actually narrows or loosens the default.
Pick exact tools
preset scopes to one of five predefined groups. To hand-pick tools instead — standalone, or layered on top of a preset — use include and exclude:
import githubExtension from '@github-tools/eve-extension'
export default githubExtension({
include: ['getRepository', 'listPullRequests', 'mergePullRequest'],
})
include adds to preset — the effective set is the union of both, so you can pull in a tool a preset is missing without switching to a bigger preset:
export default githubExtension({
preset: 'code-review', // read-only PR review tools
include: ['createIssue'], // + one write tool code-review doesn't include
})
exclude removes tool names from whatever preset + include resolved to — useful for dropping a couple of tools you don't want exposed from a larger preset:
export default githubExtension({
preset: 'maintainer', // all 42 tools
exclude: ['createRepository', 'deleteGist'], // minus these two
})
Config schema
| Field | Type | Notes |
|---|---|---|
token | string? | Falls back to GITHUB_TOKEN when omitted and connector is not set |
connector | string | (() => string | Promise<string>) | Vercel Connect connector name, or a resolver to pick one dynamically (e.g. per environment/tenant); takes priority over token |
connect | record? | Passed through to getToken when connector is set |
preset | preset name or array | code-review, issue-triage, ci-ops, repo-explorer, maintainer — see Presets |
include | string[]? | Tool names to add on top of preset (union), or the full set standalone — see Pick exact tools |
exclude | string[]? | Tool names to remove from the resolved preset + include set |
requireApproval | boolean | record | Global or per-tool; per-tool values may be 'once', 'always', 'never', or predicate functions |
overrides | record | Per-tool description / approval / toModelOutput / outputSchema |
author / committer / coAuthors | commit identity | Attribution for commit-creating tools — see Commit Attribution |
Durable approval, done right
Approval pauses the session durably until a human responds, and policies are expressive:
| Value | Maps to | Behavior |
|---|---|---|
true / 'always' | always() | Require approval on every call |
false / 'never' | never() | Skip approval |
'once' | once() | Approve once per session, then auto-allow |
| predicate | custom Approval | Input-dependent gate (toolInput, session context) |
Default (no requireApproval): all write tools → always(). Unlisted write tools keep the always() fail-safe default. Read tools never require approval. Details: Control write safety.
Vercel Connect
Skip GITHUB_TOKEN entirely and mint the token from a Connect connector — pass connector directly in the mount config, no separate connectGithubTools import needed:
import githubExtension from '@github-tools/eve-extension'
export default githubExtension({
connector: 'github/my-connector',
preset: 'code-review',
})
Unlike the deprecated direct import, no build.externalDependencies workaround is needed in agent/agent.ts — the extension is pre-built via eve extension build and loaded through eve's extension mechanism rather than inlined from a workspace-linked source import.
@vercel/connect is an optional peer dependency of the extension — install it only when using connector. connector also accepts a () => string | Promise<string> resolver for picking a connector per environment or tenant — see dynamic connector selection. See Vercel Connect for the connector setup checklist and multi-tenant scoping.
Idempotency
eve replays completed steps but re-runs steps interrupted mid-execution:
| Tool | Idempotency |
|---|---|
createOrUpdateFile | Natural when content + sha unchanged |
closeIssue | Natural when already closed |
createBranch | Natural when branch exists at same SHA |
addIssueComment, createIssue, mergePullRequest, … | Not idempotent |
Gate non-idempotent writes behind always() or once() where replay safety matters.
Migrating from the direct import
If you have an existing agent using @github-tools/sdk/eve directly in agent/tools/, move to the extension in three steps:
pnpm add @github-tools/eve-extensionand remove the now-unused directeve/ai/zodinstall if nothing else in the agent needs them.- Delete
agent/tools/github.tsand createagent/extensions/github.tsexportinggithubExtension({ ...same options... })instead ofcreateGithubTools({ ...same options... }). Options (preset,include,exclude,requireApproval,overrides,author/committer/coAuthors,token) are unchanged. - If you used
connectGithubToolsfrom@github-tools/sdk/connect/eve, drop it — passconnectordirectly togithubExtensioninstead. - If you cherry-picked single-tool factories (e.g.
listPullRequests()exported alone from its own file), replace eachagent/tools/*.tsfile with aninclude: [...]list in the singleagent/extensions/github.tsmount — see Pick exact tools.
One behavior change to be aware of: tool names gain the <namespace>__ prefix described above, so update any code that references tool names by their bare string (requireApproval and include keys stay bare — only the exposed model-facing tool name gets the prefix).
eve extension vs direct import vs AI SDK vs Workflow SDK
| eve extension | eve (direct import) | AI SDK | Workflow SDK | |
|---|---|---|---|---|
| Import | @github-tools/eve-extension | @github-tools/sdk/eve | @github-tools/sdk | @github-tools/sdk/workflow |
| Mount point | agent/extensions/ | agent/tools/ | anywhere | workflow function |
| Status | Recommended | Deprecated | Active | Active |
| Tool registration | githubExtension() mount | defineDynamic in agent/tools/ | createGithubTools() object | createGithubTools() in workflow |
| Tool naming | <namespace>__<toolName> | bare tool name | bare tool name | bare tool name |
| Approval | always / once / predicates | always / once / predicates | boolean needsApproval | boolean needsApproval (durable pause) |
| Durability | eve session (filesystem-first) | eve session (filesystem-first) | in-process | "use workflow" steps |
External references
- How to build a GitHub agent with eve and GitHub tools
- eve documentation
- eve extensions
- Dynamic capabilities (bundled with the
evepackage) - Human-in-the-loop
Agent Skills
Install the GitHub tools agent skill from github-tools.com for Cursor, Claude Code, and other assistants.
eve (direct import)
Deprecated — the direct @github-tools/sdk/eve import registers all 42 tools via defineDynamic with durable human-in-the-loop approval. Prefer the eve extension for new agents.