@github-tools/sdk

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

97 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, getCommit, getBlame, compareCommits, getRepository, getFileContent, getRepositoryTree, searchCode, listCommits, listBranches, getBranchRules, getCollaboratorPermission, listCheckRuns, getCombinedStatus, updatePullRequest, addPullRequestComment, updatePullRequestComment, deletePullRequestComment, createPullRequestReview, replyToReviewComment, resolveReviewThread, requestReviewers, createCommitStatus
issue-triagelistIssues, getIssueContext, listIssueComments, createIssue, addIssueComment, updateIssueComment, deleteIssueComment, closeIssue, updateIssue, addLabels, removeLabel, createLabel, updateLabel, deleteLabel, addAssignees, removeAssignees, listMilestones, listSubIssues, addSubIssue, listIssueReactions, addIssueReaction, listCommentReactions, addCommentReaction, getRepository, getFileContent, getRepositoryTree, searchCode, listCommits, searchRepositories, searchIssues, getPullRequest, getCollaboratorPermission
repo-explorerAll read-only tools including discussions, gists, workflows, checks/statuses, and releases (no write operations)
ci-opsgetRepository, getFileContent, getRepositoryTree, searchCode, listCommits, listBranches, getBranchRules, getCommit, getPullRequest, listWorkflows, listWorkflowRuns, getWorkflowRun, listWorkflowJobs, getWorkflowJobLogs, listCheckRuns, getCombinedStatus, getCiFailureContext, triggerWorkflow, cancelWorkflowRun, rerunWorkflowRun, createCommitStatus
security-auditRead-only exploration (getFileContent, getRepositoryTree, searchCode, listCommits, getCommit, getBlame, compareCommits), PR and CI visibility (including getWorkflowJobLogs), the repository's code scanning, Dependabot and secret scanning alerts, plus createIssue, addIssueComment, addLabels to report findings (no destructive writes)
release-managergetRepository, getFileContent, getRepositoryTree, searchCode, listCommits, listBranches, getCommit, compareCommits, listReleases, getLatestRelease, getRelease, getReleaseContext, createRelease, updateRelease, deleteRelease, listWorkflows, listWorkflowRuns, getWorkflowRun, listWorkflowJobs, triggerWorkflow, getCombinedStatus, getCiFailureContext, listPullRequests, getPullRequest
discussion-moderatorlistDiscussions, getDiscussion, addDiscussionComment, getRepository, getFileContent, getRepositoryTree, searchCode, listCommits, searchIssues, getIssueContext, addIssueComment
notification-inboxlistNotifications, markNotificationRead, getRepository, getFileContent, getRepositoryTree, searchCode, listCommits, getIssue, getIssueContext, getPullRequest, getDiscussion (requires a Notifications PAT)
pr-authorgetRepository, getFileContent, getRepositoryTree, searchCode, listCommits, listBranches, getBranchRules, getCollaboratorPermission, createBranch, deleteBranch, createOrUpdateFile, editFiles, createPullRequest, updatePullRequest, updatePullRequestBranch, listPullRequests, getPullRequest, listPullRequestFiles, listPullRequestReviewThreads, replyToReviewComment, resolveReviewThread, compareCommits, getCommit
maintainerAll 97 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' }) asks a fast evaluation model which tools the conversation's first request needs. It lists those and the core read tools for the whole conversation, and adds a callGithubTool tool the model can use to call any other GitHub tool without changing the tool list, so the provider's prompt cache holds. evaluation: { selection: 'presets' } routes each request to at most two presets instead and uses their system prompts. 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. When the client sends the conversation back on each request, as with useChat, also pass experimental_toolApprovalSecret so a client can't forge an approval: see Sign approvals.

// 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, editFiles, createPullRequest, mergePullRequest, enablePullRequestAutoMerge, disablePullRequestAutoMerge, updatePullRequest, updatePullRequestBranch, addPullRequestComment, updatePullRequestComment, deletePullRequestComment, createPullRequestReview, replyToReviewComment, resolveReviewThread, requestReviewers, createIssue, addIssueComment, updateIssueComment, deleteIssueComment, closeIssue, updateIssue, addLabels, removeLabel, createLabel, updateLabel, deleteLabel, addAssignees, removeAssignees, addSubIssue, addIssueReaction, addCommentReaction, addDiscussionComment, markNotificationRead, createGist, updateGist, deleteGist, createGistComment, triggerWorkflow, cancelWorkflowRun, rerunWorkflowRun, createCommitStatus, 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.

Guardrails

Approval decides whether a call runs. guardrails decide where any call may act, whoever approves it:

createGithubTools({
  token,
  guardrails: {
    repositories: ['vercel-labs/*'], // 'owner/repo' or 'owner/*'; a call to any other repository fails
    branches: ['agent/*'], // commits, branch creation and deletion, merges, and pull request branch updates must target a matching branch
  },
})

The checks run before any request reaches GitHub, and a blocked call fails with github_tools.GUARDRAIL_BLOCKED and a reason the model can act on. Even without guardrails, a call whose input contains what looks like a credential (a GitHub, npm, Slack, Stripe, Anthropic, OpenAI, Google or AWS key, or a private key) fails; set guardrails: { secrets: 'allow' } to turn that off. createGithubAgent denies a blocked write instead of asking for approval. Tools without a repository target (search, gists, notifications) aren't restricted. For tools built from individual factories, wrap the token with withGithubGuardrails(token, guardrails). See Guardrails.

Shadow mode

