Connectors
GitHub
Mention an agent in a GitHub issue or pull request comment.
Quickstart
Create a bot account and token
Create a GitHub account for the agent, or use your own, and a fine-grained personal access token for the repository with the Issues permission set to read and write. It covers comments on pull requests too.
Add the route
npm add @rivet-dev/pi rivetkit hono @octokit/rest @octokit/webhooks-methods
import { pi } from "@rivet-dev/pi";
import { Hono } from "hono";
import { setup } from "rivetkit";
import { type GitHubComment, github, postReply } from "./github";
const agent = pi({
model: "anthropic/claude-opus-5-5",
state: { pending: [] as GitHubComment[] },
vars: { replying: false },
actions: {
receive: async (c, comment: GitHubComment) => {
c.state.pending.push(comment);
await c.schedule.after(0, "drain");
},
// Answers the oldest message. Messages that arrive mid-run wait their turn.
drain: async (c): Promise<void> => {
const comment = c.state.pending[0];
if (!comment || c.vars.replying) return;
c.vars.replying = true;
try {
const self = c.client<typeof registry>().agent.getForId(c.actorId);
await self.prompt(comment.text);
await postReply(comment, await self.getLastAssistantText());
} catch (error) {
c.log.error({ msg: "reply failed", error });
} finally {
c.state.pending.shift();
c.vars.replying = false;
}
if (c.state.pending.length > 0) await c.schedule.after(0, "drain");
},
},
});
export const registry = setup({ use: { agent } });
const app = new Hono();
app.all("/api/rivet/*", (c) => registry.handler(c.req.raw));
app.route("/github", github);
export default app;
import { Octokit } from "@octokit/rest";
import { verify } from "@octokit/webhooks-methods";
import { Hono } from "hono";
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
export type GitHubComment = { owner: string; repo: string; issueNumber: number; text: string };
const botLogin = process.env.GITHUB_BOT_LOGIN ?? "";
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const client = createClient<typeof registry>();
export const github = new Hono();
github.post("/webhook", async (c) => {
const body = await c.req.text();
const signature = c.req.header("x-hub-signature-256") ?? "";
if (!(await verify(process.env.GITHUB_WEBHOOK_SECRET ?? "", body, signature))) {
return c.text("invalid signature", 401);
}
if (c.req.header("x-github-event") !== "issue_comment") return c.body(null, 202);
const { action, comment, issue, repository } = JSON.parse(body);
if (action === "created" && comment.user.login !== botLogin && comment.body.includes(`@${botLogin}`)) {
const owner = repository.owner.login;
const agent = client.agent.getOrCreate([owner, repository.name, String(issue.number)]);
await agent.receive({ owner, repo: repository.name, issueNumber: issue.number, text: comment.body });
}
return c.body(null, 202);
});
export async function postReply(comment: GitHubComment, reply: string | undefined) {
await octokit.rest.issues.createComment({
owner: comment.owner,
repo: comment.repo,
issue_number: comment.issueNumber,
body: reply || "I finished without a reply.",
});
}
Run the server with any runtime that serves a fetch handler, for example npx srvx --import tsx server.ts. See HTTP Server.
Set the environment
GITHUB_WEBHOOK_SECRET=...
GITHUB_TOKEN=github_pat_...
GITHUB_BOT_LOGIN=my-agent
ANTHROPIC_API_KEY=sk-ant-...
Add the webhook
In the repository’s Settings > Webhooks, add a webhook with the payload URL https://<your-worker>/github/webhook, content type application/json, your secret, and the Issue comments event. Comment @my-agent on an issue or pull request.
How it works
- The route verifies GitHub’s
X-Hub-Signature-256signature against the raw body. - Each issue or pull request gets its own agent, keyed by owner, repository, and number. Every mention in the thread continues the same session.
- The route only answers new comments that mention the bot, and never the bot’s own comments, so replies do not loop.
- GitHub times out a delivery after ten seconds, so the route only hands the comment to the agent’s
receiveaction and returns. Thereceiveaction adds the message to the agent’s queue and schedulesdrain, which prompts the agent with the oldest message and posts the answer as a comment with Octokit. A message that arrives while the agent is answering waits in the queue. The queue is saved with the agent, so replies still run if the request that delivered a message goes away.
Configure
| Variable | Purpose |
|---|---|
GITHUB_WEBHOOK_SECRET | Required. Verifies that deliveries come from GitHub. |
GITHUB_TOKEN | Required. Posts replies as the bot account. |
GITHUB_BOT_LOGIN | Required. The bot’s username. Comments that mention it start a reply. |
ANTHROPIC_API_KEY | Required. The agent’s model key. For another provider, change model and set its key instead, such as OPENAI_API_KEY or OPENROUTER_API_KEY. See LLM API Keys. |
Supported events
| GitHub event | Webhook path | What the route does |
|---|---|---|
issue_comment, created | /github/webhook | Hands the comment to the issue’s agent, which replies with a comment. Covers pull request conversations too. |