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.