dryRun: true runs reads as usual, but no write reaches GitHub and nobody is asked to approve one. Each write goes through guardrails, then returns { dryRun: true, tool, input, approval, message }, where approval says whether a person would have been asked. Use it to roll out a bot that runs on its own, test against real data, or compare models, before letting it write. It fits writes that stand on their own (labels, comments, reviews), not chains where a later step needs an earlier write's result. See Shadow mode.

Text from GitHub

Tool results drop HTML comments and invisible Unicode characters from issue, pull request, comment, review, release and discussion bodies, since a rendered page hides them but a model reads them. The default agent prompts tell the model to treat GitHub text as data and act only on what the user asked. See Text from GitHub.

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, editFiles 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/ai@0.64 @tanstack/ai-vercel-gateway@0.3 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).

eve extension (recommended)

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)
editFilesNot idempotent: a re-run whose find text was already replaced fails with "not found" instead of committing twice
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)
getBranchRulesList a branch's required checks, required reviews and other protection rules
getCollaboratorPermissionGet a user's permission and role on a repository, to check whether they may trigger the bot or approve an action
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
editFilesEdit files in one commit by exact text replacement or whole contents, create the branch if needed, and optionally open a pull request

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, optional CI checks, and optionally the issues it closes, in one call
createPullRequestOpen a new PR
mergePullRequestMerge a PR (merge, squash, or rebase)
enablePullRequestAutoMergeMerge a PR automatically once its required checks and reviews pass
disablePullRequestAutoMergeCancel a pending auto-merge
updatePullRequestUpdate a PR's title, body, state, base branch, or draft status
updatePullRequestBranchBring a PR branch up to date by merging its base branch into it
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 on a line or a range of lines, including suggested changes
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, optionally in a milestone
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
listMilestonesList milestones with their due dates and open and closed issue counts
listSubIssuesList the sub-issues of an issue
addSubIssueAdd an existing issue as a sub-issue, from the same repository or another one with the same owner

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
createCommitStatusReport a result on a commit as a commit status; works with a PAT or a GitHub App, unlike check runs
getCiFailureContextDiagnose CI failures for a ref or one workflow run: failing checks, failed jobs and steps, and the end of each failed job's log, 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 or tag, including its assets
getReleaseContextFetch a release (latest, by ID, or by tag) plus the release before it and their 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

Search

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

Security alerts

ToolDescription
listCodeScanningAlertsList code scanning alerts with the rule, severity and location
listDependabotAlertsList Dependabot alerts with the advisory and first patched version (cursor pages: pass nextCursor as after)
listSecretScanningAlertsList secret scanning alerts; the secret itself is never returned

GitHub Token

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

Fine-grained token (recommended)

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

PermissionLevelRequired for
MetadataRead-onlyAlways required (auto-included); the only permission getCollaboratorPermission needs
ContentsRead-onlygetRepository, listBranches, getBranchRules, getFileContent, getRepositoryTree, listCommits, getCommit, getBlame, compareCommits, listReleases, getLatestRelease, getRelease, getReleaseContext
ContentsRead and writecreateBranch, deleteBranch, createOrUpdateFile, editFiles, createRelease, updateRelease, deleteRelease
AdministrationRead and writeforkRepository, createRepository
Pull requestsRead-onlylistPullRequests, getPullRequest, listPullRequestFiles, listPullRequestReviews, listPullRequestReviewThreads, getPullRequestContext
Pull requestsRead and writecreatePullRequest, editFiles (with pullRequest), mergePullRequest, enablePullRequestAutoMerge, disablePullRequestAutoMerge, updatePullRequest, updatePullRequestBranch, addPullRequestComment, updatePullRequestComment, deletePullRequestComment, createPullRequestReview, replyToReviewComment, resolveReviewThread, requestReviewers
IssuesRead-onlylistIssues, getIssue, getIssueContext, listIssueComments, listLabels, listMilestones, listSubIssues, listIssueReactions, listCommentReactions
IssuesRead and writecreateIssue, addIssueComment, updateIssueComment, deleteIssueComment, closeIssue, updateIssue, addLabels, removeLabel, createLabel, updateLabel, deleteLabel, addAssignees, removeAssignees, addSubIssue, 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
Commit statusesRead and writecreateCommitStatus
Code scanning alertsRead-onlylistCodeScanningAlerts
Dependabot alertsRead-onlylistDependabotAlerts
Secret scanning alertsRead-onlylistSecretScanningAlerts

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, except the security alert tools
security_eventslistCodeScanningAlerts, listDependabotAlerts, listSecretScanningAlerts

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
  guardrails?: { repositories?: string[], branches?: string[] } // where any call may act
  dryRun?: boolean // shadow mode: reads run, writes don't reach GitHub or ask anyone
}

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 = await 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)

Call .generate() from the workflow function. Each model call and tool call is its own durable step. It needs @ai-sdk/workflow 2.0.59 or later. Return plain fields, since the result itself isn't serializable:

// review.workflow.ts
import { createDurableGithubAgent } from '@github-tools/sdk/workflow'
import type { ModelMessage } from 'ai'

async function reviewWorkflow(messages: ModelMessage[], token: string) {
  "use workflow"
  const agent = createDurableGithubAgent({ model: 'anthropic/claude-opus-5.5', token, preset: 'code-review' })
  const result = await agent.generate({ messages })
  return { text: result.text, responseMessages: result.responseMessages }
}

To run the whole loop as one step instead, call createGithubAgent inside a "use step" function:

// 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 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