Migrate Octokit calls to AI SDK tools
createGithubTools(options?)
Returns a record of AI SDK tools you can pass to generateText or streamText. All options are optional, the SDK reads GITHUB_TOKEN from your environment by default:
type GithubToolsOptions = {
token?: GithubTokenInput
requireApproval?: ToolApprovalMode | Partial<Record<GithubWriteToolName, ToolApprovalMode>>
evaluation?: GithubEvaluationOptions
overrides?: Partial<Record<string, ToolOverrides>>
preset?: GithubToolPreset | GithubToolPreset[]
author?: CommitIdentity
committer?: CommitIdentity
coAuthors?: CommitIdentity[]
}
type ToolApprovalMode = boolean | 'auto'
// Tuning for 'auto' approval and preset: 'auto'. Every field is optional.
type GithubEvaluationOptions = {
model?: Experimental_EvaluationModel // default 'typesafe-ai/jev'
maxRisk?: number // default 1, on a 0–2 scale
minIntent?: number // default 0.6
minPresetProbability?: number // default 0.7
maxPresets?: number // default 2
}
// Providers receive the tool call the token is requested for (undefined outside a tool call).
type GithubTokenInput = string | ((call?: GithubTokenCall) => Promise<string>)
type GithubTokenCall = {
toolName: GithubToolName
input: unknown // after context defaults
owner?: string // undefined for tools without a repository target
repo?: string
}
type CommitIdentity = {
name: string
email: string
}
type GithubToolPreset =
| 'code-review'
| 'issue-triage'
| 'repo-explorer'
| 'ci-ops'
| 'security-audit'
| 'release-manager'
| 'discussion-moderator'
| 'notification-inbox'
| 'pr-author'
| 'maintainer'
Minimal usage, reads GITHUB_TOKEN automatically:
import { createGithubTools } from '@github-tools/sdk'
const tools = createGithubTools()
With a preset and explicit token:
import { createGithubTools } from '@github-tools/sdk'
const tools = createGithubTools({
token: 'github_pat_xxxxxxxxxxxx',
preset: 'repo-explorer',
})
See Scope with presets for preset details and Control write safety for approval options.
Working context
Pass context to default owner / repo / pullNumber / issueNumber / ref on tool inputs. Matching schema fields become optional and fill from context when omitted. Agents also get a short system-prompt block describing the context:
import { createGithubTools, createGithubAgent } from '@github-tools/sdk'
const tools = createGithubTools({
preset: 'code-review',
context: { owner: 'vercel', repo: 'ai', pullNumber: 42 },
})
const agent = createGithubAgent({
model: 'anthropic/claude-opus-5.5',
preset: 'code-review',
context: { owner: 'vercel', repo: 'ai', pullNumber: 42 },
})
listPullRequestFiles, getCommit, and compareCommits omit diff patches by default (includePatch: false). Set includePatch: true — optionally with filenames on listPullRequestFiles — to fetch targeted diffs.
getPullRequest, getIssue, and release getters truncate bodies by default (detail: 'summary'). Pass detail: 'full' for the complete text. getIssueContext defaults to detail: 'full' (one-shot) and returns labelNames rather than full label objects.
Prefer composite tools (getPullRequestContext, getIssueContext, getReleaseContext, getCiFailureContext) for multi-part reads. Prefer getFileContent with startLine/endLine or maxLines for large files.
Tool overrides
The overrides option lets you customize any AI SDK tool() property on a per-tool basis, keyed by tool name:
import { createGithubTools } from '@github-tools/sdk'
const tools = createGithubTools({
overrides: {
deleteGist: { needsApproval: false },
listIssues: { description: 'List bugs for the current sprint' },
},
})
import type { ToolOverrides } from '@github-tools/sdk'
Supported override properties:
| Property | Type | Description |
|---|---|---|
description | string | Custom tool description for the model |
title | string | Human-readable title |
strict | boolean | Strict mode for input generation |
needsApproval | boolean | function | Gate execution behind approval |
providerOptions | ProviderOptions | Provider-specific metadata |
onInputStart | function | Callback when argument streaming starts |
onInputDelta | function | Callback on each streaming delta |
onInputAvailable | function | Callback when full input is available |
toModelOutput | function | Custom mapping of tool result to model output |
Core properties (execute, inputSchema, outputSchema) cannot be overridden.
Rate-limit metadata
Object-shaped tool results include a rateLimit field from the last GitHub response (x-ratelimit-remaining, x-ratelimit-limit, x-ratelimit-reset, x-ratelimit-resource, and retry-after when present). REST list tools now return objects ({ items, hasMore, … } or keyed collections), so they carry rateLimit too. The field is stripped before the model sees the output (toModelOutput); hooks, channels, and the chat UI still receive it.
import type { GithubRateLimit } from '@github-tools/sdk'
const remaining = (result as { rateLimit?: GithubRateLimit }).rateLimit?.remaining
resource is core, search, or graphql — search is the tightest bucket. On HTTP 403/429 the thrown error message also includes remaining/reset.
Commit attribution
The author, committer, and coAuthors options control how commits are attributed when using createOrUpdateFile or mergePullRequest:
import { createGithubTools } from '@github-tools/sdk'
const tools = createGithubTools({
token: 'github_pat_xxxxxxxxxxxx',
coAuthors: [
{ name: 'my-bot[bot]', email: '12345+my-bot[bot]@users.noreply.github.com' }
]
})
| Option | Type | Description |
|---|---|---|
author | CommitIdentity | The person who wrote the code. Falls back to the authenticated user. |
committer | CommitIdentity | The person who applied the commit. Falls back to the authenticated user. |
coAuthors | CommitIdentity[] | Additional contributors. Added as Co-authored-by trailers to commit messages. |
Commits made via the GitHub API are automatically signed by GitHub's web-flow key, passing branch protection rules that require signed commits.
See Commit attribution for detailed guidance.
createGithubAgent(options)
Returns a ToolLoopAgent with GitHub tools and system instructions pre-configured. The token is also auto-detected from GITHUB_TOKEN:
type GithubAgentOptions = {
model: string
token?: GithubTokenInput
preset?: GithubToolPreset | GithubToolPreset[] | 'auto'
requireApproval?: ToolApprovalMode | Partial<Record<GithubWriteToolName, ToolApprovalMode>>
evaluation?: GithubEvaluationOptions
system?: string
author?: CommitIdentity
committer?: CommitIdentity
coAuthors?: CommitIdentity[]
}
Use this when you want:
- reusable
.generate()/.stream()calls across multiple prompts - preset-aware system instructions without manual wiring
- a centralized agent definition shared across your codebase
preset: 'auto' selects presets per call from the latest user message, and requireApproval: 'auto' skips approval for low-risk writes the user asked for. See Let the agent pick presets and Auto approval.
import { createGithubAgent } from '@github-tools/sdk'
const agent = createGithubAgent({
model: 'anthropic/claude-opus-5.5',
preset: 'code-review',
system: 'You review PRs for security issues. Cite file paths and line numbers.',
})
createDurableGithubAgent(options)
Returns a WorkflowAgent for use inside a Vercel Workflow function ("use workflow"). Each LLM step and each GitHub tool invocation runs as a durable, retryable workflow step. Import from the workflow subpath:
import { createDurableGithubAgent } from '@github-tools/sdk/workflow'
Requires optional peer dependencies workflow and @ai-sdk/workflow, see Installation.
Options align with createGithubAgent (model, token, preset, requireApproval, instructions, additionalInstructions, stopWhen, temperature, and other agent options passed through). Write tools honor requireApproval via needsApproval: the workflow pauses until the user approves or denies.
import { createDurableGithubAgent } from '@github-tools/sdk/workflow'
import { getWritable } from 'workflow'
import type { ModelCallStreamPart, ModelMessage } from 'ai'
export async function githubAssistant(messages: ModelMessage[], token: string) {
'use workflow'
const agent = createDurableGithubAgent({
model: 'anthropic/claude-opus-5.5',
token,
preset: 'maintainer',
requireApproval: true,
})
const writable = getWritable<ModelCallStreamPart>()
await agent.stream({ messages, writable })
}
Conceptual overview: Durable workflows (Vercel Workflow).
githubExtension(options): eve extension
The recommended way to add GitHub tools to an eve agent. From @github-tools/eve-extension, mounted under agent/extensions/:
import githubExtension from '@github-tools/eve-extension'
export default githubExtension({
preset: 'code-review',
requireApproval: {
mergePullRequest: true,
createIssue: 'once',
},
})
options mirrors EveGithubToolsOptions below (token, connector, connect, preset, include, exclude, requireApproval, overrides, author/committer/coAuthors). Full config table and Connect setup: eve extension guide. Package README: packages/github-tools-eve-extension.
createGithubTools(options) : eve (deprecated)
githubExtension from @github-tools/eve-extension instead. This direct import keeps working but is no longer the recommended path. Runtime helpers for the extension are on @github-tools/sdk/eve-runtime (not deprecated).Returns a defineDynamic sentinel for eve's agent/tools/ directory. Import from @github-tools/sdk/eve:
import { createGithubTools } from '@github-tools/sdk/eve'
Requires optional peer dependencies eve and ai v7, see Installation and eve (direct import).
import { createGithubTools } from '@github-tools/sdk/eve'
export default createGithubTools({
preset: 'code-review',
requireApproval: {
mergePullRequest: true,
createIssue: 'once',
addPullRequestComment: false,
},
})
@github-tools/sdk/eve-runtime
Shared eve primitives for @github-tools/eve-extension and advanced integrations: tool descriptors, executeGithubEveTool, formatGithubEveToolOutput / hasGithubEveToolModelOutput (built-in toModelOutput lookup by tool name), approval helpers (mapEveApprovalValue, resolveEveApproval), and related types. Not deprecated — this is the supported low-level surface the extension imports. Prefer the extension mount for agents; use this subpath only when building on top of the same runtime.
import {
listEveToolDescriptors,
executeGithubEveTool,
formatGithubEveToolOutput,
hasGithubEveToolModelOutput,
mapEveApprovalValue,
resolveEveApproval,
} from '@github-tools/sdk/eve-runtime'
type EveGithubToolsOptions = {
token?: GithubTokenInput
preset?: GithubToolPreset | GithubToolPreset[]
include?: GithubToolName[]
exclude?: GithubToolName[]
requireApproval?: boolean | Partial<Record<GithubWriteToolName, EveApprovalValue>>
overrides?: EveToolOverrides
author?: CommitIdentity
committer?: CommitIdentity
coAuthors?: CommitIdentity[]
}
type EveApprovalValue =
| boolean
| 'always'
| 'once'
| 'never'
| Approval // from eve/tools
include is a set of tool names. Pass it alone to hand-pick an exact set, or alongside preset to add tools the preset is missing (the effective set is the union of both). exclude removes tool names from that resolved preset + include set, use it to drop a couple of tools from a larger preset. Also exports individual eve tool factories (listPullRequests(), createIssue(), …) for one-tool-per-file layouts. Approval supports once, predicates, and eve helper passthrough. Unlike the Workflow subpath, approval is enforced at runtime.
connectGithubTools(connector, options?)
Import from @github-tools/sdk/connect. Returns the same tool record as createGithubTools, backed by a Vercel Connect connector. connector is a GithubConnectorInput: a name string or a () => string | Promise<string> resolver, re-resolved on every call (e.g. to pick a connector per environment or tenant). Scopes are derived from preset unless overridden in connect.scopes:
import { connectGithubTools } from '@github-tools/sdk/connect'
const tools = connectGithubTools('github/my-connector', {
preset: 'code-review',
connect: {
installationId: 'inst_abc',
repositories: ['my-org/my-repo'],
},
})
type ConnectGithubToolsOptions = GithubToolsOptions & {
connect?: GithubConnectParams | GithubConnectParamsResolver
}
type GithubConnectParams = Omit<ConnectTokenParams, 'subject'> & {
subject?: ConnectTokenSubject
repositories?: string[]
}
type GithubConnectParamsResolver = (call?: GithubTokenCall) => GithubConnectParams | Promise<GithubConnectParams>
type GithubConnectorInput = string | (() => string | Promise<string>)
subject defaults to { type: 'app' } (the project's GitHub App installation). Pass { type: 'user', id } to mint a token for that user's own connection — see per-user tokens. See the Vercel Connect guide for the dynamic connector example.
App-subject tokens target the GitHub App installation that owns the call's repository: when the call has owner / repo and the params set none of installationId, authorizationDetails or repositories, authorizationDetails: [{ type: 'github_app_installation', org: owner, repositories: [repo] }] is added. See One App installed on several accounts. A connect resolver runs on every tool call with its GithubTokenCall for other per-call rules. Scopes still derive from preset unless the resolved params set scopes.
connectGithubTools(connector, options?): eve (deprecated)
connector directly to githubExtension instead; no separate Connect import is needed.Import from @github-tools/sdk/connect/eve. Same as the AI SDK variant but returns a defineDynamic sentinel. Set build.externalDependencies: ['@vercel/connect'] in agent.ts until eve externalizes transitive Connect imports from workspace-linked packages (TODO(eve-connect-bundle)).
import { connectGithubTools } from '@github-tools/sdk/connect/eve'
export default connectGithubTools('github/my-connector', {
preset: 'maintainer',
})
connectGithubToken(connector, options?)
Returns a lazy GithubTokenInput backed by getToken. connector accepts the same GithubConnectorInput (name or resolver function) as connectGithubTools. Use with createGithubTools when you only need the token provider:
import { connectGithubToken } from '@github-tools/sdk/connect'
import { createGithubTools } from '@github-tools/sdk'
const tools = createGithubTools({
preset: 'ci-ops',
token: connectGithubToken('github/my-connector', { preset: 'ci-ops' }),
})
Pass the same preset (and the same include / exclude, when you use them) to connectGithubToken: it derives Connect scopes independently of the selection given to createGithubTools. With include alone, scopes follow the selected tools rather than the full preset union.
connectGithubScopesForPreset(preset?)
Returns Vercel Connect scope strings for a preset or combined presets. Without a preset, returns the union of all preset scopes.
connectGithubScopesForSelection({ preset?, include?, exclude? })
Same mapping as connectGithubToken uses when params.scopes is omitted. With no include / exclude, identical to connectGithubScopesForPreset. When either is set, scopes are derived from the resolved tool names via connectGithubScopesForTools.
connectGithubScopesForTools(names)
Returns the Connect scopes covering exactly the given tool names. Always includes metadata:read. Gist and notification tools contribute no scopes (installation tokens cannot call those APIs).
resolveGithubToken(token?)
Resolves a GithubTokenInput (token string, async provider, or the process.env.GITHUB_TOKEN fallback) to a token string. Throws when no token is available.
createOctokit(token?)
Returns a configured @octokit/rest instance. Use this when you need lower-level GitHub API access or want to build custom tool factories:
import { createOctokit, resolveGithubToken } from '@github-tools/sdk'
const octokit = createOctokit(await resolveGithubToken())
const { data } = await octokit.repos.get({ owner: 'HugoRCD', repo: 'github-tools' })
githubToolsErrors
The evlog error catalog behind every classifiable SDK failure (github_tools.* codes with why/fix fields). Exported from @github-tools/sdk and @github-tools/sdk/eve-runtime for consumers who want to match on specific codes or throw catalog errors from custom tools. See the errors guide for the full code list and the model-facing shape.
import { githubToolsErrors } from '@github-tools/sdk'
import { parseError } from 'evlog'
const { code, why, fix } = parseError(error)
if (code === 'github_tools.RATE_LIMITED') { /* back off */ }
TanStack AI
Import these APIs from @github-tools/sdk/tanstack. See the TanStack AI guide for installation and complete examples.
createGithubTools(options?)
Returns a native TanStack server-tool array. Accepts token, explicit or composed preset, context, requireApproval, author, committer, coAuthors, and per-tool overrides. Overrides support description and boolean needsApproval; execution and schemas are fixed. Named tool factories accept a token, with approval options on write factories.
Static approval uses TanStack's native flow. For requireApproval: 'auto', add the middleware below. The tool array alone keeps those writes approval-required.
createGithubAgent(options)
Accepts the tool options above, plus:
| Option | Behavior |
|---|---|
adapter | Required TanStack chat adapter |
preset | A preset, preset array, or 'auto' for routing new invocations |
evaluation | TanStack evaluation adapter and shared thresholds |
instructions | Replaces the preset system prompt |
additionalInstructions | Appends to the preset prompt; ignored when instructions is set |
modelOptions | Provider-specific model settings |
agentLoopStrategy | TanStack loop strategy; defaults to TanStack's limit |
agent.generate({ prompt }) returns Promise<string> and throws when the run ends on an interrupt. agent.stream({ prompt }) returns the native TanStack event stream. Use stream() for approval UIs. Both accept messages instead of prompt, and TanStack call options such as threadId, runId, parentRunId, resume, and abortController. Supply conversation history on each call; the agent does not retain it. Auto-preset continuations skip routing and expose repo-explorer plus pending GitHub tools.
createGithubApprovalMiddleware({ tools, evaluation? })
Returns TanStack middleware for auto approval. Pass the same tools array to the middleware and chat(), and register githubToolApproval in chat({ interrupts }). The prebuilt agent does this automatically.
evaluation accepts a TanStack adapter and the shared maxRisk, minIntent, minPresetProbability, and maxPresets thresholds. The default adapter is vercelGatewayDecider('typesafe-ai/jev'); Gateway is optional when a custom adapter is supplied.
githubToolApproval
Import this shared interrupt definition from @github-tools/sdk/tanstack/interrupts, including in browser code. Register it on the client to handle auto calls that need human review. Its payload contains toolCallId, toolName, and resolved input; its response is { approved: boolean }. Continue using TanStack's message history and resume data. Decisions apply only to the reviewed call and arguments.
External references
Tools Catalog
Every available tool, grouped by domain and write-safety status. Tools run as durable workflow steps when used with Vercel Workflow.
eve (direct import)
Deprecated. The direct @github-tools/sdk/eve import registers all 84 tools via defineDynamic with durable human-in-the-loop approval. Prefer the eve extension for new agents.