Files
Ben Sabic 6750d59e72 feat(github): add Vercel Connect support (#650)
Adds Vercel Connect support to the GitHub adapter:

- A new `installationToken` config option (string or resolver) supplies
installation access tokens directly, skipping the GitHub App private-key
JWT exchange.
- A new optional `webhookVerifier` verifies inbound webhooks (Connect
trigger-forwarded requests via a Vercel OIDC token) in place of the
GitHub webhook secret.

Pair with `connectGitHubAdapter()` from `@vercel/connect/chat`. Includes
a changeset (`@chat-adapter/github` minor).

Stacked on #647 (base `vercel-connect/base`).

## Companion

`@vercel/connect/chat` subpath: vercel/vercel#16826.

<img width="933" height="755" alt="CleanShot 2026-06-30 at 12 02 18"
src="https://github.com/user-attachments/assets/cc834560-0486-4f09-b8d5-8264be360544"
/>

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-03 01:38:51 +10:00

296 lines
9.5 KiB
Plaintext

---
title: GitHub
description: Respond to @mentions in PR and issue comment threads.
packageName: "@chat-adapter/github"
slug: github
type: platform
logo: github
tagline: Build bots that respond to pull request and issue comment threads. Treats issues and PRs as threads, comments as messages.
beta: true
features:
postMessage: yes
editMessage: yes
deleteMessage: yes
fileUploads: no
streaming:
status: partial
label: Buffered
scheduledMessages: no
cardFormat:
status: yes
label: GFM Markdown
buttons: no
linkButtons: no
selectMenus: no
tables:
status: yes
label: GFM
fields: yes
imagesInCards: yes
modals: no
slashCommands: no
mentions: yes
addReactions: yes
removeReactions:
status: partial
label: ""
typingIndicator: no
directMessages: no
ephemeralMessages: no
userLookup: yes
parentSubject: yes
nativeClient: yes
customApiEndpoint: yes
fetchMessages: yes
fetchSingleMessage: no
fetchThreadInfo: yes
fetchChannelMessages: yes
listThreads: yes
fetchChannelInfo: yes
postChannelMessage: no
---
## Install
<PackageInstall package="@chat-adapter/github" />
## Quick start
<Callout type="info">
The adapter auto-detects credentials from `GITHUB_TOKEN` (or `GITHUB_APP_ID`/`GITHUB_PRIVATE_KEY`), `GITHUB_WEBHOOK_SECRET`, and `GITHUB_BOT_USERNAME`.
</Callout>
```typescript title="lib/bot.ts" lineNumbers
import { Chat } from "chat";
import { createGitHubAdapter } from "@chat-adapter/github";
const bot = new Chat({
userName: "my-bot",
adapters: {
github: createGitHubAdapter(),
},
});
bot.onNewMention(async (thread, message) => {
await thread.post("Hello from GitHub!");
});
```
## Configuration
<TypeTable
type={{
token: {
type: "string",
description:
"Personal Access Token. Auto-detected from `GITHUB_TOKEN`.",
},
appId: {
type: "string | number",
description: "GitHub App ID. Auto-detected from `GITHUB_APP_ID`.",
},
privateKey: {
type: "string",
description:
"GitHub App private key (PEM). Auto-detected from `GITHUB_PRIVATE_KEY`.",
},
installationId: {
type: "number",
description:
"Installation ID. Omit for multi-tenant setups so the adapter resolves it from each webhook.",
},
installationToken: {
type: "string | (() => string | Promise<string>)",
description:
"Vercel Connect mode: installation access token (or a resolver invoked per API call). Skips the GitHub App JWT exchange.",
},
webhookSecret: {
type: "string",
description:
"Webhook secret. Auto-detected from `GITHUB_WEBHOOK_SECRET`.",
},
webhookVerifier: {
type: "(request, body) => unknown | Promise<unknown>",
description:
"Custom verifier used in place of `webhookSecret` (e.g. Vercel Connect OIDC). Takes precedence over the secret; required in Connect mode.",
},
userName: {
type: "string",
default: '"github-bot"',
description:
"Bot username for @mention detection. Auto-detected from `GITHUB_BOT_USERNAME`.",
},
apiUrl: {
type: "string",
description:
"Override the GitHub API base URL (e.g. for GitHub Enterprise Server).",
},
}}
/>
One of `token`, `appId`+`privateKey`, or `installationToken` (Vercel Connect) is
required, plus either `webhookSecret` or a `webhookVerifier`.
## Authentication
### Option A — Personal Access Token
Best for personal projects, testing, or single-repo bots.
1. Go to [Settings then Developer settings then Personal access tokens](https://github.com/settings/tokens).
2. Create a token with `repo` scope.
3. Set `GITHUB_TOKEN`.
```typescript
createGitHubAdapter({
token: process.env.GITHUB_TOKEN!,
});
```
### Option B — GitHub App (recommended)
Better rate limits, security, and supports multiple installations.
**Create the app:**
1. Go to [Settings then Developer settings then GitHub Apps then New GitHub App](https://github.com/settings/apps/new).
2. Set **Webhook URL** to `https://your-domain.com/api/webhooks/github`.
3. Set a **Webhook secret**.
4. Permissions: Issues — Read & write, Pull requests — Read & write, Metadata — Read-only.
5. Subscribe to events: Issue comment, Pull request review comment.
6. Click **Create GitHub App** and generate a private key.
**Install the app:**
1. From your app's settings click **Install App** and choose repositories.
2. Note the **Installation ID** in the URL: `https://github.com/settings/installations/12345678`.
**Single-tenant config:**
```typescript
createGitHubAdapter({
appId: process.env.GITHUB_APP_ID!,
privateKey: process.env.GITHUB_PRIVATE_KEY!,
installationId: parseInt(process.env.GITHUB_INSTALLATION_ID!, 10),
});
```
**Multi-tenant** (omit `installationId`):
```typescript
createGitHubAdapter({
appId: process.env.GITHUB_APP_ID!,
privateKey: process.env.GITHUB_PRIVATE_KEY!,
});
```
The adapter automatically extracts installation IDs from webhooks and caches API clients per-installation.
### Option C — Vercel Connect
Use [Vercel Connect](https://vercel.com/docs/connect) to source installation access tokens at runtime instead of storing a GitHub App private key. The `connectGitHubAdapter()` helper from [`@vercel/connect/chat`](https://www.npmjs.com/package/@vercel/connect) wires an `installationToken` resolver (skipping the App JWT exchange) and a `webhookVerifier` for Connect trigger-forwarded webhooks:
```typescript
import { createGitHubAdapter } from "@chat-adapter/github";
import { connectGitHubAdapter } from "@vercel/connect/chat";
createGitHubAdapter({
...connectGitHubAdapter("github/acme-github"),
userName: "my-bot[bot]",
});
```
`installationToken` accepts a `string` or `() => string | Promise<string>` resolver invoked per API call. When `webhookVerifier` is set it takes precedence over `webhookSecret` and `GITHUB_WEBHOOK_SECRET`.
<Callout type="warn">
In Connect mode the adapter only holds an installation token, so it can't auto-detect its own bot user id (the `/app` lookup needs the App's JWT). Without it the adapter can't recognize its own comments and will reply to itself in a loop. It learns the id from the first comment it posts, but that's in-memory only — not enough on serverless, where each webhook can hit a fresh instance. Pass `botUserId` (the numeric id of your `…[bot]` user, e.g. from `curl -s 'https://api.github.com/users/your-app%5Bbot%5D'`) so every instance knows it up front:
```typescript
createGitHubAdapter({
...connectGitHubAdapter("github/acme-github"),
botUserId: 12345678,
});
```
Or set the `GITHUB_BOT_USER_ID` environment variable, which the adapter auto-detects.
</Callout>
## Advanced
### Installation lookup
Resolve the GitHub App installation ID associated with a `Thread` or `Message`:
```typescript
bot.onNewMention(async (thread, message) => {
const installationIdFromThread = await github.getInstallationId(thread);
const installationIdFromMessage = await github.getInstallationId(
message.threadId
);
});
```
- **PAT mode** — returns `undefined`.
- **Single-tenant GitHub App** — returns the configured installation ID.
- **Multi-tenant** — only succeeds after the adapter has received a webhook for that repository and cached the mapping. Use a persistent state adapter so the mapping survives restarts.
### Direct API client
Access the underlying [Octokit](https://github.com/octokit/octokit.js) instance via `.octokit`:
```typescript
const github = bot.getAdapter("github").octokit;
const { data: pulls } = await github.rest.pulls.list({
owner: "vercel",
repo: "chat",
state: "open",
});
```
PAT and single-tenant App modes return the same client anywhere. Multi-tenant mode requires webhook-handler context — calling `.octokit` outside a handler throws.
> The previous `.client` getter still works as a deprecated alias for `.octokit`.
### Webhook setup
For repository or organization webhooks:
1. **Settings** then **Webhooks** then **Add webhook**.
2. Set payload URL to `https://your-domain.com/api/webhooks/github`.
3. Set **Content type** to `application/json` (the default `application/x-www-form-urlencoded` does not work).
4. Set **Secret** to match `webhookSecret`.
5. Select events: Issue comments, Pull request review comments.
GitHub App webhooks are configured during app creation — make sure to select `application/json`.
### Thread model
| Type | Context | Thread ID format |
|------|---------|-----------------|
| PR-level | PR Conversation tab | `github:{owner}/{repo}:{prNumber}` |
| Review comments | PR Files Changed tab | `github:{owner}/{repo}:{prNumber}:rc:{commentId}` |
| Issue comments | Issue thread | `github:{owner}/{repo}:issue:{issueNumber}` |
### Reactions
| SDK emoji | GitHub reaction |
|-----------|----------------|
| `thumbs_up` | +1 |
| `thumbs_down` | -1 |
| `laugh` | laugh |
| `confused` | confused |
| `heart` | heart |
| `hooray` | hooray |
| `rocket` | rocket |
| `eyes` | eyes |
## Feature support
<FeatureSupport />
## Resources
- [Ship a GitHub code review bot with Hono and Redis](https://vercel.com/kb/guide/ship-a-github-code-review-bot-with-hono-and-redis?utm_source=chat-sdk_site&utm_medium=docs&utm_campaign=adapter-github&utm_content=ship-a-github-code-review-bot-with-hono-and-redis) — Walks through building a GitHub bot that reviews pull requests on demand. When a user @mentions the bot on a PR, Chat SDK picks up the mention, spins up a Vercel Sandbox with the repo cloned, and uses AI SDK to analyze the diff.
See all guides and templates on the [resources](/resources?utm_source=chat-sdk_site&utm_medium=docs&utm_campaign=adapter-github&utm_content=resources) page.