vercel-labs/github-tools

GitHub tools and agents for the Vercel AI SDK, wrap GitHub's API as AI SDK tools ready to plug into any agent

TypeScript

132

182 commits

updated Oct 8, 2026

See the code

README

GitHub tools banner

@github-tools/sdk

npm version npm downloads TypeScript license

Give any agent GitHub access. A typed tool layer for GitHub AI agents, with presets, human approval, and durable execution. Works with eve, the AI SDK, TanStack AI, Vercel Workflow, and Chat SDK.

Docs: github-tools.com

Why not the GitHub MCP server, gh CLI, or raw Octokit?

They all reach the GitHub API, but none of them were built as an agent's tool layer. An agent still needs a schema it can fill reliably, a safety gate before it merges a PR, output shaped to fit a context window, and a way to survive a crash mid-task.

@github-tools/sdkGitHub MCP servergh CLIRaw Octokit
IntegrationNative AI SDK and TanStack AI toolsMCP wire protocol via a separate processShell-out from the agentHand-written per call
Human approvalBuilt in, on by defaultHost-dependent, inconsistentNoneYou build it
Durable / retryableWorkflow entry point uses "use step"NoNoNo
Scoped by taskPresets (10 built-in)Full server surface, or manual filteringFull CLI surfaceYou build it
Token-efficient outputShaped and truncated by designRaw API responsesRaw text, needs parsingRaw API responses
Native eve / Workflow / Chat SDKYesNoNoNo

Pick your path

What are you buildingStart here
A standalone GitHub agent, fast: 3 files, durable approvaleve extension
Scripts, chat backends, or an existing AI SDK appQuick Start below
A TanStack AI appTanStack AI
Production agents that must survive restarts and timeoutsDurable Agents
A GitHub, Slack, or Discord botChat SDK docs

84 tools cover repositories, branches, pull requests, issues, reactions, discussions, notifications, commits, releases, checks and statuses, search, gists, and workflows. See the full Tools Catalog. Write operations support granular approval control out of the box.

Installation

pnpm add @github-tools/sdk

ai and zod are peer dependencies (ai v6 or v7; the eve subpath requires v7):

pnpm add ai zod

Quick Start

// list-prs.ts
import { createGithubTools } from '@github-tools/sdk'
import { generateText } from 'ai'

const result = await generateText({
  model: yourModel,
  tools: createGithubTools({ token: process.env.GITHUB_TOKEN! }),
  prompt: 'List the open pull requests on vercel/ai and summarize them.',
})

Presets

Use preset to get only the tools relevant to a specific use case:

// Code-review agent: PRs, commits, file content, and comments
createGithubTools({ token, preset: 'code-review' })

// Issue triage: read/create/close issues, search
createGithubTools({ token, preset: 'issue-triage' })

// Read-only exploration: browse repos without write access
createGithubTools({ token, preset: 'repo-explorer' })

// Security audit: read-only exploration, PR/CI visibility, plus issue creation to report findings
createGithubTools({ token, preset: 'security-audit' })

// Release manager: releases, compare diff, commits, workflow runs, pull requests
createGithubTools({ token, preset: 'release-manager' })

// Discussion moderator: Discussions plus light issue context
createGithubTools({ token, preset: 'discussion-moderator' })

// Notification inbox: triage user notifications (needs a Notifications PAT)
createGithubTools({ token, preset: 'notification-inbox' })

// PR author: branches, file edits, and opening PRs
createGithubTools({ token, preset: 'pr-author' })

// Full catalog: all tools (same as omitting preset)
createGithubTools({ token, preset: 'maintainer' })

Presets are composable, pass an array to combine them:

createGithubTools({ token, preset: ['code-review', 'issue-triage'] })
PresetTools included
code-reviewgetPullRequest, listPullRequests, listPullRequestFiles, listPullRequestReviews, listPullRequestReviewThreads, getPullRequestContext, getFileContent, listCommits, getCommit, getBlame, compareCommits, getRepository, listBranches, searchCode, listCheckRuns, getCombinedStatus, updatePullRequest, addPullRequestComment, updatePullRequestComment, deletePullRequestComment, createPullRequestReview, replyToReviewComment, resolveReviewThread, requestReviewers
issue-triagelistIssues, getIssueContext, listIssueComments, createIssue, addIssueComment, updateIssueComment, deleteIssueComment, closeIssue, updateIssue, addLabels, removeLabel, createLabel, updateLabel, deleteLabel, addAssignees, removeAssignees, listIssueReactions, addIssueReaction, listCommentReactions, addCommentReaction, getRepository, searchRepositories, searchCode, searchIssues
repo-explorerAll read-only tools including discussions, gists, workflows, checks/statuses, and releases (no write operations)
ci-opslistWorkflows, listWorkflowRuns, getWorkflowRun, listWorkflowJobs, getWorkflowJobLogs, listCheckRuns, getCombinedStatus, getCiFailureContext, triggerWorkflow, cancelWorkflowRun, rerunWorkflowRun, getRepository, listBranches, listCommits, getCommit
security-auditRead-only exploration (getFileContent, getRepositoryTree, searchCode, listCommits, getCommit, getBlame, compareCommits), PR and CI visibility (including getWorkflowJobLogs), plus createIssue, addIssueComment, addLabels to report findings (no destructive writes)
release-managerlistReleases, getLatestRelease, getRelease, getReleaseContext, createRelease, updateRelease, deleteRelease, compareCommits, listCommits, getCommit, listWorkflowRuns, getWorkflowRun, listPullRequests, getPullRequest, getRepository, listBranches
discussion-moderatorlistDiscussions, getDiscussion, addDiscussionComment, getRepository, searchIssues, getIssueContext, addIssueComment
notification-inboxlistNotifications, markNotificationRead, getIssue, getPullRequest, getRepository (requires a Notifications PAT)
pr-authorgetRepository, listBranches, getFileContent, createBranch, deleteBranch, createOrUpdateFile, createPullRequest, updatePullRequest, getPullRequest, listPullRequestFiles, listPullRequestReviewThreads, replyToReviewComment, resolveReviewThread, compareCommits, getCommit
maintainerAll 84 tools

Start with the smallest preset that fits. Use maintainer or omit preset when you need the full catalog. Full breakdown: Tools Catalog.

For an agent that handles open-ended requests, createGithubAgent({ model, preset: 'auto' }) picks presets per call from the latest user message. It narrows the tools the model sees to at most two presets and uses the matching system prompt. When no preset clearly matches, it uses the most likely one, and read-only repo-explorer when nothing stands out, so the full catalog is never exposed. This needs ai 7.0.105 or later.

Cherry-Picking Tools

You can also import individual tool factories for full control:

import { listPullRequests, createIssue } from '@github-tools/sdk'

const token = process.env.GITHUB_TOKEN!

const tools = {
  listPullRequests: listPullRequests(token),
  createIssue: createIssue(token),
}

Each tool factory accepts a token string. Tools use named module-level step functions with "use step" internally, ensuring proper step registration and full Node.js access when running inside a Vercel Workflow sandbox. See Durable Agents.

Approval Control

Write operations (creating issues, merging PRs, pushing files, …) require user approval by default. This is designed for human-in-the-loop agent workflows.

// All writes need approval (default)
createGithubTools({ token })

// No approval needed
createGithubTools({ token, requireApproval: false })

// Skip approval for low-risk writes the user asked for (labels, comments, reactions, …)
createGithubTools({ token, requireApproval: 'auto' })

// Granular: only destructive actions need approval
createGithubTools({
  token,
  requireApproval: {
    mergePullRequest: true,
    createOrUpdateFile: true,
    closeIssue: true,
    createPullRequest: false,
    addPullRequestComment: false,
    createIssue: false,
    addIssueComment: false,
  },
})

Write tools: createBranch, deleteBranch, forkRepository, createRepository, createOrUpdateFile, createPullRequest, mergePullRequest, updatePullRequest, addPullRequestComment, updatePullRequestComment, deletePullRequestComment, createPullRequestReview, replyToReviewComment, resolveReviewThread, requestReviewers, createIssue, addIssueComment, updateIssueComment, deleteIssueComment, closeIssue, updateIssue, addLabels, removeLabel, createLabel, updateLabel, deleteLabel, addAssignees, removeAssignees, addIssueReaction, addCommentReaction, addDiscussionComment, markNotificationRead, createGist, updateGist, deleteGist, createGistComment, triggerWorkflow, cancelWorkflowRun, rerunWorkflowRun, createRelease, updateRelease, deleteRelease.

All other tools are read-only and never require approval.

