Mcparen is a small Common Lisp client for the Model Context Protocol. Its core loads on SBCL and Clozure CL on Unix-like systems and implements MCP 2025-11-25 and 2025-06-18 over standard I/O and Streamable HTTP. It requests the newer revision and accepts the older revision when selected during protocol negotiation.
The library handles:
- JSON-RPC lifecycle
- concurrent requests
- typed errors
- timeouts and cancellation
- cursor pagination
- tool calls, resources, and prompts
- HTTP sessions
- bounded process cleanup
List pagination is bounded before pages are accumulated. One list operation may
read at most 1,000 pages and retain at most 10,000 items, 16 MiB of conservative
encoded item and cursor data, and 100,000 item and cursor tree nodes. Session
restarts begin a fresh accumulation and are limited to eight attempts. These
reloadable bounds are exported with the *mcp-pagination- prefix.
(ql:quickload :mcparen)Create a lazy Streamable HTTP client:
(defparameter *client*
(mcparen:make-mcp-client
(mcparen:make-mcp-streamable-http-transport
"https://example.test/mcp"
:headers-function
(lambda ()
(let ((token (uiop:getenv "EXAMPLE_MCP_TOKEN")))
(and token
(list (cons "Authorization"
(format nil "Bearer ~A" token)))))))
:name "example-client"
:version "1.0.0"))
(unwind-protect
(progn
(mcparen:mcp-client-connect *client*)
(dolist (tool (mcparen:mcp-client-list-tools *client*))
(format t "~A: ~A~%"
(mcparen:mcp-tool-name tool)
(mcparen:mcp-tool-description tool))))
(mcparen:mcp-client-close *client*))Create a standard I/O client:
(mcparen:make-mcp-client
(mcparen:make-mcp-stdio-transport
"/absolute/path/to/server"
:arguments '("--stdio")
:directory #P"/workspace/"))Credential providers run at request or process-launch time. Callers can retain environment-variable names instead of secret values in long-lived objects.
Streamable HTTP transports also accept :exchange-scope-function. This function
receives and synchronously invokes a zero-argument thunk around each complete
HTTP exchange, from late header resolution through response parsing and
dispatch. It can establish application-owned dynamic state for normal requests,
SSE resumptions, idle listener cycles, and session deletion.
Both transport constructors accept :maximum-message-characters. The default
is 16 MiB. Mcparen rejects larger standard I/O messages, HTTP JSON bodies, SSE
lines, and aggregate SSE event data before retaining an unbounded document.
Outbound documents are checked against the same transport-specific limit before
encoding.
Public raw MCP values use these exact Common Lisp representations:
- JSON objects are hash tables with
equalkeys - JSON arrays are vectors
- JSON strings and numbers are Common Lisp strings and numbers
- JSON
true,false, andnullare returned byjson-true-value,json-false-value, andjson-null-value
json-object constructs an object from alternating string keys and values.
These representations are also used by tool schemas, raw tool metadata, tool
arguments, structured results, resources, and prompts.
mcp-client-connect performs initialize followed by
notifications/initialized. Public operations reconnect a deliberately closed
client when it is used again. mcp-client-close terminates owned resources.
mcp-client-detach only closes inherited descriptors and is suitable for a
forked image that must not signal a parent-owned server.
On supported POSIX systems, a standard I/O server must enter a process group led by its direct child process. Shutdown closes stdin, then uses bounded TERM and KILL phases.
Load mcparen/managed for shared restartable connections and discovery snapshots.
Construct mcp-managed-server instances with :name, :required-p and either
:client or :client-factory. A factory receives the server and a notification
callback; pass that callback to the transport constructor.
(asdf:load-system :mcparen/managed)
(defparameter *servers*
(make-instance 'mcparen:mcp-connection-manager
:maximum-schema-bytes (* 1024 1024)
:runtimes
(list (make-instance
'mcparen:mcp-managed-server :name "example"
:client-factory
(lambda (server notification-handler)
(declare (ignore server))
(mcparen:make-mcp-client
(mcparen:make-mcp-streamable-http-transport
"https://example.test/mcp"
:notification-handler notification-handler)))))))
(unwind-protect
(progn
(mcparen:mcp-manager-start *servers*)
(mcparen:mcp-manager-snapshot *servers*))
(mcparen:mcp-manager-close *servers*))Use mcp-manager-build with zero-argument server factories for cleanup across
factory and partial-startup failures. A server has one lifecycle owner; share
that manager among application consumers. Required servers receive aggregate
schema budget first, preserving presentation order. Optional failures are
isolated, and failed discovery is cached until a refresh is requested.
Use mcp-manager-refresh for explicit rediscovery, or
mcp-server-runtime-connect at use boundaries. Notifications and transparent
reconnections invalidate discovery. Read detached mcp-discovery-snapshot
values through mcp-server-runtime-snapshot or mcp-manager-snapshot; each
contains state, capabilities, generation, revision, tools and a diagnostic.
Use mcp-manager-collect to aggregate resources or prompts, supplying a client
list function and optionally an item identity function. Results and failures
are separate, server-qualified lists; duplicate identities within a server
are rejected when an identity function is supplied.
Use mcp-server-runtime-call for scoped client operations. Consume or project
secret-bearing results inside its callback before returning to the caller.
Specialize the mcp-managed- protocol for credential scopes, diagnostic
redaction, metadata projection and provider-specific schema validation.
mcp-managed-credential-key returns a non-secret identity of the exact active
credential snapshot and an optional resolution condition; enable persistent
identity checks with mcp-managed-credential-check-p. Specialize
mcp-managed-call-with-local-cleanup when local fallback needs application
bindings without fresh credential resolution. Before callback entry, errors
and nonlocal scope exits still trigger exactly one local teardown attempt. Use
mcp-server-runtime-cancel to interrupt in-flight requests before closing.
Close and detach leave connections restartable. Manager teardown is serialized
against discovery.
The lock order is manager, server, then notification revision. Scope and projection methods run synchronously. Within those methods, inspect the given server rather than reentering manager operations.
./script/checkPart of the Lambda Symbolics library shelf.