# BugTape MCP tools

Public tool list for agents connecting over the hosted MCP door. Delivery paths (listen vs webhook vs poll) live in [agents.md](./agents.md).

## Remote door

- **URL:** `https://app.bugtape.ai/mcp`
- **RPC:** `POST` for `initialize` / tools / resources (JSON-RPC, stateless)
- **Listen:** `GET` may open an SSE stream with keep-alive (not a second RPC door)

```http
Authorization: Bearer bt_pat_…
```

Ingest keys (`bt_live_…` / `bt_test_…`) are rejected — they are for SDK ingest only. MCP works on every plan (`features.mcp`). On Free and Prepaid credits, each agent read of a bug's captured events costs 2 credits (Free includes 150 a month); on Pro and above, reads are free.

Example client shape (field names vary by host):

```json
{
  "mcpServers": {
    "bugtape": {
      "url": "https://app.bugtape.ai/mcp",
      "headers": {
        "Authorization": "Bearer bt_pat_your_agent_token"
      }
    }
  }
}
```

Org-scoped agent PATs inherit organization on the API — `BUGTAPE_ORG_ID` / `X-BugTape-Org` are optional when the PAT is set.

## Authentication

HTTP at `https://app.bugtape.ai/mcp` is the supported path. Sign in with OAuth, or send an agent token. There is no published stdio package.

| Credential | How | Starts MCP? |
|------------|-----|-------------|
| OAuth | Sign in at `/console/connect` (Claude, ChatGPT, Cursor, Codex, `bugtape` CLI) | **Yes** |
| Agent token `bt_pat_…` | `Authorization: Bearer …` on Streamable HTTP `/mcp` | **Yes** |
| Ingest key `bt_live_…` / `bt_test_…` | SDK / `POST /v1/ingest` only | **No** — rejected |

There is no PAT→JWT mint. Request Bearer PATs are request-scoped.

Agent PATs may call the agent verbs (`create_bug`, `ack_bug`, `resolve_bug`, `listen` / `resources/subscribe`), the evidence tools (`get_repro_context`, `suggest_fix`, `get_bug`, `get_bug_events`, `get_occurrence_files`, `get_screenshot`, `get_bug_occurrences`, `get_bug_history`, `get_similar_bugs`) and the read-only routing tools (`list_recommended_critical`, `list_bugs`, `list_bug_groups`, `search_bugs`, `get_live_occurrences`, `get_bug_users`, `get_user_timeline`, `list_projects`, `get_project_stats`, `get_current_org`, `get_credit_usage`). Ordinary issue mutations — `update_bug`, `bulk_update_bugs`, `delete_bug`, `merge_bugs`, `add_comment`, `watch_bug` / `unwatch_bug` — stay **human-only**. The one status change an agent PAT may make is `resolve_bug`: status → `resolved`, in its own org and granted projects, nothing else. Separately, PATs with the named work actions and a current project grant can claim work and submit/promote verification through the tools below. There is no `agent_resolved` tool.

## Tools

### Bug management