With 'auto', a fast evaluation model checks each call to a low-risk write tool (AUTO_APPROVAL_TOOLS). It scores how costly the call would be if wrong, and whether the latest user message asked for it. The call runs without a prompt only when both checks pass. Other write tools keep requiring approval. Any tool can opt in per tool ({ updateIssue: 'auto' }). 'auto' needs ai 7.0.105 or later and is not available on the durable agent. Tune it with evaluation: { maxRisk, minIntent, model }. The default model is TypeSafe's Jev.

Tool overrides

The overrides option lets you customize any AI SDK tool() property on a per-tool basis, keyed by tool name.

import type { ToolOverrides } from "@github-tools/sdk";

Supported override properties:

PropertyTypeDescription
descriptionstringCustom tool description for the model
titlestringHuman-readable title
strictbooleanStrict mode for input generation
needsApprovalboolean | functionGate execution behind approval
providerOptionsProviderOptionsProvider-specific metadata
onInputStartfunctionCallback when argument streaming starts
onInputDeltafunctionCallback on each streaming delta
onInputAvailablefunctionCallback when full input is available
toModelOutputfunctionCustom 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. REST list tools now return objects, so they carry it too. The field is stripped before the model sees the output; hooks, channels, and UIs still receive it.

import type { GithubRateLimit } from '@github-tools/sdk'

result.rateLimit?.remaining

resource is core, search, or graphql. On HTTP 403/429 the thrown error message also includes remaining/reset.

Errors

Classifiable failures become structured evlog catalog errors with a stable code, a why (technical cause), and a fix (actionable remedy) — written so a model recovers instead of hallucinating. The two causes models get wrong most often are spelled out: GitHub answers 404 for private resources the token cannot see (NOT_FOUND), and an expired VERCEL_OIDC_TOKEN is caught before any request with the exact expiry time (OIDC_TOKEN_EXPIRED) instead of surfacing as an opaque Connect 403.

Codes: TOKEN_REQUIRED, OIDC_TOKEN_EXPIRED, CONNECT_NOT_AUTHORIZED, CONNECT_USER_NOT_CONNECTED, CONNECT_INSTALLATION_REQUIRED, SUBJECT_CONTEXT_REQUIRED, UNAUTHORIZED (401), FORBIDDEN (403), RATE_LIMITED (403/429), NOT_FOUND (404), VALIDATION_FAILED (422). Unmapped statuses pass through unchanged; the original Octokit error stays reachable as cause.

In the eve extension, a failing tool returns { error: { code, message, why, fix, link } } to the model. With generateText/streamText, the framework forwards error.message (self-sufficient by design); use evlog's parseError(error) when you need the full structure. The catalog is exported as githubToolsErrors. See the errors guide.

Commit Attribution

Control how commits are attributed when using createOrUpdateFile or mergePullRequest:

import { createGithubTools } from '@github-tools/sdk'

const tools = createGithubTools({
  token,
  coAuthors: [
    { name: 'my-bot[bot]', email: '12345+my-bot[bot]@users.noreply.github.com' }
  ]
})

This appends Co-authored-by trailers to commit messages, crediting additional contributors.

OptionTypeDescription
author{ name: string, email: string }The person who wrote the code. Falls back to the authenticated user.
committer{ name: string, email: string }The person who applied the commit. Falls back to the authenticated user.
coAuthors{ name: string, email: string }[]Additional contributors added as Co-authored-by trailers.

Commits made via the GitHub API are automatically signed by GitHub's web-flow key, passing branch protection rules that require signed commits.

Tool Selection with toolpick

With dozens of tools, context window usage adds up. toolpick selects only the most relevant tools per step so the model sees what it needs:

import { createGithubTools } from '@github-tools/sdk'
import { createToolIndex } from 'toolpick'
import { generateText } from 'ai'
import { openai } from '@ai-sdk/openai'

const tools = createGithubTools()
const index = createToolIndex(tools, {
  embeddingModel: openai.embeddingModel('text-embedding-3-small'),
})

const result = await generateText({
  model: 'anthropic/claude-opus-5.5',
  tools,
  prepareStep: index.prepareStep(),
  prompt: 'List open PRs on vercel/ai and summarize them.',
})

Each step, toolpick picks the best ~5 tools using keyword + semantic search. All tools remain callable, only the visible set changes. See toolpick docs for LLM re-ranking, caching, and model-driven discovery options.

Durable Agents (Vercel Workflow SDK)

All tools include "use step" directives with named, module-level step functions, making them natively compatible with the Vercel Workflow SDK. Each tool execution runs as a properly registered durable step with full Node.js access in the workflow sandbox.

Use WorkflowAgent via the @github-tools/sdk/workflow subpath to make every LLM call and tool execution a retryable, crash-safe step:

import { createDurableGithubAgent } from '@github-tools/sdk/workflow'

const agent = createDurableGithubAgent({
  model: 'anthropic/claude-opus-5.5',
  token: process.env.GITHUB_TOKEN!,
  preset: 'maintainer',
})

All presets work with createDurableGithubAgent. Write tools honor requireApproval via needsApproval: the workflow pauses until the user approves or denies.

workflow and @ai-sdk/workflow are optional peer dependencies, install them only when using the workflow subpath.

Vercel Connect

Vercel Connect mints short-lived GitHub tokens from a connector, with no PAT to store. The @github-tools/sdk/connect subpath derives scopes from your preset automatically.

pnpm add @vercel/connect
import { connectGithubTools } from '@github-tools/sdk/connect'

const tools = connectGithubTools('github/my-connector', {
  preset: 'code-review',
})

For eve agents, pass connector directly to the eve extension (recommended): no separate Connect import, and no build.externalDependencies workaround needed:

// agent/extensions/github.ts
import githubExtension from '@github-tools/eve-extension'

export default githubExtension({
  connector: 'github/my-connector',
  preset: 'maintainer',
})

For the deprecated direct import, use connectGithubTools from @github-tools/sdk/connect/eve the same way inside agent/tools/github.ts. That path does need build: { externalDependencies: ['@vercel/connect'] } in agent.ts (see eve, direct import below).

connectGithubTools mints tokens lazily at tool execution. Do not await getToken(...) at module top level in agent/tools/ (that runs at import/build time).

Token provider only (custom factories):

import { connectGithubToken } from '@github-tools/sdk/connect'

createGithubTools({
  preset: 'ci-ops',
  token: connectGithubToken('github/my-connector', { preset: 'ci-ops' }),
})

Pass the same preset to connectGithubToken: it derives Connect scopes independently of the preset given to createGithubTools.

Override installation, repositories, or scopes via connect:

connectGithubTools('github/my-connector', {
  preset: 'issue-triage',
  connect: {
    installationId: 'inst_abc',
    repositories: ['my-org/my-repo'],
    scopes: ['issues:write'],
  },
})

A GitHub App installed on several accounts needs no configuration: each app token is minted for the installation that owns the tool call's target owner/repo. Set installationId, authorizationDetails or repositories in connect to pin one installation. connect also accepts any (call) => params resolver for other per-call rules.

@vercel/connect is an optional peer dependency, install it only when using the /connect subpath.

connector accepts a () => string | Promise<string> resolver instead of a static name, re-resolved on every call. Useful to pick a connector per environment or tenant:

connectGithubTools(
  () => (process.env.VERCEL_ENV === 'production' ? 'github/prod-connector' : 'github/preview-connector'),
  { preset: 'code-review' },
)

TanStack AI

Import native tool arrays and the prebuilt agent from @github-tools/sdk/tanstack:

pnpm add @github-tools/sdk @tanstack/[email protected] @tanstack/[email protected] ai zod
import { createGithubAgent } from '@github-tools/sdk/tanstack'
import { vercelGatewayText } from '@tanstack/ai-vercel-gateway'

const agent = createGithubAgent({
  adapter: vercelGatewayText('anthropic/claude-opus-5'),
  context: { owner: 'vercel', repo: 'ai' },
  preset: 'auto',
  requireApproval: 'auto',
})

const text = await agent.generate({ prompt: 'Summarize the open pull requests.' })

Set GITHUB_TOKEN and Gateway authentication on the server. generate() returns a string and throws on approval interrupts; stream() returns TanStack's native event stream. Auto modes use TanStack decide() with Gateway Jev and the same approval and routing thresholds as the AI SDK integration. Supply evaluation.adapter to use another evaluator. Auto-preset continuations skip routing and expose repo-explorer plus pending GitHub tools.

createGithubTools() returns an array for TanStack chat({ tools }). Direct tools with auto approval also need createGithubApprovalMiddleware({ tools }) and the shared githubToolApproval interrupt from @github-tools/sdk/tanstack/interrupts. Register that definition on the client to handle human review. See the TanStack AI guide for tools, streaming, and approval continuation.

eve

eve is Vercel's filesystem-first agent framework. @github-tools/eve-extension is the recommended way to add GitHub tools to an eve agent: a mountable eve extension, no CLI setup, no direct SDK import in agent/tools/. The legacy createGithubTools / per-tool factories on @github-tools/sdk/eve are deprecated for that registration pattern; they keep working and are documented below for existing agents. Shared runtime helpers for the extension live on @github-tools/sdk/eve-runtime (not deprecated).

