Minimal Model Context Protocol (MCP) client. Speaks JSON-RPC 2.0 over two transports:
:stdio — spawn the server process and frame newline-delimited JSON-RPC on its stdin/stdout (the dominant local-server pattern). A daemon thread drains stderr into the vis log so a chatty server never deadlocks on a full pipe.
:http — Streamable HTTP: POST each JSON-RPC message to one endpoint; the
reply is either application/json (one response) or
text/event-stream (SSE) — both handled. The Mcp-Session-Id
handed back by initialize rides on every later request, a
DELETE frees it on shutdown, and an optional GET listen loop
reacts to server-pushed notifications/tools/list_changed.
OAuth 2.1 (spec 2025-06-18) is supported for HTTP transports via
oauth.clj: pass :bearer-fn (a 0/1-arg fn yielding the current Bearer
token, called with the just-rejected token on 401). A 401 triggers a
single-flight refresh and one automatic retry.
A conn is a plain map of closures + state; the extension treats it
opaquely. Lifecycle: connect (which performs the initialize handshake) →
list-tools / call-tool → close.
Minimal Model Context Protocol (MCP) client. Speaks JSON-RPC 2.0 over two
transports:
:stdio — spawn the server process and frame newline-delimited JSON-RPC on
its stdin/stdout (the dominant local-server pattern). A daemon
thread drains stderr into the vis log so a chatty server never
deadlocks on a full pipe.
:http — Streamable HTTP: POST each JSON-RPC message to one endpoint; the
reply is either `application/json` (one response) or
`text/event-stream` (SSE) — both handled. The `Mcp-Session-Id`
handed back by `initialize` rides on every later request, a
`DELETE` frees it on shutdown, and an optional GET listen loop
reacts to server-pushed `notifications/tools/list_changed`.
OAuth 2.1 (spec `2025-06-18`) is supported for HTTP transports via
`oauth.clj`: pass `:bearer-fn` (a 0/1-arg fn yielding the current Bearer
token, called with the just-rejected token on 401). A 401 triggers a
single-flight refresh and one automatic retry.
A `conn` is a plain map of closures + state; the extension treats it
opaquely. Lifecycle: `connect` (which performs the `initialize` handshake) →
`list-tools` / `call-tool` → `close`.(call-tool conn tool-name arguments)(call-tool conn tool-name arguments timeout-ms)tools/call tool-name with arguments (a map). Returns the result map
({"content" [...] "isError" bool}).
`tools/call` `tool-name` with `arguments` (a map). Returns the result map
(`{"content" [...] "isError" bool}`).(connect name spec)Connect to MCP server name per its spec and run the initialize
handshake. spec supports:
stdio → {:transport :stdio :command :args :env :cwd :timeout-ms}
http → {:transport :http :url :headers :bearer-fn :timeout-ms :listen? :on-notification}
Returns an opaque conn map (or throws).
Connect to MCP server `name` per its `spec` and run the `initialize`
handshake. `spec` supports:
stdio → `{:transport :stdio :command :args :env :cwd
:timeout-ms}`
http → `{:transport :http :url :headers :bearer-fn
:timeout-ms :listen? :on-notification}`
Returns an opaque `conn` map (or throws).(invalidate-tools! conn)Clear the cached tools list so the next list-tools re-fetches — called by
the HTTP listen loop on notifications/tools/list_changed.
Clear the cached tools list so the next `list-tools` re-fetches — called by the HTTP listen loop on `notifications/tools/list_changed`.
(list-tools conn)tools/list → vector of tool maps {"name" "description" "inputSchema"}.
Cached on the conn after the first call; invalidated by
notifications/tools/list_changed from the listen channel.
`tools/list` → vector of tool maps `{"name" "description" "inputSchema"}`.
Cached on the conn after the first call; invalidated by
`notifications/tools/list_changed` from the listen channel.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 |