[ mcp ]

MCP server

Connect Claude Code, Cursor, Claude Desktop or any MCP client to Dev Bench and ask about your errors in plain language: “what is the most common error this month?”, “who was affected by issue 412?”. The tools are the HTTP API's routes, with the same scopes and limits.

Connect

You need an API key (dbk_…): an owner creates one on the account page. Replace Bearer dbk_… below with it. The server is https://api.developerbench.com/mcp (Streamable HTTP); the key goes in the Authorization header.

Claude Code

[ claude code ]
claude mcp add --transport http devbench https://api.developerbench.com/mcp \
  --header "Authorization: Bearer dbk_…"

Cursor

In ~/.cursor/mcp.json (or .cursor/mcp.json in a project):

[ cursor ] mcp.json
{
  "mcpServers": {
    "devbench": {
      "url": "https://api.developerbench.com/mcp",
      "headers": {
        "Authorization": "Bearer dbk_…"
      }
    }
  }
}

Claude Desktop

Claude Desktop reaches a remote server that takes a header through the mcp-remote bridge (it needs Node.js). In claude_desktop_config.json (Settings → Developer → Edit Config), then restart Claude Desktop:

[ claude desktop ] claude_desktop_config.json
{
  "mcpServers": {
    "devbench": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://api.developerbench.com/mcp",
        "--header",
        "Authorization:${DEVBENCH_AUTH}"
      ],
      "env": {
        "DEVBENCH_AUTH": "Bearer dbk_…"
      }
    }
  }
}

Tools

Endpoint: https://api.developerbench.com/mcp, MCP Streamable HTTP transport, authenticated with the same Authorization: Bearer dbk_… header on every request. No OAuth. Stateless: no session state is needed beyond the key, and no Mcp-Session-Id is issued. Built on the official MCP Go SDK; protocol versions are the SDK's (2025-03-26 onwards).

  • POST /mcp carries one JSON-RPC message per request, answered as application/json (never an SSE stream). A JSON-RPC batch (an array) is HTTP 400 {"error":"JSON-RPC batches are not supported; send one message per request."}. GET /mcp (a server-to-client stream) is 405: the server never sends requests or notifications of its own.
  • A missing or bad key is HTTP 401 (as for /v1), before any JSON-RPC.
  • A suspended tenant's key is HTTP 403 with the suspended sentence ("Suspended tenants" above), before any JSON-RPC: tools/list included, so a connected client sees the server as unavailable rather than an empty tool list.
  • 503 (disabled) and 429 (rate limited, Retry-After) are HTTP statuses too.
  • Inside a valid session, a tool that fails (402, 403, 404, 400, 409, 502 above) returns a tool result with isError: true and the same error sentence as its text, so the client's AI can read and act on it. So does a bad argument: an argument the tool does not take (unknown argument "<name>"; the arguments are …), a missing path argument (id is required), or an object where a value belongs (<name> must be a string or a number); values themselves are checked by the route, with its 400 and 404 sentences.
  • A tool the key cannot use (not in its tools/list) can still be called and answers the route's 403 sentence as an error result.

Tools map one-to-one onto routes and call the same handler code with the same scope, edition, entitlement and audit rules. Arguments are the route's path and query parameters (and body fields for writes), with the same names, types and limits. Results are the route's JSON as structuredContent, plus one text block: a one-line summary followed by the JSON.

Tool Route Scope
list_issues GET /v1/issues issues:read
get_issue GET /v1/issues/{id} issues:read
top_errors GET /v1/errors/top sensors:read
error_trend GET /v1/fingerprints/{id}/trend (argument fingerprint_id) sensors:read
release_impact GET /v1/releases/{release}/impact sensors:read
find_user GET /v1/users users:read
issue_users GET /v1/issues/{id}/users users:read
issue_logs GET /v1/issues/{id}/logs logs:read
issue_evidence GET /v1/issues/{id}/evidence logs:read
resolve_issue POST /v1/issues/{id}/resolve issues:write (V2)
dismiss_issue POST /v1/issues/{id}/dismiss issues:write (V2)
reopen_issue POST /v1/issues/{id}/reopen issues:write (V2)
comment_on_issue POST /v1/issues/{id}/comments issues:write (V2)
signal_counts GET /v1/signals/events/{name}/counts signals:read (add-on)
top_values GET /v1/signals/events/{name}/top signals:read (add-on)
missing_followup GET /v1/signals/followups signals:read (add-on)
list_watches GET /v1/signals/watches signals:read (add-on)
watch_history GET /v1/signals/watches/{id}/firings signals:read (add-on)
create_watch POST /v1/signals/watches signals:write (add-on)

Path ids are the argument id (an integer), except error_trend (fingerprint_id), release_impact (release), and signal_counts / top_values (name, the event name). GET /v1/me has no tool; a client that needs it calls the route. A tool exists exactly while its route is served: the signals tools appear when their routes do. Path and query arguments may be sent as JSON strings or numbers ("limit": 10 and "limit": "10" are the same); body arguments have the route's body types.

  • tools/list returns only the tools the key can use (its usable scopes): a V1 read key never sees a write tool, and a tenant without the signals add-on sees no signals tool. While billing has lapsed the list is unchanged and calls return the 402 error.
  • Tool descriptions are written for an AI reader: what the tool returns, units (counts are occurrences; per_day_* are occurrences per day), and that every time is UTC.
  • The test suite fails if a /v1 route has no tool or a tool has no route (other than /v1/me and the /v1/chat/… routes, which belong to the widget and never take an API key).

Generated from the API contract when the site is built.