pnpm add @github-tools/eve-extension eve

eve is a required peer dependency (itself requiring ai v7).

// agent/extensions/github.ts
import githubExtension from '@github-tools/eve-extension'

export default githubExtension({
  preset: ['code-review', 'issue-triage'],
  requireApproval: {
    mergePullRequest: true,
    createIssue: 'once',
    addPullRequestComment: false,
    createOrUpdateFile: ({ 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.

For Vercel Connect, pass connector directly, no separate import needed:

// agent/extensions/github.ts
import githubExtension from '@github-tools/eve-extension'

export default githubExtension({
  connector: 'github/my-connector',
  preset: 'maintainer',
})

No build.externalDependencies workaround is needed here. Unlike the deprecated direct import below, 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.

See packages/github-tools-eve-extension and examples/eve for the full package README and a runnable agent.

eve, direct import (deprecated)

The @github-tools/sdk/eve subpath registers all GitHub tools via defineDynamic: one file, zero CLI. This keeps working but new agents should use the extension above.

pnpm add @github-tools/sdk eve ai zod

eve v0.19+ requires ai v7 as a peer dependency.

// agent/tools/github.ts
import { createGithubTools } from '@github-tools/sdk/eve'

export default createGithubTools({
  preset: ['code-review', 'issue-triage'],
  requireApproval: {
    mergePullRequest: true,
    createIssue: 'once',
    addPullRequestComment: false,
    createOrUpdateFile: ({ toolInput }) => toolInput?.owner !== 'vercel-labs',
  },
})

Dynamic tools are named by their bare map key: the model sees listPullRequests, createIssue, and so on (same names as the AI SDK package). There is no automatic file-slug prefix when returning a tool map from defineDynamic.

Approval (eve)

ValueMaps toBehavior
true / 'always'always()Require approval on every call
false / 'never'omit approvalSkip approval (eve default)
'once'once()Approve once per session, then auto-allow
predicatecustom ApprovalInput-dependent gate; booleans map to user-approval / not-applicable
always() / once() / never()passthroughUse eve helpers directly

Default (no requireApproval): all write tools → always(). Unlisted write tools keep the always() fail-safe default.

Unlike the Workflow SDK subpath, eve approval works durably: gated tools pause the session until a human approves.

Cherry-picking (one tool per file)

// agent/tools/list_pull_requests.ts
import { listPullRequests } from '@github-tools/sdk/eve'

export default listPullRequests()

Idempotency

eve replays completed steps but re-runs steps interrupted mid-execution. Write tools vary:

ToolIdempotency
createOrUpdateFileNatural when content + sha unchanged (skips no-op updates)
closeIssueNatural when already closed
createBranchNatural when branch exists at same SHA
removeAssigneesNatural: removing an assignee that isn't assigned is a no-op on GitHub
addIssueReaction, addCommentReactionNatural: GitHub returns the existing reaction when the user already reacted with the same content
markNotificationReadNatural when the thread is already read
updateIssue, updatePullRequest, updateRelease, updateIssueComment, updatePullRequestCommentNot idempotent: each call applies a new revision
deleteIssueComment, deletePullRequestComment, deleteRelease, deleteBranchNot idempotent: deleting an already-deleted resource fails on GitHub
addIssueComment, addDiscussionComment, createIssue, mergePullRequest, createRelease, …Not idempotent: each call creates new side effects

Gate non-idempotent writes behind always() or once() where replay safety matters.

Vercel Connect

Mint the token from a Connect connector instead of GITHUB_TOKEN. connectGithubTools derives scopes from preset and fetches the token lazily inside each tool call:

// agent/tools/github.ts
import { connectGithubTools } from '@github-tools/sdk/connect/eve'

export default connectGithubTools('github/my-connector', {
  preset: 'maintainer',
})

Add build: { externalDependencies: ['@vercel/connect'] } to agent.ts. See Vercel Connect above for the full setup checklist.

eve is an optional peer dependency, install it only when using the /eve subpath.

New agents should use the eve extension above; see examples/eve.

Available Tools

List tools return { items, hasMore, page, perPage, nextPage? } (or add those fields next to checkRuns / runs / …). When hasMore, call with nextPage or set maxPages to combine sequential pages in one call — do not repeat the same page. Filter listCommits with path / author / since / until. getRepositoryTree accepts a path prefix.

Repository

ToolDescription
getRepositoryGet repository metadata (stars, language, default branch, …)
listBranchesList branches (hasMore / nextPage when there are more)
getFileContentRead a file or directory listing (prefer startLine/endLine or maxLines for large files)
getRepositoryTreeList the file and directory structure at a given ref (prefer a path prefix over recursive: true)
createBranchCreate a new branch from an existing branch or commit SHA
deleteBranchPermanently delete a branch
forkRepositoryFork a repository to a user or organization
createRepositoryCreate a new repository for a user or organization
createOrUpdateFileCreate or update a file and commit it

Pull Requests

ToolDescription
listPullRequestsList PRs filtered by state
getPullRequestGet a PR's full details (diff stats, body, merge status; body truncated by default)
listPullRequestFilesList files changed in a PR (patches omitted by default; set includePatch / filenames for diffs)
listPullRequestReviewsList reviews on a PR (approvals, change requests, comments)
listPullRequestReviewThreadsList review threads with comments, resolution state, and reply/resolve IDs (unresolved only by default)
getPullRequestContextFetch PR details plus files, reviews, and optional CI checks in one call
createPullRequestOpen a new PR
mergePullRequestMerge a PR (merge, squash, or rebase)
updatePullRequestUpdate a PR's title, body, state, base branch, or draft status
addPullRequestCommentPost a comment on a PR
updatePullRequestCommentEdit the body of a PR comment
deletePullRequestCommentPermanently delete a PR comment
createPullRequestReviewSubmit a formal review (approve, request changes, or comment) with inline comments
replyToReviewCommentReply to a review comment in its thread
resolveReviewThreadMark a review thread as resolved
requestReviewersRequest reviews from users or teams on a PR

Issues

ToolDescription
listIssuesList issues filtered by state and labels
getIssueGet an issue's details (body truncated by default; set detail: full for complete text)
getIssueContextFetch an issue plus label names and recent comments in one call
listIssueCommentsList comments on an issue (paginated; prefer getIssueContext for the first page)
createIssueOpen a new issue
addIssueCommentPost a comment on an issue
updateIssueCommentEdit the body of an issue comment
deleteIssueCommentPermanently delete an issue comment
closeIssueClose an issue (completed or not planned)
updateIssueUpdate an issue's title, body, labels, milestone, or assignees — set state: 'open' to reopen
listLabelsList labels available in a repository
addLabelsAdd labels to an issue or pull request
removeLabelRemove a label from an issue or pull request
createLabelCreate a label in a repository
updateLabelUpdate a label's name, color, or description
deleteLabelDelete a label from a repository permanently
addAssigneesAssign users to an issue or pull request
removeAssigneesRemove assignees from an issue or pull request

Reactions

Pull request conversations share the issue numbering, so the issue-level tools work on PRs too.

ToolDescription
listIssueReactionsList reactions on an issue or pull request, with per-emoji counts
addIssueReactionReact to an issue or pull request (+1, -1, laugh, confused, heart, hooray, rocket, eyes)
listCommentReactionsList reactions on an issue or pull request comment
addCommentReactionReact to an issue or pull request comment

Discussions

ToolDescription
listDiscussionsList discussions, most recently updated first, optionally filtered by category name (cursor-paginated)
getDiscussionGet a discussion by number (body truncated by default; set detail: full for complete text)
addDiscussionCommentPost a comment on a discussion

Notifications

ToolDescription
listNotificationsList notification threads for the authenticated user (unread only unless all: true)
markNotificationReadMark a single notification thread as read

Gists

ToolDescription
listGistsList gists for the authenticated user or a specific user
getGistGet a gist including file contents
listGistCommentsList comments on a gist
createGistCreate a new gist with one or more files
updateGistUpdate a gist's description or files
deleteGistDelete a gist permanently
createGistCommentPost a comment on a gist

Workflows

ToolDescription
listWorkflowsList GitHub Actions workflows in a repository
listWorkflowRunsList workflow runs filtered by workflow, branch, status, or event
getWorkflowRunGet a workflow run's status, timing, and trigger info
listWorkflowJobsList jobs in a workflow run with step-level status
getWorkflowJobLogsRead a job's log output (last 200 lines by default, timestamps stripped)
triggerWorkflowTrigger a workflow via workflow_dispatch event
cancelWorkflowRunCancel an in-progress workflow run
rerunWorkflowRunRe-run a workflow run, optionally only failed jobs

Checks and Statuses

ToolDescription
listCheckRunsList check runs (Checks API: GitHub Actions and other CI providers) for a commit, branch, or tag
getCombinedStatusGet the combined commit status (Statuses API: legacy CI integrations) for a commit, branch, or tag
getCiFailureContextDiagnose CI failures for a ref — combined status, failing checks, and failed workflow jobs in one call

Releases

ToolDescription
listReleasesList releases, newest first (includes drafts and prereleases)
getLatestReleaseGet the latest published release (body truncated by default; set detail: full for complete notes)
getReleaseGet a specific release by ID, including its assets
getReleaseContextFetch a release plus the previous release and tag comparison in one call
createReleaseCreate a new release (and its tag if needed)
updateReleaseUpdate a release's tag, target, title, notes, draft, or prerelease status
deleteReleasePermanently delete a release (does not delete the underlying git tag)

Commits

ToolDescription
listCommitsList commits, optionally filtered by file path, author, or date range. When hasMore, pass nextPage
getCommitGet a commit's full details including changed files and diffs
getBlameLine-level git blame for a file (GitHub GraphQL)
compareCommitsCompare two branches, tags, or commits: ahead/behind counts, commits in between, and files that differ
ToolDescription
searchCodeSearch code across GitHub with qualifier support (includes matching text snippets when available)
searchRepositoriesSearch repositories by keyword, topic, language, stars, …
searchIssuesSearch issues and pull requests using qualifiers like is:open, type:pr, or label:bug

GitHub Token

All tools authenticate with a GitHub personal access token (PAT).

Create one at GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens.

PermissionLevelRequired for
MetadataRead-onlyAlways required (auto-included)
ContentsRead-onlygetRepository, listBranches, getFileContent, getRepositoryTree, listCommits, getCommit, getBlame, compareCommits, listReleases, getLatestRelease, getRelease, getReleaseContext
ContentsRead and writecreateBranch, deleteBranch, createOrUpdateFile, createRelease, updateRelease, deleteRelease
AdministrationRead and writeforkRepository, createRepository
Pull requestsRead-onlylistPullRequests, getPullRequest, listPullRequestFiles, listPullRequestReviews, listPullRequestReviewThreads, getPullRequestContext
Pull requestsRead and writecreatePullRequest, mergePullRequest, updatePullRequest, addPullRequestComment, updatePullRequestComment, deletePullRequestComment, createPullRequestReview, replyToReviewComment, resolveReviewThread, requestReviewers
IssuesRead-onlylistIssues, getIssue, getIssueContext, listIssueComments, listLabels, listIssueReactions, listCommentReactions
IssuesRead and writecreateIssue, addIssueComment, updateIssueComment, deleteIssueComment, closeIssue, updateIssue, addLabels, removeLabel, createLabel, updateLabel, deleteLabel, addAssignees, removeAssignees, addIssueReaction, addCommentReaction
DiscussionsRead-onlylistDiscussions, getDiscussion
DiscussionsRead and writeaddDiscussionComment
GistsRead-onlylistGists, getGist, listGistComments
GistsRead and writecreateGist, updateGist, deleteGist, createGistComment
Notifications (account)Read and writelistNotifications, markNotificationRead
ActionsRead-onlylistWorkflows, listWorkflowRuns, getWorkflowRun, listWorkflowJobs, getWorkflowJobLogs, getCiFailureContext
ActionsRead and writetriggerWorkflow, cancelWorkflowRun, rerunWorkflowRun
ChecksRead-onlylistCheckRuns, getCiFailureContext
Commit statusesRead-onlygetCombinedStatus, getCiFailureContext

Search tools (searchCode, searchRepositories, searchIssues) work with any token.

Classic token

ScopeRequired for
public_repoAll tools on public repositories
repoAll tools on public and private repositories

API

createGithubTools(options)

Returns an object of tools, ready to spread into tools of any AI SDK call.

type GithubToolsOptions = {
  token?: GithubTokenInput // defaults to process.env.GITHUB_TOKEN
  requireApproval?: boolean | Partial<Record<GithubWriteToolName, boolean>>
  preset?: GithubToolPreset | GithubToolPreset[]
  context?: GithubToolsContext // default owner / repo / PR / issue / ref
}

type GithubTokenInput = string | (() => Promise<string>)

type GithubToolPreset = 'code-review' | 'issue-triage' | 'repo-explorer' | 'ci-ops' | 'security-audit' | 'release-manager' | 'discussion-moderator' | 'notification-inbox' | 'pr-author' | 'maintainer'

createGithubAgent(options)

Returns a ToolLoopAgent instance with .generate() and .stream() methods, pre-configured with GitHub tools and tailored instructions.

import { createGithubAgent } from '@github-tools/sdk'

// Prefer a preset: scoped tools + tailored prompt
const reviewer = createGithubAgent({
  model: 'anthropic/claude-opus-5.5',
  token: process.env.GITHUB_TOKEN!,
  preset: 'code-review',
  context: { owner: 'vercel', repo: 'ai', pullNumber: 42 },
})

// Add context to the built-in prompt
const triager = createGithubAgent({
  model: 'anthropic/claude-opus-5.5',
  token: process.env.GITHUB_TOKEN!,
  preset: 'issue-triage',
  additionalInstructions: 'Focus on the nuxt/ui repository. Always respond in French.',
})

// Full catalog (omit preset or use maintainer)
const agent = createGithubAgent({
  model: 'anthropic/claude-opus-5.5',
  token: process.env.GITHUB_TOKEN!,
  preset: 'maintainer',
})

// Full override: replace the built-in prompt entirely
const custom = createGithubAgent({
  model: 'anthropic/claude-opus-5.5',
  token: process.env.GITHUB_TOKEN!,
  instructions: 'You are a security auditor. Only flag security-related issues.',
})

// Use the agent
const result = await reviewer.generate({ prompt: 'Review this PR' })
const stream = reviewer.stream({ prompt: 'Review this PR' })
OptionDescription
modelLanguage model: string ('anthropic/claude-opus-5.5') or provider instance
tokenGitHub token string or async provider
presetOptional preset or array of presets to scope tools
contextDefault owner / repo / pullNumber / issueNumber / ref for tools and the system prompt
requireApprovalApproval config (same as createGithubTools)
instructionsReplaces the built-in system prompt entirely (context is still appended)
additionalInstructionsAppended to the built-in system prompt

All other ToolLoopAgent options (stopWhen, toolChoice, onStepFinish, etc.) are passed through.

createDurableGithubAgent(options)

Returns a DurableGithubAgent instance for use inside Vercel Workflow SDK functions. Every LLM call and tool execution runs as a durable step with automatic retries and crash recovery.

Supports both .stream() (real-time output to a writable) and .generate() (non-streaming, returns the full text response).

Requires the optional peer dependencies workflow and @ai-sdk/workflow:

pnpm add workflow @ai-sdk/workflow

Streaming (chat UI)

// durable-chat.workflow.ts
import { createDurableGithubAgent } from '@github-tools/sdk/workflow'
import { getWritable } from 'workflow'
import type { ModelMessage, UIMessageChunk } from 'ai'

async function chatWorkflow(messages: ModelMessage[], token: string) {
  "use workflow"
  const agent = createDurableGithubAgent({
    model: 'anthropic/claude-opus-5.5',
    token,
    preset: 'code-review',
  })
  const writable = getWritable<UIMessageChunk>()
  await agent.stream({ messages, writable })
}

Non-streaming (bot / background job, needs "use step")

// agent-turn.step.ts
import { createGithubAgent } from '@github-tools/sdk'

async function agentTurn(prompt: string) {
  "use step"
  const agent = createGithubAgent({
    model: 'anthropic/claude-opus-5.5',
    preset: 'code-review',
    requireApproval: false,
  })
  const { text } = await agent.generate({ prompt })
  return text
}

See examples/pr-review-agent for a complete PR review agent built with Chat SDK and Vercel Workflow.

All presets (code-review, issue-triage, ci-ops, repo-explorer, security-audit, release-manager, discussion-moderator, notification-inbox, pr-author, maintainer) work with createDurableGithubAgent. Options mirror createGithubAgent with additional pass-through for WorkflowAgentOptions fields like experimental_telemetry, onStepEnd, onEnd, and prepareStep. Write tools honor requireApproval via needsApproval.

resolveGithubToken(token?)

Resolves a GithubTokenInput (token string, async provider, or process.env.GITHUB_TOKEN) to a token string. Throws when no token is available.

createOctokit(token)

Returns a configured octokit instance. Useful for building custom tools.

License

MIT

Made by @HugoRCD

agent
agents
ai
ai-sdk
github
presets
tools

vercel-labs/github-tools

GitHub tools and agents for the Vercel AI SDK, wrap GitHub's API as AI SDK tools ready to plug into any agent

TypeScript

132

182 commits

updated Oct 8, 2026

See the code

README

GitHub tools banner

@github-tools/sdk

npm version npm downloads TypeScript license

Give any agent GitHub access. A typed tool layer for GitHub AI agents, with presets, human approval, and durable execution. Works with eve, the AI SDK, TanStack AI, Vercel Workflow, and Chat SDK.

Docs: github-tools.com

Why not the GitHub MCP server, gh CLI, or raw Octokit?

They all reach the GitHub API, but none of them were built as an agent's tool layer. An agent still needs a schema it can fill reliably, a safety gate before it merges a PR, output shaped to fit a context window, and a way to survive a crash mid-task.

@github-tools/sdkGitHub MCP servergh CLIRaw Octokit
IntegrationNative AI SDK and TanStack AI toolsMCP wire protocol via a separate processShell-out from the agentHand-written per call
Human approvalBuilt in, on by defaultHost-dependent, inconsistentNoneYou build it
Durable / retryableWorkflow entry point uses "use step"NoNoNo
Scoped by taskPresets (10 built-in)Full server surface, or manual filteringFull CLI surfaceYou build it
Token-efficient outputShaped and truncated by designRaw API responsesRaw text, needs parsingRaw API responses
Native eve / Workflow / Chat SDKYesNoNoNo

Pick your path

What are you buildingStart here
A standalone GitHub agent, fast: 3 files, durable approvaleve extension
Scripts, chat backends, or an existing AI SDK appQuick Start below
A TanStack AI appTanStack AI
Production agents that must survive restarts and timeoutsDurable Agents
A GitHub, Slack, or Discord botChat SDK docs

84 tools cover repositories, branches, pull requests, issues, reactions, discussions, notifications, commits, releases, checks and statuses, search, gists, and workflows. See the full Tools Catalog. Write operations support granular approval control out of the box.

Installation

pnpm add @github-tools/sdk

ai and zod are peer dependencies (ai v6 or v7; the eve subpath requires v7):

pnpm add ai zod

Quick Start

// list-prs.ts
import { createGithubTools } from '@github-tools/sdk'
import { generateText } from 'ai'

const result = await generateText({
  model: yourModel,
  tools: createGithubTools({ token: process.env.GITHUB_TOKEN! }),
  prompt: 'List the open pull requests on vercel/ai and summarize them.',
})

Presets

Use preset to get only the tools relevant to a specific use case:

// Code-review agent: PRs, commits, file content, and comments
createGithubTools({ token, preset: 'code-review' })

// Issue triage: read/create/close issues, search
createGithubTools({ token, preset: 'issue-triage' })

// Read-only exploration: browse repos without write access
createGithubTools({ token, preset: 'repo-explorer' })

// Security audit: read-only exploration, PR/CI visibility, plus issue creation to report findings
createGithubTools({ token, preset: 'security-audit' })

// Release manager: releases, compare diff, commits, workflow runs, pull requests
createGithubTools({ token, preset: 'release-manager' })

// Discussion moderator: Discussions plus light issue context
createGithubTools({ token, preset: 'discussion-moderator' })

// Notification inbox: triage user notifications (needs a Notifications PAT)
createGithubTools({ token, preset: 'notification-inbox' })

// PR author: branches, file edits, and opening PRs
createGithubTools({ token, preset: 'pr-author' })

// Full catalog: all tools (same as omitting preset)
createGithubTools({ token, preset: 'maintainer' })

Presets are composable, pass an array to combine them:

createGithubTools({ token, preset: ['code-review', 'issue-triage'] })
PresetTools included
code-reviewgetPullRequest, listPullRequests, listPullRequestFiles, listPullRequestReviews, listPullRequestReviewThreads, getPullRequestContext, getFileContent, listCommits, getCommit, getBlame, compareCommits, getRepository, listBranches, searchCode, listCheckRuns, getCombinedStatus, updatePullRequest, addPullRequestComment, updatePullRequestComment, deletePullRequestComment, createPullRequestReview, replyToReviewComment, resolveReviewThread, requestReviewers
issue-triagelistIssues, getIssueContext, listIssueComments, createIssue, addIssueComment, updateIssueComment, deleteIssueComment, closeIssue, updateIssue, addLabels, removeLabel, createLabel, updateLabel, deleteLabel, addAssignees, removeAssignees, listIssueReactions, addIssueReaction, listCommentReactions, addCommentReaction, getRepository, searchRepositories, searchCode, searchIssues
repo-explorerAll read-only tools including discussions, gists, workflows, checks/statuses, and releases (no write operations)
ci-opslistWorkflows, listWorkflowRuns, getWorkflowRun, listWorkflowJobs, getWorkflowJobLogs, listCheckRuns, getCombinedStatus, getCiFailureContext, triggerWorkflow, cancelWorkflowRun, rerunWorkflowRun, getRepository, listBranches, listCommits, getCommit
security-auditRead-only exploration (getFileContent, getRepositoryTree, searchCode, listCommits, getCommit, getBlame, compareCommits), PR and CI visibility (including getWorkflowJobLogs), plus createIssue, addIssueComment, addLabels to report findings (no destructive writes)
release-managerlistReleases, getLatestRelease, getRelease, getReleaseContext, createRelease, updateRelease, deleteRelease, compareCommits, listCommits, getCommit, listWorkflowRuns, getWorkflowRun, listPullRequests, getPullRequest, getRepository, listBranches
discussion-moderatorlistDiscussions, getDiscussion, addDiscussionComment, getRepository, searchIssues, getIssueContext, addIssueComment
notification-inboxlistNotifications, markNotificationRead, getIssue, getPullRequest, getRepository (requires a Notifications PAT)
pr-authorgetRepository, listBranches, getFileContent, createBranch, deleteBranch, createOrUpdateFile, createPullRequest, updatePullRequest, getPullRequest, listPullRequestFiles, listPullRequestReviewThreads, replyToReviewComment, resolveReviewThread, compareCommits, getCommit
maintainerAll 84 tools

Start with the smallest preset that fits. Use maintainer or omit preset when you need the full catalog. Full breakdown: Tools Catalog.

For an agent that handles open-ended requests, createGithubAgent({ model, preset: 'auto' }) picks presets per call from the latest user message. It narrows the tools the model sees to at most two presets and uses the matching system prompt. When no preset clearly matches, it uses the most likely one, and read-only repo-explorer when nothing stands out, so the full catalog is never exposed. This needs ai 7.0.105 or later.

Cherry-Picking Tools

You can also import individual tool factories for full control:

import { listPullRequests, createIssue } from '@github-tools/sdk'

const token = process.env.GITHUB_TOKEN!

const tools = {
  listPullRequests: listPullRequests(token),
  createIssue: createIssue(token),
}

Each tool factory accepts a token string. Tools use named module-level step functions with "use step" internally, ensuring proper step registration and full Node.js access when running inside a Vercel Workflow sandbox. See Durable Agents.

Approval Control

Write operations (creating issues, merging PRs, pushing files, …) require user approval by default. This is designed for human-in-the-loop agent workflows.

// All writes need approval (default)
createGithubTools({ token })

// No approval needed
createGithubTools({ token, requireApproval: false })

// Skip approval for low-risk writes the user asked for (labels, comments, reactions, …)
createGithubTools({ token, requireApproval: 'auto' })

// Granular: only destructive actions need approval
createGithubTools({
  token,
  requireApproval: {
    mergePullRequest: true,
    createOrUpdateFile: true,
    closeIssue: true,
    createPullRequest: false,
    addPullRequestComment: false,
    createIssue: false,
    addIssueComment: false,
  },
})

Write tools: createBranch, deleteBranch, forkRepository, createRepository, createOrUpdateFile, createPullRequest, mergePullRequest, updatePullRequest, addPullRequestComment, updatePullRequestComment, deletePullRequestComment, createPullRequestReview, replyToReviewComment, resolveReviewThread, requestReviewers, createIssue, addIssueComment, updateIssueComment, deleteIssueComment, closeIssue, updateIssue, addLabels, removeLabel, createLabel, updateLabel, deleteLabel, addAssignees, removeAssignees, addIssueReaction, addCommentReaction, addDiscussionComment, markNotificationRead, createGist, updateGist, deleteGist, createGistComment, triggerWorkflow, cancelWorkflowRun, rerunWorkflowRun, createRelease, updateRelease, deleteRelease.

All other tools are read-only and never require approval.

With 'auto', a fast evaluation model checks each call to a low-risk write tool (AUTO_APPROVAL_TOOLS). It scores how costly the call would be if wrong, and whether the latest user message asked for it. The call runs without a prompt only when both checks pass. Other write tools keep requiring approval. Any tool can opt in per tool ({ updateIssue: 'auto' }). 'auto' needs ai 7.0.105 or later and is not available on the durable agent. Tune it with evaluation: { maxRisk, minIntent, model }. The default model is TypeSafe's Jev.

Tool overrides

The overrides option lets you customize any AI SDK tool() property on a per-tool basis, keyed by tool name.

import type { ToolOverrides } from "@github-tools/sdk";

Supported override properties:

PropertyTypeDescription
descriptionstringCustom tool description for the model
titlestringHuman-readable title
strictbooleanStrict mode for input generation
needsApprovalboolean | functionGate execution behind approval
providerOptionsProviderOptionsProvider-specific metadata
onInputStartfunctionCallback when argument streaming starts
onInputDeltafunctionCallback on each streaming delta
onInputAvailablefunctionCallback when full input is available
toModelOutputfunctionCustom 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. REST list tools now return objects, so they carry it too. The field is stripped before the model sees the output; hooks, channels, and UIs still receive it.

import type { GithubRateLimit } from '@github-tools/sdk'

result.rateLimit?.remaining

resource is core, search, or graphql. On HTTP 403/429 the thrown error message also includes remaining/reset.

Errors

Classifiable failures become structured evlog catalog errors with a stable code, a why (technical cause), and a fix (actionable remedy) — written so a model recovers instead of hallucinating. The two causes models get wrong most often are spelled out: GitHub answers 404 for private resources the token cannot see (NOT_FOUND), and an expired VERCEL_OIDC_TOKEN is caught before any request with the exact expiry time (OIDC_TOKEN_EXPIRED) instead of surfacing as an opaque Connect 403.

Codes: TOKEN_REQUIRED, OIDC_TOKEN_EXPIRED, CONNECT_NOT_AUTHORIZED, CONNECT_USER_NOT_CONNECTED, CONNECT_INSTALLATION_REQUIRED, SUBJECT_CONTEXT_REQUIRED, UNAUTHORIZED (401), FORBIDDEN (403), RATE_LIMITED (403/429), NOT_FOUND (404), VALIDATION_FAILED (422). Unmapped statuses pass through unchanged; the original Octokit error stays reachable as cause.

In the eve extension, a failing tool returns { error: { code, message, why, fix, link } } to the model. With generateText/streamText, the framework forwards error.message (self-sufficient by design); use evlog's parseError(error) when you need the full structure. The catalog is exported as githubToolsErrors. See the errors guide.

Commit Attribution

Control how commits are attributed when using createOrUpdateFile or mergePullRequest:

import { createGithubTools } from '@github-tools/sdk'

const tools = createGithubTools({
  token,
  coAuthors: [
    { name: 'my-bot[bot]', email: '12345+my-bot[bot]@users.noreply.github.com' }
  ]
})

This appends Co-authored-by trailers to commit messages, crediting additional contributors.

OptionTypeDescription
author{ name: string, email: string }The person who wrote the code. Falls back to the authenticated user.
committer{ name: string, email: string }The person who applied the commit. Falls back to the authenticated user.
coAuthors{ name: string, email: string }[]Additional contributors added as Co-authored-by trailers.

Commits made via the GitHub API are automatically signed by GitHub's web-flow key, passing branch protection rules that require signed commits.

Tool Selection with toolpick

With dozens of tools, context window usage adds up. toolpick selects only the most relevant tools per step so the model sees what it needs:

import { createGithubTools } from '@github-tools/sdk'
import { createToolIndex } from 'toolpick'
import { generateText } from 'ai'
import { openai } from '@ai-sdk/openai'

const tools = createGithubTools()
const index = createToolIndex(tools, {
  embeddingModel: openai.embeddingModel('text-embedding-3-small'),
})

const result = await generateText({
  model: 'anthropic/claude-opus-5.5',
  tools,
  prepareStep: index.prepareStep(),
  prompt: 'List open PRs on vercel/ai and summarize them.',
})

Each step, toolpick picks the best ~5 tools using keyword + semantic search. All tools remain callable, only the visible set changes. See toolpick docs for LLM re-ranking, caching, and model-driven discovery options.

Durable Agents (Vercel Workflow SDK)

All tools include "use step" directives with named, module-level step functions, making them natively compatible with the Vercel Workflow SDK. Each tool execution runs as a properly registered durable step with full Node.js access in the workflow sandbox.

Use WorkflowAgent via the @github-tools/sdk/workflow subpath to make every LLM call and tool execution a retryable, crash-safe step:

import { createDurableGithubAgent } from '@github-tools/sdk/workflow'

const agent = createDurableGithubAgent({
  model: 'anthropic/claude-opus-5.5',
  token: process.env.GITHUB_TOKEN!,
  preset: 'maintainer',
})

All presets work with createDurableGithubAgent. Write tools honor requireApproval via needsApproval: the workflow pauses until the user approves or denies.

workflow and @ai-sdk/workflow are optional peer dependencies, install them only when using the workflow subpath.

Vercel Connect

Vercel Connect mints short-lived GitHub tokens from a connector, with no PAT to store. The @github-tools/sdk/connect subpath derives scopes from your preset automatically.

pnpm add @vercel/connect
import { connectGithubTools } from '@github-tools/sdk/connect'

const tools = connectGithubTools('github/my-connector', {
  preset: 'code-review',
})

For eve agents, pass connector directly to the eve extension (recommended): no separate Connect import, and no build.externalDependencies workaround needed:

// agent/extensions/github.ts
import githubExtension from '@github-tools/eve-extension'

export default githubExtension({
  connector: 'github/my-connector',
  preset: 'maintainer',
})

For the deprecated direct import, use connectGithubTools from @github-tools/sdk/connect/eve the same way inside agent/tools/github.ts. That path does need build: { externalDependencies: ['@vercel/connect'] } in agent.ts (see eve, direct import below).

connectGithubTools mints tokens lazily at tool execution. Do not await getToken(...) at module top level in agent/tools/ (that runs at import/build time).

Token provider only (custom factories):

import { connectGithubToken } from '@github-tools/sdk/connect'

createGithubTools({
  preset: 'ci-ops',
  token: connectGithubToken('github/my-connector', { preset: 'ci-ops' }),
})

Pass the same preset to connectGithubToken: it derives Connect scopes independently of the preset given to createGithubTools.

Override installation, repositories, or scopes via connect:

connectGithubTools('github/my-connector', {
  preset: 'issue-triage',
  connect: {
    installationId: 'inst_abc',
    repositories: ['my-org/my-repo'],
    scopes: ['issues:write'],
  },
})

A GitHub App installed on several accounts needs no configuration: each app token is minted for the installation that owns the tool call's target owner/repo. Set installationId, authorizationDetails or repositories in connect to pin one installation. connect also accepts any (call) => params resolver for other per-call rules.

@vercel/connect is an optional peer dependency, install it only when using the /connect subpath.

connector accepts a () => string | Promise<string> resolver instead of a static name, re-resolved on every call. Useful to pick a connector per environment or tenant:

connectGithubTools(
  () => (process.env.VERCEL_ENV === 'production' ? 'github/prod-connector' : 'github/preview-connector'),
  { preset: 'code-review' },
)

TanStack AI

Import native tool arrays and the prebuilt agent from @github-tools/sdk/tanstack:

pnpm add @github-tools/sdk @tanstack/[email protected] @tanstack/[email protected] ai zod
import { createGithubAgent } from '@github-tools/sdk/tanstack'
import { vercelGatewayText } from '@tanstack/ai-vercel-gateway'

const agent = createGithubAgent({
  adapter: vercelGatewayText('anthropic/claude-opus-5'),
  context: { owner: 'vercel', repo: 'ai' },
  preset: 'auto',
  requireApproval: 'auto',
})

const text = await agent.generate({ prompt: 'Summarize the open pull requests.' })

Set GITHUB_TOKEN and Gateway authentication on the server. generate() returns a string and throws on approval interrupts; stream() returns TanStack's native event stream. Auto modes use TanStack decide() with Gateway Jev and the same approval and routing thresholds as the AI SDK integration. Supply evaluation.adapter to use another evaluator. Auto-preset continuations skip routing and expose repo-explorer plus pending GitHub tools.

createGithubTools() returns an array for TanStack chat({ tools }). Direct tools with auto approval also need createGithubApprovalMiddleware({ tools }) and the shared githubToolApproval interrupt from @github-tools/sdk/tanstack/interrupts. Register that definition on the client to handle human review. See the TanStack AI guide for tools, streaming, and approval continuation.

eve

eve is Vercel's filesystem-first agent framework. @github-tools/eve-extension is the recommended way to add GitHub tools to an eve agent: a mountable eve extension, no CLI setup, no direct SDK import in agent/tools/. The legacy createGithubTools / per-tool factories on @github-tools/sdk/eve are deprecated for that registration pattern; they keep working and are documented below for existing agents. Shared runtime helpers for the extension live on @github-tools/sdk/eve-runtime (not deprecated).

pnpm add @github-tools/eve-extension eve

eve is a required peer dependency (itself requiring ai v7).

// agent/extensions/github.ts
import githubExtension from '@github-tools/eve-extension'

export default githubExtension({
  preset: ['code-review', 'issue-triage'],
  requireApproval: {
    mergePullRequest: true,
    createIssue: 'once',
    addPullRequestComment: false,
    createOrUpdateFile: ({ 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.

For Vercel Connect, pass connector directly, no separate import needed:

// agent/extensions/github.ts
import githubExtension from '@github-tools/eve-extension'

export default githubExtension({
  connector: 'github/my-connector',
  preset: 'maintainer',
})

No build.externalDependencies workaround is needed here. Unlike the deprecated direct import below, 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.

See packages/github-tools-eve-extension and examples/eve for the full package README and a runnable agent.

eve, direct import (deprecated)

The @github-tools/sdk/eve subpath registers all GitHub tools via defineDynamic: one file, zero CLI. This keeps working but new agents should use the extension above.

pnpm add @github-tools/sdk eve ai zod

eve v0.19+ requires ai v7 as a peer dependency.

// agent/tools/github.ts
import { createGithubTools } from '@github-tools/sdk/eve'

export default createGithubTools({
  preset: ['code-review', 'issue-triage'],
  requireApproval: {
    mergePullRequest: true,
    createIssue: 'once',
    addPullRequestComment: false,
    createOrUpdateFile: ({ toolInput }) => toolInput?.owner !== 'vercel-labs',
  },
})

Dynamic tools are named by their bare map key: the model sees listPullRequests, createIssue, and so on (same names as the AI SDK package). There is no automatic file-slug prefix when returning a tool map from defineDynamic.

Approval (eve)

ValueMaps toBehavior
true / 'always'always()Require approval on every call
false / 'never'omit approvalSkip approval (eve default)
'once'once()Approve once per session, then auto-allow
predicatecustom ApprovalInput-dependent gate; booleans map to user-approval / not-applicable
always() / once() / never()passthroughUse eve helpers directly

Default (no requireApproval): all write tools → always(). Unlisted write tools keep the always() fail-safe default.

Unlike the Workflow SDK subpath, eve approval works durably: gated tools pause the session until a human approves.

Cherry-picking (one tool per file)

// agent/tools/list_pull_requests.ts
import { listPullRequests } from '@github-tools/sdk/eve'

export default listPullRequests()

Idempotency

eve replays completed steps but re-runs steps interrupted mid-execution. Write tools vary:

ToolIdempotency
createOrUpdateFileNatural when content + sha unchanged (skips no-op updates)
closeIssueNatural when already closed
createBranchNatural when branch exists at same SHA
removeAssigneesNatural: removing an assignee that isn't assigned is a no-op on GitHub
addIssueReaction, addCommentReactionNatural: GitHub returns the existing reaction when the user already reacted with the same content
markNotificationReadNatural when the thread is already read
updateIssue, updatePullRequest, updateRelease, updateIssueComment, updatePullRequestCommentNot idempotent: each call applies a new revision
deleteIssueComment, deletePullRequestComment, deleteRelease, deleteBranchNot idempotent: deleting an already-deleted resource fails on GitHub
addIssueComment, addDiscussionComment, createIssue, mergePullRequest, createRelease, …Not idempotent: each call creates new side effects

Gate non-idempotent writes behind always() or once() where replay safety matters.

Vercel Connect

Mint the token from a Connect connector instead of GITHUB_TOKEN. connectGithubTools derives scopes from preset and fetches the token lazily inside each tool call:

// agent/tools/github.ts
import { connectGithubTools } from '@github-tools/sdk/connect/eve'

export default connectGithubTools('github/my-connector', {
  preset: 'maintainer',
})

Add build: { externalDependencies: ['@vercel/connect'] } to agent.ts. See Vercel Connect above for the full setup checklist.

eve is an optional peer dependency, install it only when using the /eve subpath.

New agents should use the eve extension above; see examples/eve.

Available Tools

List tools return { items, hasMore, page, perPage, nextPage? } (or add those fields next to checkRuns / runs / …). When hasMore, call with nextPage or set maxPages to combine sequential pages in one call — do not repeat the same page. Filter listCommits with path / author / since / until. getRepositoryTree accepts a path prefix.

Repository

ToolDescription
getRepositoryGet repository metadata (stars, language, default branch, …)
listBranchesList branches (hasMore / nextPage when there are more)
getFileContentRead a file or directory listing (prefer startLine/endLine or maxLines for large files)
getRepositoryTreeList the file and directory structure at a given ref (prefer a path prefix over recursive: true)
createBranchCreate a new branch from an existing branch or commit SHA
deleteBranchPermanently delete a branch
forkRepositoryFork a repository to a user or organization
createRepositoryCreate a new repository for a user or organization
createOrUpdateFileCreate or update a file and commit it

Pull Requests

ToolDescription
listPullRequestsList PRs filtered by state
getPullRequestGet a PR's full details (diff stats, body, merge status; body truncated by default)
listPullRequestFilesList files changed in a PR (patches omitted by default; set includePatch / filenames for diffs)
listPullRequestReviewsList reviews on a PR (approvals, change requests, comments)
listPullRequestReviewThreadsList review threads with comments, resolution state, and reply/resolve IDs (unresolved only by default)
getPullRequestContextFetch PR details plus files, reviews, and optional CI checks in one call
createPullRequestOpen a new PR
mergePullRequestMerge a PR (merge, squash, or rebase)
updatePullRequestUpdate a PR's title, body, state, base branch, or draft status
addPullRequestCommentPost a comment on a PR
updatePullRequestCommentEdit the body of a PR comment
deletePullRequestCommentPermanently delete a PR comment
createPullRequestReviewSubmit a formal review (approve, request changes, or comment) with inline comments
replyToReviewCommentReply to a review comment in its thread
resolveReviewThreadMark a review thread as resolved
requestReviewersRequest reviews from users or teams on a PR

Issues

ToolDescription
listIssuesList issues filtered by state and labels
getIssueGet an issue's details (body truncated by default; set detail: full for complete text)
getIssueContextFetch an issue plus label names and recent comments in one call
listIssueCommentsList comments on an issue (paginated; prefer getIssueContext for the first page)
createIssueOpen a new issue
addIssueCommentPost a comment on an issue
updateIssueCommentEdit the body of an issue comment
deleteIssueCommentPermanently delete an issue comment
closeIssueClose an issue (completed or not planned)
updateIssueUpdate an issue's title, body, labels, milestone, or assignees — set state: 'open' to reopen
listLabelsList labels available in a repository
addLabelsAdd labels to an issue or pull request
removeLabelRemove a label from an issue or pull request
createLabelCreate a label in a repository
updateLabelUpdate a label's name, color, or description
deleteLabelDelete a label from a repository permanently
addAssigneesAssign users to an issue or pull request
removeAssigneesRemove assignees from an issue or pull request

Reactions

Pull request conversations share the issue numbering, so the issue-level tools work on PRs too.

ToolDescription
listIssueReactionsList reactions on an issue or pull request, with per-emoji counts
addIssueReactionReact to an issue or pull request (+1, -1, laugh, confused, heart, hooray, rocket, eyes)
listCommentReactionsList reactions on an issue or pull request comment
addCommentReactionReact to an issue or pull request comment

Discussions

ToolDescription
listDiscussionsList discussions, most recently updated first, optionally filtered by category name (cursor-paginated)
getDiscussionGet a discussion by number (body truncated by default; set detail: full for complete text)
addDiscussionCommentPost a comment on a discussion

Notifications

ToolDescription
listNotificationsList notification threads for the authenticated user (unread only unless all: true)
markNotificationReadMark a single notification thread as read

Gists

ToolDescription
listGistsList gists for the authenticated user or a specific user
getGistGet a gist including file contents
listGistCommentsList comments on a gist
createGistCreate a new gist with one or more files
updateGistUpdate a gist's description or files
deleteGistDelete a gist permanently
createGistCommentPost a comment on a gist

Workflows

ToolDescription
listWorkflowsList GitHub Actions workflows in a repository
listWorkflowRunsList workflow runs filtered by workflow, branch, status, or event
getWorkflowRunGet a workflow run's status, timing, and trigger info
listWorkflowJobsList jobs in a workflow run with step-level status
getWorkflowJobLogsRead a job's log output (last 200 lines by default, timestamps stripped)
triggerWorkflowTrigger a workflow via workflow_dispatch event
cancelWorkflowRunCancel an in-progress workflow run
rerunWorkflowRunRe-run a workflow run, optionally only failed jobs

Checks and Statuses

ToolDescription
listCheckRunsList check runs (Checks API: GitHub Actions and other CI providers) for a commit, branch, or tag
getCombinedStatusGet the combined commit status (Statuses API: legacy CI integrations) for a commit, branch, or tag
getCiFailureContextDiagnose CI failures for a ref — combined status, failing checks, and failed workflow jobs in one call

Releases

ToolDescription
listReleasesList releases, newest first (includes drafts and prereleases)
getLatestReleaseGet the latest published release (body truncated by default; set detail: full for complete notes)
getReleaseGet a specific release by ID, including its assets
getReleaseContextFetch a release plus the previous release and tag comparison in one call
createReleaseCreate a new release (and its tag if needed)
updateReleaseUpdate a release's tag, target, title, notes, draft, or prerelease status
deleteReleasePermanently delete a release (does not delete the underlying git tag)

Commits

ToolDescription
listCommitsList commits, optionally filtered by file path, author, or date range. When hasMore, pass nextPage
getCommitGet a commit's full details including changed files and diffs
getBlameLine-level git blame for a file (GitHub GraphQL)
compareCommitsCompare two branches, tags, or commits: ahead/behind counts, commits in between, and files that differ
ToolDescription
searchCodeSearch code across GitHub with qualifier support (includes matching text snippets when available)
searchRepositoriesSearch repositories by keyword, topic, language, stars, …
searchIssuesSearch issues and pull requests using qualifiers like is:open, type:pr, or label:bug

GitHub Token

All tools authenticate with a GitHub personal access token (PAT).

Create one at GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens.

PermissionLevelRequired for
MetadataRead-onlyAlways required (auto-included)
ContentsRead-onlygetRepository, listBranches, getFileContent, getRepositoryTree, listCommits, getCommit, getBlame, compareCommits, listReleases, getLatestRelease, getRelease, getReleaseContext
ContentsRead and writecreateBranch, deleteBranch, createOrUpdateFile, createRelease, updateRelease, deleteRelease
AdministrationRead and writeforkRepository, createRepository
Pull requestsRead-onlylistPullRequests, getPullRequest, listPullRequestFiles, listPullRequestReviews, listPullRequestReviewThreads, getPullRequestContext
Pull requestsRead and writecreatePullRequest, mergePullRequest, updatePullRequest, addPullRequestComment, updatePullRequestComment, deletePullRequestComment, createPullRequestReview, replyToReviewComment, resolveReviewThread, requestReviewers
IssuesRead-onlylistIssues, getIssue, getIssueContext, listIssueComments, listLabels, listIssueReactions, listCommentReactions
IssuesRead and writecreateIssue, addIssueComment, updateIssueComment, deleteIssueComment, closeIssue, updateIssue, addLabels, removeLabel, createLabel, updateLabel, deleteLabel, addAssignees, removeAssignees, addIssueReaction, addCommentReaction
DiscussionsRead-onlylistDiscussions, getDiscussion
DiscussionsRead and writeaddDiscussionComment
GistsRead-onlylistGists, getGist, listGistComments
GistsRead and writecreateGist, updateGist, deleteGist, createGistComment
Notifications (account)Read and writelistNotifications, markNotificationRead
ActionsRead-onlylistWorkflows, listWorkflowRuns, getWorkflowRun, listWorkflowJobs, getWorkflowJobLogs, getCiFailureContext
ActionsRead and writetriggerWorkflow, cancelWorkflowRun, rerunWorkflowRun
ChecksRead-onlylistCheckRuns, getCiFailureContext
Commit statusesRead-onlygetCombinedStatus, getCiFailureContext

Search tools (searchCode, searchRepositories, searchIssues) work with any token.

Classic token

ScopeRequired for
public_repoAll tools on public repositories
repoAll tools on public and private repositories

API

createGithubTools(options)

Returns an object of tools, ready to spread into tools of any AI SDK call.

type GithubToolsOptions = {
  token?: GithubTokenInput // defaults to process.env.GITHUB_TOKEN
  requireApproval?: boolean | Partial<Record<GithubWriteToolName, boolean>>
  preset?: GithubToolPreset | GithubToolPreset[]
  context?: GithubToolsContext // default owner / repo / PR / issue / ref
}

type GithubTokenInput = string | (() => Promise<string>)

type GithubToolPreset = 'code-review' | 'issue-triage' | 'repo-explorer' | 'ci-ops' | 'security-audit' | 'release-manager' | 'discussion-moderator' | 'notification-inbox' | 'pr-author' | 'maintainer'

createGithubAgent(options)

Returns a ToolLoopAgent instance with .generate() and .stream() methods, pre-configured with GitHub tools and tailored instructions.

import { createGithubAgent } from '@github-tools/sdk'

// Prefer a preset: scoped tools + tailored prompt
const reviewer = createGithubAgent({
  model: 'anthropic/claude-opus-5.5',
  token: process.env.GITHUB_TOKEN!,
  preset: 'code-review',
  context: { owner: 'vercel', repo: 'ai', pullNumber: 42 },
})

// Add context to the built-in prompt
const triager = createGithubAgent({
  model: 'anthropic/claude-opus-5.5',
  token: process.env.GITHUB_TOKEN!,
  preset: 'issue-triage',
  additionalInstructions: 'Focus on the nuxt/ui repository. Always respond in French.',
})

// Full catalog (omit preset or use maintainer)
const agent = createGithubAgent({
  model: 'anthropic/claude-opus-5.5',
  token: process.env.GITHUB_TOKEN!,
  preset: 'maintainer',
})

// Full override: replace the built-in prompt entirely
const custom = createGithubAgent({
  model: 'anthropic/claude-opus-5.5',
  token: process.env.GITHUB_TOKEN!,
  instructions: 'You are a security auditor. Only flag security-related issues.',
})

// Use the agent
const result = await reviewer.generate({ prompt: 'Review this PR' })
const stream = reviewer.stream({ prompt: 'Review this PR' })
OptionDescription
modelLanguage model: string ('anthropic/claude-opus-5.5') or provider instance
tokenGitHub token string or async provider
presetOptional preset or array of presets to scope tools
contextDefault owner / repo / pullNumber / issueNumber / ref for tools and the system prompt
requireApprovalApproval config (same as createGithubTools)
instructionsReplaces the built-in system prompt entirely (context is still appended)
additionalInstructionsAppended to the built-in system prompt

All other ToolLoopAgent options (stopWhen, toolChoice, onStepFinish, etc.) are passed through.

createDurableGithubAgent(options)

Returns a DurableGithubAgent instance for use inside Vercel Workflow SDK functions. Every LLM call and tool execution runs as a durable step with automatic retries and crash recovery.

Supports both .stream() (real-time output to a writable) and .generate() (non-streaming, returns the full text response).

Requires the optional peer dependencies workflow and @ai-sdk/workflow:

pnpm add workflow @ai-sdk/workflow

Streaming (chat UI)

// durable-chat.workflow.ts
import { createDurableGithubAgent } from '@github-tools/sdk/workflow'
import { getWritable } from 'workflow'
import type { ModelMessage, UIMessageChunk } from 'ai'

async function chatWorkflow(messages: ModelMessage[], token: string) {
  "use workflow"
  const agent = createDurableGithubAgent({
    model: 'anthropic/claude-opus-5.5',
    token,
    preset: 'code-review',
  })
  const writable = getWritable<UIMessageChunk>()
  await agent.stream({ messages, writable })
}

Non-streaming (bot / background job, needs "use step")

// agent-turn.step.ts
import { createGithubAgent } from '@github-tools/sdk'

async function agentTurn(prompt: string) {
  "use step"
  const agent = createGithubAgent({
    model: 'anthropic/claude-opus-5.5',
    preset: 'code-review',
    requireApproval: false,
  })
  const { text } = await agent.generate({ prompt })
  return text
}

See examples/pr-review-agent for a complete PR review agent built with Chat SDK and Vercel Workflow.

All presets (code-review, issue-triage, ci-ops, repo-explorer, security-audit, release-manager, discussion-moderator, notification-inbox, pr-author, maintainer) work with createDurableGithubAgent. Options mirror createGithubAgent with additional pass-through for WorkflowAgentOptions fields like experimental_telemetry, onStepEnd, onEnd, and prepareStep. Write tools honor requireApproval via needsApproval.

resolveGithubToken(token?)

Resolves a GithubTokenInput (token string, async provider, or process.env.GITHUB_TOKEN) to a token string. Throws when no token is available.

createOctokit(token)

Returns a configured octokit instance. Useful for building custom tools.

License

MIT

Made by @HugoRCD

agent
agents
ai
ai-sdk
github
presets
tools

Significant stargazers

Eduardo San Martin Morote

6,586 followers · starred Feb 2026

wong2

2,531 followers · starred Apr 2026

Benjamin Canac

893 followers · starred Feb 2026

Saatvik Arya

90 followers · starred Apr 2026