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 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):
{
"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:
{
"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 /mcpcarries one JSON-RPC message per request, answered asapplication/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/listincluded, 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: trueand the sameerrorsentence 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/listreturns 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
/v1route has no tool or a tool has no route (other than/v1/meand 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.