mcp-lisp

mcp
SUMMARY

Common Lisp SDK for Model Context Protocol (MCP) and Agent-to-Agent Protocol (A2A)

README.md

mcp-lisp

Common Lisp SDK for Model Context Protocol (MCP 2025-11-25) — server, client, agent, behavioral spec engine.

44/44 conformance checks passing (32 scenarios) against @modelcontextprotocol/conformance.

Requirements

Installation

ln -s /path/to/mcp-lisp ~/quicklisp/local-projects/mcp-lisp
(ql:quickload :mcp-lisp)

MCP Server

(use-package :mcp-lisp)

;; Tool with annotations and enum support
(define-tool lookup-order
    ((order-id string "Order ID" :required t)
     (status string "Filter by status" :enum ("pending" "shipped" "delivered")))
  "Look up an order by ID."
  (:annotations :read-only t)
  (fetch-order order-id status))

;; Resource
(define-resource "config://settings"
    (:name "Settings" :mime-type "application/json")
  "Application settings."
  (encode-json *settings*))

;; Prompt
(define-prompt summarize ((text string "Text to summarize" :required t))
  "Generate a summarization prompt."
  (list (make-ht "role" "user"
                 "content" (make-ht "type" "text"
                                    "text" (format nil "Summarize: ~a" text)))))

;; Start (stdio for Claude Code, blocking)
(run-server :name "my-server" :version "1.0.0")

Transports

  • :stdio (default) -- newline-delimited JSON over stdio, for Claude Code
  • :sse -- Streamable HTTP via Woo (libev), for persistent servers and HTTP clients
;; Non-blocking HTTP server for REPL development
(defvar *server* (make-server :name "my-server" :version "1.0.0"))
(server-start *server* :transport :sse :port 8080)
;; server running on http://localhost:8080/mcp
(server-stop *server*)

SSE Server Configuration

Parameter Default Description
:port 8080 Listen port
:event-loops CPU cores Woo event loop threads. Handle all requests except tools/call.
:tool-workers = event-loops Worker pool threads for tools/call. Runs off the event loop so tool handlers can block (e.g. sampling, elicitation) without deadlocking.
(server-start *server* :transport :sse :port 9090
              :event-loops 4 :tool-workers 8)

Structured Tool Errors

Tools can signal categorized errors for agent recovery decisions:

(error 'tool-error
       :message "Refund exceeds $500 policy limit"
       :category :business
       :retryable nil)

Categories: :transient, :validation, :permission, :business.

Structured Access Log

Set *access-log-stream* for JSON-lines operational logging:

(setf mcp-lisp/src/transport/mcp-woo:*access-log-stream* *standard-output*)
{"ts":"2026-03-15T08:44:00Z","session":"3982553040-11","method":"tools/call","id":2,"duration_us":165,"status":"ok","target":"test_tool_with_logging"}

MCP Client

Supports both stdio (subprocess) and Streamable HTTP transports:

;; HTTP client
(with-client (c "http://localhost:8080/mcp")
  (dolist (tool (list-tools c))
    (format t "~a~%" (gethash "name" tool)))
  (call-tool c "echo" :message "hello"))

;; Stdio client (spawns subprocess)
(with-client (c "sbcl" "--script" "server.lisp")
  (list-tools c))

REPL Agent

Run Claude (or Groq, OpenAI) with tools in your Lisp environment:

(use-package :mcp-lisp)

(setf *provider* :anthropic)  ; or :groq, :openai

(run-agent "Write a function that computes fibonacci numbers and test it"
           :system "You are a Lisp coding assistant."
           :max-iterations 10
           :thinking-budget 10000)    ; Anthropic extended thinking (optional)

Built-in Tools

Tool Description
eval_lisp Evaluate Lisp in a sandboxed package. Per-session isolation.
clear_repl Reset the sandbox.
shell Execute shell commands.
read_file Read file contents.
web_search Search via Tavily.
grep_files Search file contents (uses rg or grep).
find_files Find files by name (uses fd or find).
list_tools List all registered tools.

Multi-Provider Support

(setf *provider* :anthropic)  ; Claude (default: claude-sonnet-4-20250514)
(setf *provider* :groq)       ; Llama (default: llama-4-maverick-17b-128e-instruct)
(setf *provider* :openai)     ; GPT (default: gpt-4o)
(setf *model* "claude-opus-4-20250514")  ; override default

Prompt Caching

Anthropic prompt caching is enabled automatically. The system prompt and tool definitions are marked with cache_control, so iterations 2+ of the agent loop pay ~0.1x for the cached prefix. The verbose output shows cache status:

[Tokens: 459 in, 276 out (cache hit: 1,141) | Session: 1,843 in, 610 out]

Extended Thinking

Enable Anthropic's extended thinking for complex reasoning tasks:

(run-agent "Design a data structure for..."
           :thinking-budget 10000)  ; tokens allocated for reasoning

The agent shows [Thinking: N chars] in verbose output. Thinking is incompatible with tool_choice, so the model chooses tools freely when thinking is enabled.

Agent Swarm

agent-swarm.lisp is a multi-agent coordinator that decomposes tasks across specialists:

Coordinator (delegation + test_tool)
  ├─ ask_researcher  →  Researcher (web_search, read_file)
  ├─ ask_coder       →  Coder (eval_lisp, clear_repl, test_tool)
  ├─ ask_analyst     →  Analyst (shell, read_file)
  └─ test_tool       →  Test agent (any registered tool)
