The agent loop
The loop: someone captures a bug → it's assigned to a bot → the bot's webhook fires → your CI runs an agent with the Peekr MCP → the agent looks at the screenshot, fixes the code, opens a PR, and resolves the capture with the link. Nobody relays anything. This page is the recipe, with GitHub Actions and Claude Code, in two flavours.
1. Create the bot
Settings → Bots → New bot. Name it (“Claude Code (CI)”), make it a guest so it sees only the groups you add it to, and save the pk_bot_… token as a repository secret called PEEKR_BOT_TOKEN.
Then, on the groups it should work: Settings → Groups → add the bot as a member, and optionally set it as the group's default assignee so every new capture there is its job.
2a. Simplest: poll on a schedule (no infrastructure)
# .github/workflows/peekr-agent.yml
name: Peekr agent
on:
schedule: [{ cron: "*/15 * * * *" }]
workflow_dispatch:
jobs:
fix:
runs-on: ubuntu-latest
permissions: { contents: write, pull-requests: write }
steps:
- uses: actions/checkout@v4
- run: npm install -g @anthropic-ai/claude-code
- run: |
cat > peekr-mcp.json <<EOF
{ "mcpServers": { "peekr": { "type": "http", "url": "https://mcp.peekr.dev/mcp",
"headers": { "Authorization": "Bearer $PEEKR_BOT_TOKEN" } } } }
EOF
env: { PEEKR_BOT_TOKEN: ${{ secrets.PEEKR_BOT_TOKEN }} }
- run: |
claude -p "Use Peekr. List captures assigned to you that are open. For each: claim it, \
look at every screenshot, find the root cause in this repo, fix it on a branch named \
peekr/<short id>, run the tests, open a PR with 'gh pr create', comment your diagnosis \
on the capture, and resolve it with the PR URL. If you can't fix one, comment why and \
leave it open." \
--mcp-config peekr-mcp.json --allowedTools "mcp__peekr__*,Bash,Edit,Write,Read"
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}Fifteen minutes of latency, zero moving parts. Fine for most teams.
2b. Instant: the bot's webhook triggers the workflow
GitHub can start a workflow from an HTTP call (repository_dispatch), but it needs a GitHub token in the request, and Peekr's webhooks carry only their own signature. So put a tiny relay in between — a Cloudflare Worker works well — that verifies Peekr's signature and calls GitHub:
// worker.js — verify Peekr's signature, then start the workflow
export default {
async fetch(req, env) {
const body = await req.text();
const ts = req.headers.get("X-Peekr-Timestamp");
const sig = req.headers.get("X-Peekr-Signature"); // "sha256=<hex>"
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(env.PEEKR_WEBHOOK_SECRET),
{ name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
const mac = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(`${ts}.${body}`));
const hex = [...new Uint8Array(mac)].map((b) => b.toString(16).padStart(2, "0")).join("");
if (sig !== `sha256=${hex}` || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return new Response("bad signature", { status: 401 });
const event = JSON.parse(body);
if (event.event !== "capture.assigned") return new Response("ignored");
await fetch(`https://api.github.com/repos/${env.GH_REPO}/dispatches`, {
method: "POST",
headers: { Authorization: `Bearer ${env.GH_TOKEN}`, Accept: "application/vnd.github+json", "User-Agent": "peekr-relay" },
body: JSON.stringify({ event_type: "peekr-assigned", client_payload: { capture: event.capture.id } }),
});
return new Response("ok");
},
};Set the bot's webhook URL to the worker, put the signing secret Peekr showed you in PEEKR_WEBHOOK_SECRET, and change the workflow trigger to:
on:
repository_dispatch: { types: [peekr-assigned] }…and the prompt to “Use Peekr. Claim capture ${{ github.event.client_payload.capture }}, look at every screenshot…”. Captures turn into PRs within a minute of being filed.
3. Keep a person in the loop
- Set the workspace to propose (Settings → General) and the bot can't mark anything resolved on its own: it proposes, the author gets a notification, and one click confirms.
- Every action shows the bot's name on the board and in the activity history; nothing it does is silent.
- Rotate the bot's token from Settings → Bots if it ever leaks; the old token stops immediately.
Other agents
Nothing here is specific to Claude Code. Any agent that speaks MCP over HTTP with a bearer header works the same way; see Connect an agent for Cursor, VS Code and Windsurf, and the API if you'd rather script it without MCP.