# Connect an agent

> **For agents:** you are probably already connected. Call `whoami()` — it returns how you are authenticated, the organization slug(s) you can reach, and your scopes. Then `list_projects(org="…")`. If a call fails with `token lacks …`, ask a human for a token with that scope; you cannot mint one yourself.

## The endpoint

```
POST https://api.bugwatch.io/mcp
Authorization: Bearer bwt_…
content-type: application/json
```

- Transport: **Streamable HTTP**, stateless — one POST per JSON-RPC 2.0 message (batches are accepted). There is no SSE stream; `GET` and `DELETE /mcp` answer `405`.
- Protocol version `2025-06-18`. Notifications (messages without an `id`) get `202` and no body.
- Authentication is the same API token as the REST API. A session cookie from the dashboard also works, which is how the dashboard's own agent panel calls it.
- The server declares `tools`, `resources`, and `prompts` capabilities and returns short `instructions` on `initialize`.

Create the token in **Settings → API tokens** with one of the presets described in [Tokens & scopes](https://docs.bugwatch.io/agents/tokens-and-scopes.md). `Read-only` is enough for exploration and `get_fix_context`.

## Client configuration

**Claude Code** (`.mcp.json` in the repo, or `~/.claude.json`):

```json
{
  "mcpServers": {
    "bugwatch": {
      "type": "http",
      "url": "https://api.bugwatch.io/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}
```

**Cursor** (`.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "bugwatch": {
      "url": "https://api.bugwatch.io/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}
```

**Claude Desktop** (stdio only, so bridge with `mcp-remote`):

```json
{
  "mcpServers": {
    "bugwatch": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://api.bugwatch.io/mcp", "--header", "Authorization: Bearer <token>"]
    }
  }
}
```

**curl** (any language, no client library):

```sh
curl -sS https://api.bugwatch.io/mcp -H 'Authorization: Bearer <token>' -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"whoami","arguments":{}}}'
```

The dashboard's **Settings → Agents** page renders these with your real token placeholder filled in, and every issue, release, and rule page has a "Copy MCP call" action that emits the exact `tools/call` body.

## Signing in instead of pasting a token

A client that supports OAuth — most MCP clients do — needs no token at all. Point it at the MCP endpoint and it will open a browser, ask you to sign in, show you what it is requesting, and get its own credential.

Nothing to configure. The client discovers everything from the server:

| It asks for | It learns |
|---|---|
| `GET /.well-known/oauth-protected-resource` | Which authorization server to use |
| `GET /.well-known/oauth-authorization-server` | Where to register, authorize and get a token |
| `POST /oauth/register` | Its own `client_id` — no pre-registration, no support ticket |

Then it sends you to a consent screen naming the client and the exact permissions, you choose which organization it may act in, and it receives an access token.

### What you are approving

**The client acts as you, in one organization.** It can never do more than you can: the permissions are narrowed three times — to what the client asked for, to what it registered for, and to what your role actually holds. A viewer approving a client that asked to write alert rules grants a read-only token, and the token says so.

**`org:admin` is never granted over OAuth.** It implies every other permission, and registration is open to any client, so it is not something to hand out by consent. An agent that needs it uses a token a human made deliberately.

**Your role is re-checked when the token is issued**, not just when you approve. If you are removed from the organization between the two, no token is issued; if you are demoted, the token carries the narrower set.

### Revoking it

An OAuth-issued token is an ordinary API token — it appears in **Settings → Tokens** under the client's name, and you revoke it there like any other. There is no separate list of connected apps to remember to check.

Tokens expire after 30 days, at which point the client repeats the flow.

### If you are implementing a client

PKCE with `code_challenge_method=S256` is required; `plain` is refused. Public clients only — `token_endpoint_auth_method=none` — since a client on somebody's laptop cannot keep a secret. Redirect URIs must be `https`, or `http` on a literal loopback address (`127.0.0.1` or `[::1]`) for a native client; `localhost` is not accepted, because the name resolves through DNS and the addresses do not. Redirect URIs are matched **exactly** against what you registered, with the loopback port the only thing allowed to vary.

Send the `resource` parameter (RFC 8707) on both the authorization and the token request, and the server will check they agree. Authorization responses carry `iss` (RFC 9207), including error responses, and the server advertises that it does.

An authorization code is single-use and lives 60 seconds. **Using one twice revokes the token the first use produced** — a code travels over a redirect, so a replay means it leaked, and the safe reading is that somebody else has the token.

## What the server exposes

- **Tools** — one per REST route, generated from a single registry. The full list with arguments, scope, and example call: [MCP tool reference](https://docs.bugwatch.io/reference/mcp-tools.md) (JSON: `/mcp-tools.json`). Each tool is dispatched through the REST handler it maps to, so validation, scope checks, and errors are identical to the API's.
- **Resources** — every documentation page as `bugwatch://docs/<slug>` (`text/markdown`), plus templates for `bugwatch://{org}/{project}/issues/{shortId}`, `…/fix-context`, and `…/events/{eventId}`.
- **Prompts** — `fix_issue`, `triage`, `release_check`; see [Agent workflows](https://docs.bugwatch.io/agents/workflows.md).

Tool results are compact text with dashboard deep links. Two tools deliberately return large JSON: `get_fix_context` (everything needed to fix an issue) and `get_event` (a full processed event, often 10–40 KB). Prefer the former.

## Audit trail

Every mutating tool call writes an `agent_actions` row — tool, arguments (channel secrets reduced to their kind), token, user, outcome, duration. Issue-scoped mutations (`resolve_issue`, `ignore_issue`, `unresolve_issue`) also append to the issue's activity feed as `mcp_action`, so humans see "Agent ran resolve_issue via token *ci-bot*" next to the status change. Read it with `list_agent_activity(org="acme")` or in **Settings → Agents → Activity**.

## Recommended first turn

1. `whoami` — confirm org and scopes.
2. `list_projects` — pick the project slug.
3. `search_docs` before guessing at any setup question.
4. `search_issues(status="unresolved")` and `get_fix_context` before proposing code changes.