sbcl --load agent-swarm.lisp

Self-Extending: Runtime Tool Creation

The swarm can create and register new MCP tools at runtime, then test them via a sub-agent that discovers the tool from its schema alone. Tools created during a session persist in the registry and are available to subsequent tasks.

Example session (full trace):

swarm> create and register a new MCP tool called add-n -- that should take
       at least 1 integer and add them all. once done, call this new tool
       a few times, with different values

>> CODER: defines add-n via eval_lisp, tests with pp, runs test_tool
   [Thinking: 1989 chars]
   [cache hit: 1,160]
   Test 1 - Sum of [1, 2, 3, 4, 5]:  → 15
   Test 2 - Single integer [42]:      → 42
   Test 3 - Mix [-5, 10, -3, 8]:      → 10
   test_tool smoke test: 10+25+7+13   → 55

>> TEST-AGENT: coordinator calls add_n via test_tool
   7 + 8 + 9     → 24
   100+200+300+50 → 650
   -5+10-15+25    → 15
   42             → 42

The coder defines the tool, unit-tests it with pp, then test_tool spawns a fresh agent that only sees add_n in its tool list — verifying the schema and description work end-to-end.

Behavioral Spec Engine

A specification DSL for capturing domain models — entities, rules, invariants — with property-based testing, state machine analysis, and PostgreSQL codegen. Define the domain once; the engine validates completeness, tests correctness across random data and state transitions, and generates DDL with CHECK constraints and transition triggers derived from your spec.

(defentity end-device ()
  (id string :required t :unique t)
  (lfdi string :required t :unique t)
  (lifecycle (member :active :soft-deleted :hard-deleted) :default :active)
  (enabled boolean :default t)
  (:belongs-to aggregator :of end-device :optional t))

(defrule soft-delete-device
  :when (end-device :lifecycle :active)
  :sets ((end-device-enabled end-device) nil)
  :ensures ((eq (end-device-lifecycle end-device) :soft-deleted)))

(definvariant active-means-enabled
  :on end-device
  :check (if (eq (end-device-lifecycle end-device) :active)
             (end-device-enabled end-device) t))

(run-pbt :trials 500 :negative-trials 200)  ;; 40,000 trials, 0 failures
(random-walk "end-device" :steps 20 :trials 50)
(specs-to-sql)  ;; → DDL with CHECK constraints + state transition triggers

See etc/spec-engine.md for the full guide, etc/spec-reference.md for the API reference, and examples/ for specs across domains (utility servers, power grids, trading, Kubernetes, Raft, railway signaling).

Copy etc/spec-CLAUDE.md into your project's CLAUDE.md to teach Claude Code the spec-first workflow.

A2A (Agent-to-Agent Protocol)

Partial implementation of A2A 1.0. Covers agent card discovery, messaging, skills, and task lifecycle. Does not implement streaming, push notifications, or security schemes. No conformance suite exists for A2A.

Conformance Testing

Run the MCP conformance suite against this SDK:

# Start the conformance server
sbcl --load conformance-server.lisp

# In another terminal
npx @modelcontextprotocol/conformance server --url http://localhost:8080/mcp

# Client conformance
npx @modelcontextprotocol/conformance client \
  --command "sbcl --non-interactive --load conformance-client.lisp"

Stress Testing

Two test scripts, both targeting the conformance server:

sbcl --load conformance-server.lisp   # terminal 1

soak-test.py -- uniform load, single scenario, simple stats:

uv run soak-test.py --concurrency 50

stress-test.py -- multi-scenario with per-scenario stats and result verification:

uv run stress-test.py --concurrency 20

Runs three scenario types concurrently:

  • simple -- conformance tools, resources, prompts (content assertions)
  • eval -- multi-step eval_lisp sessions: define functions, call with random inputs, verify computed results, clear sandbox, verify isolation
  • errors -- bad tool names, missing params, syntax errors, nonexistent resources

Reports per-scenario req/s, latency percentiles, assertion failures, heap usage, and leak detection. Ctrl-C to stop.

Self-Hosting Tricks

The Streamable HTTP server can serve as its own MCP client, enabling some entertaining self-referential tests.

Eval Quine

A format-based quine — code that evaluates to its own source:

(let ((s "(let ((s ~s)) (format nil s s))"))
  (format nil s s))
;; => "(let ((s \"(let ((s ~s)) (format nil s s))\")) (format nil s s))"

MCP Inception

The server connects to itself as a client, and through that connection evals code that connects again, recursively:

Claude → eval_lisp → HTTP client → server → eval_lisp → HTTP client → server → ...

Each layer adds another level of JSON string escaping. At depth 4 you get 15 layers of backslashes, but the server handles it without deadlocking.

Concurrent Self-Connection Stress Test

From inside the running server, spawn threads that each run full MCP session lifecycles back against the same server:

;; 100 concurrent workers × 100 iterations = 10,000 sessions, 90,000 requests
;; Results on Apple M4 Pro:
;;   10 workers:  15,223 req/s, 0 errors
;;   50 workers:  17,238 req/s, 0 errors
;;  100 workers:  12,717 req/s, 0 errors

Testing

make test    # 438 unit tests
make clean   # remove .fasl files

License

Dual-licensed under MIT OR Apache-2.0.

Yorumlar (0)

Sonuc bulunamadi