| Tool | Description |
|------|-------------|
| `get_repro_context` | **Agent-first** pack: bug metadata + compact console/network/error timeline (not raw rrweb). Prefer this for fix loops. |
| `list_recommended_critical` | **Start here.** The response opens with `sentToYou`: bugs a person sent with **Send to agent** (or auto-notify handed you), each with `bugId`, `projectId`, `title`, `severity`, `trigger`, `sentAt` and the `next` calls. Collecting them marks the handoff received in the console (2026-09-26). Then the prefiltered agent inbox: new unique critical/high (or regressions). Prefer over `list_bugs` for “what to work on”. Rows also carry their project-scoped group totals — `group_key`, `instance_count`, `report_total`, `occurrence_count` (the `/groups` rule), `first_seen`, `last_seen`, `assignees` — plus the bug's own `assigned_to` and the group's org-scoped `assignee_name` sort key, and the group's displayed evidence — `group_severity` (maximum member severity; the raw `severity` / `recommended_severity` stay the bug's own), `member_id_sample` (at most 8 member ids; not the full set), `member_ids_omitted`, `instances_path`, `signal_classes`, `signal_families`, `any_regression`, `platforms`, `browsers`, `oses`, `session_count`, `user_count` — never narrowed by a filter; one row per signature group, and `total` counts those groups. |
| `resolve_bug` | Agent PAT marks a bug in its own org as `resolved` (needs the `suggest_fix` action, and a current grant when the token has project grants). Status only: any field other than `note` is rejected (`AGENT_RESOLVE_FIELDS`), so title, severity and assignee stay human-only. Never sets or downgrades `verified_fixed` (that needs a receipt via `promote_verification`); already resolved is a no-op; onboarding pipe checks are refused. History records "Resolved by agent …"; watchers and `status_change` webhooks are notified. A person can reopen it. |
| `ack_bug` | Agent acknowledge a bug (removes it from the recommended inbox). With `working`, optional `prUrl` (https) records the PR you opened. `working` and a new `prUrl` notify the project's humans through their existing destinations (`agent_update`). You open the PR; a human reviews it. |
| `list_bugs` | List bugs with filters (status, severity, project, search). Paginated. |
| `list_bug_groups` | Grouped triage view; accepts `platform`, `assignee`, `sort` (`last_seen`, `first_seen`, `count`, `severity`, `title`, `signal`, `assignee`) and `direction` (`asc`/`desc`). Each group is identified by `projectId` + `groupKey`. |
| `get_bug` | Get full bug details including reporters, AI summary, environment info. |
| `update_bug` | Update status, severity, or assignment. Add a triage note. **Human-only.** Agent PATs use `resolve_bug` to mark a bug resolved. |
| `delete_bug` | Permanently delete a bug and all associated data. **Human-only.** |
| `search_bugs` | Full-text search across bug titles and descriptions. |
| `create_bug` | Create a new bug report using the MCP authenticated session. If `projectId` is omitted, the first project in the org is used. |
| `bulk_update_bugs` | Batch update up to 100 bugs at once by `ids`, project-scoped `groupRefs` (`{ projectId, groupKey }` from `list_bug_groups`/`search_bugs`) or legacy `groupKeys` (rejected with `GROUP_KEY_AMBIGUOUS` when a key spans projects). **Human-only.** |
| `merge_bugs` | **Destructive.** Merge N duplicate bugs into one canonical bug. **Human-only.** Max 19 sources per call. |
| `get_similar_bugs` | Find other bugs similar to a target bug, ranked by similarity score. |
| `get_bug_history` | Full audit trail of status/severity changes and assignments. |
| `subscribe_feed` | Elect the delivery feed this agent token is woken for (`all`, `urgent_critical` = severity Score ≥ high at confidence ≥ 0.80, `ui_ux` = UI/UX Noul ≥ 0.70) and whether to be notified (`notify`, default `true`). `list_bugs`, `list_bug_groups`, `search_bugs`, `get_project_stats`, `get_live_occurrences` and the inbox resource then honor that feed unless you pass `deliveryFeed`. The token scope is named `subscribe`; there is no `subscribe` tool. |

### Claims and trusted verification

**Verified Close:** a fixer PAT records a proposal, then a different verifier PAT records an explicit `replay_assert` observation against the same `evidence_hash`. This credential separation prevents one PAT from self-verifying. It does not establish separate agent identity, require measured artifacts, or set `verified_fixed`.

Flow: fixer PAT `claim_issue` → bind pack/contract (`evidence_hash`) → `propose_fix` (never closes, never sets `verified_fixed`) → **different** PAT `verify_fix` with explicit `observation.reproduced`. Same fixer token on verify → `E_SELF_VERIFY`. Use `prepare_verification` / `submit_verification` / `promote_verification` for enrolled, signed, scope-wide measured proof. On MCP-related failure packs, `census_stamp` is a first-class optional field — record it when present. `watch_bug` stays human. Narrative: [agents.md — Verified Close](./agents.md#verified-close).

These tools require a current PAT and explicit project work grant. Actions and grants are rechecked on each call. Keep claim generation and fencingToken as strings. A capture key cannot call them. See the [public runner contract and offline signer](./runner-verification.md) for exact signing bytes, evidence upload, enrollment, and retries.

| Tool | Required PAT action | Result |
|---|---|---|
| `claim_issue` | `claim_issue` | Acquire a 60–900 second claim; retain its ID, generation and fencingToken. |
| `get_claim` | `claim_issue` | Read the current claim; expired ownership is not active. |
| `renew_claim` | `claim_issue` | Renew this PAT's live claim with the current fence. |
| `release_claim` | `claim_issue` | Release unfinished work without resolving or verifying the issue. |
| `complete_claim` | `claim_issue` | Record work completion with an applicable receipt; does not promote the issue. |
| `store_failure_pack` | `claim_issue` / `request_evidence` | Store content-addressed `agent_failure.pack` and birth/update the ReproContract `evidence_hash`. |
| `get_failure_pack` | work read scopes / `get_repro_context` | Read the linked pack when present (`get_repro_context` also embeds it). |
| `ensure_repro_contract` | `claim_issue` / `request_evidence` | Birth a ReproContract for the current generation from the first or pinned occurrence. |
| `get_repro_contract` | work read scopes / `get_repro_context` | Read contract status, `evidence_hash`, and `failure_predicate`. |
| `propose_fix` | `claim_issue` | Record PR/commit URL → `fix_proposed`. Never closes; never sets `verified_fixed`. |
| `verify_fix` | `submit_verification` | Separate-PAT assertion bound by `evidence_hash`; requires explicit `observation.reproduced`. Same PAT as fixer → `E_SELF_VERIFY`. MVP method `replay_assert` only (others 501). Does not flip `verified_fixed`. |
| `prepare_verification` | `submit_verification` | Issue a short-lived challenge for an enrolled runner; not a test pass. |
| `submit_verification` | `submit_verification` | Store exact scope, observations and artifact references. Unsigned assertions remain pending. |
| `get_verification_receipt` | `read_verification` | Read the historical receipt and current applicability. |
| `request_evidence` | `request_evidence` | Return authorized receipt artifact metadata; not hosted content or storage availability. |
| `promote_verification` | `submit_verification` | Requires a live claim and current trusted receipts covering every observed application/runtime and proposed repaired build. Success completes the claim atomically. |

`register_verification_runner`, `register_verification_device`, `list_verification_enrollment`, `revoke_verification_runner`, and `revoke_verification_device` require a **human organization admin** session. They do not let an agent create its own trust. Send only public runner keys. Device/runtime enrollment is explicit; simulator results cannot prove physical-device behavior.

Artifact reservation/upload is a separate HTTP capability. A PAT needs `create_bug` and a current project grant to write its CI log; `submit_verification` alone is insufficient. Check storage capabilities and ready/hash state. Application/build/installation registry management remains human manager/admin only.

### Events & analysis

| Tool | Description |
|------|-------------|
| `get_bug_events` | Session replay events (DOM, network, console, errors). Filter by type. |
| `get_bug_occurrences` | Occurrences for one bug. |
| `get_occurrence_files` | Paginated discovery of files/artifacts on an occurrence. |
| `get_screenshot` | Fetch a screenshot artifact by id (when present). |
| `suggest_fix` | AI-powered analysis: errors, failed requests, investigation steps. |
| `get_credit_usage` | Check AI credit wallet before expensive calls. |

### Users & live feed

| Tool | Description |
|------|-------------|
| `get_bug_users` | Who hit this bug (per end-user key) with counts and last occurrence. |
| `get_user_timeline` | Everything one end user hit, newest first, across bugs/projects. |
| `get_live_occurrences` | Recent occurrences across the org. |

### Projects & org

| Tool | Description |
|------|-------------|
| `list_projects` | List projects with bug counts. |
| `get_project_stats` | Aggregate stats: bugs by severity/status, report counts, recent activity. |
| `list_team` | Team members with roles and notification preferences. |
| `list_my_orgs` | Organizations the authenticated user belongs to. |
| `get_current_org` | Organization the MCP session is pinned to. |
| `create_org` | Create an organization and join it as admin. **Human-only.** Does not create a project. |
| `create_project` | Create a project in the current organization (`BUGTAPE_ORG_ID`). **Human-only.** Then mint an ingest key in the console Setup page. |

### Comments & watchers

| Tool | Description |
|------|-------------|
| `add_comment` | Add a comment to a bug. Supports @email mentions. **Human-only.** |
| `get_bug_comments` | List comments on a bug. |
| `watch_bug` | **Human** notification preference (email/in-app). Not for agent waits. |
| `unwatch_bug` | Stop human watching. |
| `listen` | **Agent** wait on a real status transition or project inbox. Optional `toStatus` / durable `afterCursor` / `resourceUri`. The inbox wait also wakes with `kind: "agent_notify"` when a human pressed **Send to agent** or auto-notify fired. A retained cursor the server no longer has is a successful result with `kind: "cursor_expired"` (no `bugId`). That is not "no events". |

## MCP subscriptions (agent listen)

Both paths are supported. `resources/subscribe` acks interest and returns delivery hints under `_meta["bugtape/delivery"]`; event delivery still uses `listen` or `GET /mcp?resource=…`.

1. `resources/subscribe` on `bugtape://bugs/{bugId}/status`, bare `bugtape://bugs/{bugId}`, or `bugtape://projects/{projectId}/inbox`
2. Receive `_meta["io.modelcontextprotocol/subscriptionId"]` plus `_meta["bugtape/delivery"]` with `delivery:"listen"`, `hint`, `listenTool:"listen"`, `streamUrl:"/mcp?resource=<url-encoded uri>"` (MCP `EmptyResultSchema` rejects unknown top-level keys, so these live under `_meta` only) and `notifications/subscriptions/acknowledged`
3. **To receive updates**, either call the `listen` tool, or open `GET /mcp?resource=<uri>` with `Accept: text/event-stream` (example: `GET /mcp?resource=bugtape://projects/{projectId}/inbox`). `resources/subscribe` alone does not open a stream; bare `GET /mcp` returns **405** with typed code `SUBSCRIBE_STREAM_REQUIRES_RESOURCE` and never streams.
4. Handle `notifications/resources/updated`, then `resources/read` — inbox pings are bug-level (new fingerprint), not occurrence-level
5. Or call `listen` directly. Optional `afterCursor` replays this project's retained events (Postgres on the hosted service, one sequence per project, 7 days and at most 1000 events). Pass `0` to replay the retained window. Omit `afterCursor` to wait only. Pass an inbox `resourceUri` to wait for or replay new-bug and `agent_notify` events. A dropped SSE stream is not a replay log.
6. `agent_notify` means a human or the project's auto-notify handed you this bug. Read `get_repro_context`, `ack_bug working`, and `ack_bug working` with `prUrl` once you open the PR. The MCP server reads handoffs from `GET /v1/projects/{projectId}/agent-notifications` (subscribe scope); collecting one marks it delivered on the issue page.

### `listen` `kind: "cursor_expired"`

`listen` puts JSON in a **successful** tool result. `isError` is not set, so the call did not fail and the agent must not retry the same cursor. A real event has a `bugId`. `cursor_expired` has no `bugId`. Do not treat that as "no events", an empty inbox, or a timeout.

```json
{ "kind": "cursor_expired", "code": "cursor_expired", "cursorExpired": true, "message": "cursor expired, resync" }
```

`kind` is the field to branch on. It is `cursor_expired` when the cursor is from before this log, beyond a position this project issued, or older than the retained prefix (7 days and 1000 events per project). Cursors are positions within the selected project; the same number can exist in two projects. The server never returns another project's events.

Resync, in this order:

1. `resources/read` on `bugtape://projects/{projectId}/inbox` and take that open list as the current snapshot.
2. Call `listen` again on that same inbox URI with `afterCursor: 0` (or omit `afterCursor`) and **save the new `cursor`**.
3. Do not pass the expired cursor again.

A status wait uses the same `kind`. After the inbox read, `listen` on `bugId` the same way and save that cursor.

`GET /mcp` for SSE requires `Accept: text/event-stream`, an explicit subscribe-scoped Bearer PAT, and 1–32 `resource` query values from the same workspace. No resource filter returns **405** (`Allow: GET, POST`) with JSON-RPC `error.data = { code: "SUBSCRIBE_STREAM_REQUIRES_RESOURCE", hint, example }` so agents learn to call `listen` or open `GET /mcp?resource=…`, while standard MCP clients treat 405 as optional SSE unavailable and continue with POST tools.

See [agents.md](./agents.md) for the full delivery contract and fix loop.

## Tool effects for plugin hosts

Every tool advertises a title and explicit MCP read-only, destructive, idempotent and open-world hints. These describe effects; the existing workspace and scope gates still decide access. Pure retrieval is read-only. Captured-event reads (including repro context and file/screenshot occurrence checks) can use credits, so they are effectful. Recommended triage and listen can record handoff delivery. Changes, revocations, deletions and notification/model calls are classified separately. Hosts should refresh server metadata after a release.
