JSON-RPC dispatch for MCP.
Takes a parsed request map, returns a response map. No I/O and no transport concerns — which is what makes it testable without a socket.
JSON-RPC dispatch for MCP. Takes a parsed request map, returns a response map. No I/O and no transport concerns — which is what makes it testable without a socket.
(configure! opts)Adopt init!'s teaching-surface options. Called before the port opens,
so no initialize ever composes without them.
Adopt `init!`'s teaching-surface options. Called before the port opens, so no initialize ever composes without them.
(handle-modern {:keys [id method params]} ctx)One stateless-era message, already past the transport's header checks. Returns the response, or nil for a notification.
One stateless-era message, already past the transport's header checks. Returns the response, or nil for a notification.
(handle-payload payload)(handle-payload payload ctx)A parsed request body — one message or a batch of them. Returns the response payload, or nil when nothing in the batch warrants a reply.
A parsed request body — one message or a batch of them. Returns the response payload, or nil when nothing in the batch warrants a reply.
(probe)The sessionless identity GET /probe serves: who answers here, minting
nothing. :server is the fixed discriminator discovery tooling keys on;
:version reads server-info — a site the release checklist already
syncs, so no fifth version location; :describe is the init!-time
self-description, nil when unset. Consumers key on field presence, not
shape completeness — fields may accrete here without coordination.
The sessionless identity `GET /probe` serves: who answers here, minting nothing. `:server` is the fixed discriminator discovery tooling keys on; `:version` reads `server-info` — a site the release checklist already syncs, so no fifth version location; `:describe` is the `init!`-time self-description, nil when unset. Consumers key on field presence, not shape completeness — fields may accrete here without coordination.
The stateless revision this server speaks beside the legacy handshake.
The stateless revision this server speaks beside the legacy handshake.
The tool table. Each entry is its own MCP declaration plus a :handler, which is stripped before the entry is sent to the client.
The tool table. Each entry is its own MCP declaration plus a :handler, which is stripped before the entry is sent to the client.
(tools-changed? sid)Whether the tool set differs from what sid last fetched. Per session
(§11), and deliberately "last fetched", not "last announced": a lost
announcement re-announces on the next stream, and a client holding a
list from a previous server run is told to refresh on its first call —
the marker only advances when a tools/list actually serves the
current set (§4). The transport asks before closing each response
stream and announces tools/list_changed while the answer is yes —
the client re-fetches promptly (measured 2026-08-04, claude-code
2.1.220), which is what makes a reloaded tool table reach a live
session without a reconnect.
Whether the tool set differs from what `sid` last fetched. Per session (§11), and deliberately "last fetched", not "last announced": a lost announcement re-announces on the next stream, and a client holding a list from a previous server run is told to refresh on its first call — the marker only advances when a `tools/list` actually serves the current set (§4). The transport asks before closing each response stream and announces `tools/list_changed` while the answer is yes — the client re-fetches promptly (measured 2026-08-04, claude-code 2.1.220), which is what makes a reloaded tool table reach a live session without a reconnect.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |