Skip to main content

Request path

Every HTTP request to serve goes through the same steps, in this order. A tool handler runs only after all checks pass.

  1. Route. GET /healthz is answered at once (see below). Any path other than /mcp gets 404.
  2. Authenticate. The Authorization: Bearer <token> header is verified: known token, not revoked, not expired, principal not disabled. Otherwise 401 with WWW-Authenticate: Bearer realm="session-bus".
  3. Read the body (POST). Over 64 KiB: 413. Not JSON: 400. A JSON-RPC batch (an array): 400.
  4. Authorize. The JSON-RPC method must be in the allowlist, and for tools/call the tool must be in the scope map and the token must hold its scope. Otherwise 403.
  5. Handle. Only now is an MCP server object created for this request, with the caller's principal fixed. It is discarded when the response closes.

Other HTTP methods than GET, POST and DELETE on /mcp get 405.

Method allowlist​

MethodAllowed for
initialize, notifications/initialized, ping, tools/listany valid token
tools/calla token with the tool's scope
anything elsenobody (403)

Scope map​

ToolScope
inbox, list_sessionsbus:read
send, broadcast, register, ack, unregisterbus:write

Health check​

GET /healthz is the only unauthenticated route. It runs a write probe inside a transaction that is always rolled back, then returns 200 {"ok":true,"schema":1}, or 503 {"ok":false} if the database cannot be written. The body is constant and carries no session data.

Stateless MCP​

The server does not use MCP session ids. Each request is independent. This keeps one code path for authorization and lets the bus restart without clients holding stale MCP sessions.

Tool errors​

Bus-level errors are returned as an MCP tool result with isError: true and text <CODE>: <message>, for example NAME_TAKEN: NAME_TAKEN or NOT_FOUND: no such session server-ci. See MCP tools.

Logging​

One line per request on stderr:

ts=... method=POST path=/mcp status=200 principal=me token_id=<uuid> tool=send session=laptop-api latency_ms=3 ts_login=- ts_caps=-
  • Caller-supplied values are percent-encoded.
  • Raw tokens are redacted from every log line. Only the token id is logged.
  • Successful inbox calls are logged only with BUS_LOG_LEVEL=debug, because push shims poll every 5 seconds.
  • ts_login and ts_caps come from Tailscale headers. They are logged only when bound to loopback (behind tailscale serve), for attribution. They are not verified and never used for authorization.