---
title: "Model Context Protocol server (JSON-RPC 2.0 over plain HTTP POST)"
method: POST
path: "/api/mcp"
---

# Model Context Protocol server (JSON-RPC 2.0 over plain HTTP POST)

`POST /api/mcp`

## Request

- Base URL: `https://api.signalbureau.ai`
- URL: `https://api.signalbureau.ai/api/mcp`
- Auth: none declared

MCP over Streamable HTTP, stateless JSON-response mode. Protocol version is NEGOTIATED at `initialize` (supported: `2025-11-25`, `2025-06-18`, `2025-03-26`, `2024-11-05` — the server echoes the client's version when supported, else its newest; these are MESSAGE versions — only Streamable HTTP is served, and the deprecated 2024-era dual-endpoint HTTP+SSE transport is NOT provided, so `2024-11-05` is accepted only for clients that can initialize over this endpoint). Each JSON-RPC 2.0 message is its own HTTP POST; responses are plain JSON (no SSE stream, no long-lived connection, no session id issued). Standard lifecycle supported: `initialize` -> `notifications/initialized` (HTTP 202, no body; all `notifications/*` are accepted with 202) -> `tools/list` -> `tools/call`; `ping` also supported. As a documented Signal Bureau CONVENIENCE EXTENSION, direct `tools/call` without the handshake also works for lightweight stateless integrations. An `MCP-Protocol-Version` request header, when present, is validated: unsupported values receive HTTP 400. Unknown methods return JSON-RPC error -32601. GET on this endpoint returns HTTP 405 (Allow: POST) with a human-readable discovery body. Tools (29), in the order `tools/list` serves them, grouped by job: Orient first: start_here; Walk the record: traverse, search_entities, get_entity, get_topic_record, get_events, get_signals, top_accelerating, todays_brief; Trace a claim: ask, get_answer, get_corroborated, get_record, get_calibration; Quote and order: get_quote, create_order, get_order, get_account; Keep a Watch: list_standard_watches, unfurl_watch, save_watch, list_saved_watches, recall_watch, retire_watch, get_watch_feed, get_desk_feed; Feedback and coverage requests: send_feedback, get_feedback_status, propose_topic. Every tool in tools/list carries MCP annotations. Read-only, no side effects (readOnlyHint true): start_here, traverse, search_entities, get_entity, get_topic_record, get_events, get_signals, top_accelerating, todays_brief, get_answer, get_corroborated, get_record, get_calibration, get_order, get_account, list_standard_watches, unfurl_watch, list_saved_watches, get_watch_feed, get_desk_feed, get_feedback_status. Writes state (readOnlyHint false): ask, get_quote, create_order, save_watch, recall_watch, retire_watch, send_feedback, propose_topic; of these, only retire_watch cannot be undone (destructiveHint true). Call `start_here` first, then walk the record with `traverse`. To see how the record fits together, start at an entity or event (e.g. Hormuz) and traverse its maintained relationships; each step returns its relation type, receipts, standing and time. traverse opens one node of the record per call: an entity, a situation, a claim or a relation. Each node carries its standing as five separate records (evidence, admission, disposition, validity, measurement), its receipts with source urls, and its neighbours as counts plus typed ids, with a cursor for the next page. One level per call; follow any id it returns to take the next step. Free, no key. The REST twin is GET /api/v1/node/{kind}/{hex}. A first walk in three calls: (1) Open an entity by name. traverse {"id":"entity:hormuz"}, or GET /api/v1/node?id=entity:hormuz: the entity, its standing, its receipts (children), and the situations and claims it is named in (relations). (2) Follow a claim. traverse {"id":"<a relations.items[].id whose kind is claim>"}, or GET /api/v1/node/p/{hex}: the claim in its sources' words, whether it is corroborated, single source or contested, and who carried it. (3) Open its sources. traverse {"id":"<the same claim id>","level":"children"}, or GET /api/v1/node/p/{hex}?level=children: every receipt as an observation with the publisher, the date and the url; open the url to read the source itself. Earlier tool names are PERMANENT aliases, not a deprecation queue — machine callers hold tool names in configs we do not control: `orient` (now `start_here`, since 2026-09-07), `get_truth_object` (now `get_topic_record`, since 2026-09-07), `get_truth_record` (now `get_topic_record`, since 2026-10-01). Each keeps answering, returns the current tool's work, and carries a `deprecated` marker naming the current name. Earlier prompt names answer the same way: `judge-us-first` (now `look-around`, since 2026-10-06), naming the current name in the reply's `_meta.alias`. An alias never reshapes a payload underneath a caller who never asked for the new name. Prompts (3): morning-read, look-around, coverage-quote. Resources: 8. Rate limits: the `ask` tool shares the Answer Engine's daily quota and adds a 6/minute burst at the MCP door. Every JSON-RPC error carries a machine-readable `error.data.code` (parse_error, invalid_request, method_not_found, invalid_params, rate_limited, cannot_measure, data_unavailable, internal_error, or the upstream REST code such as key_required) beside the human sentence in `error.message`; rate limits add `error.data.retryAfter` and a Retry-After header. A deploy whose data could not be read answers every method with -32000 / `data.state: "cannot-measure"` (HTTP 503) rather than an empty result. JSON-RPC batches are refused with -32600.

## Request body

- object
  - `jsonrpc` '2.0', required
  - `id` union
    - integer
    - string
  - `method` 'initialize' | 'notifications/initialized' | 'tools/list' | 'tools/call' | 'ping', required
  - `params` object

## Response `200`

JSON-RPC result or error object.

- object

## Other responses

- `204`: Acknowledged notification (no body).

---

[API](https://skmtc.dev/sigb/apis/signal-bureau-api.md) · [All operations](https://skmtc.dev/sigb/apis/signal-bureau-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sigb/apis/signal-bureau-api/revisions/290c9c6ca0d0?raw)
