I recently wanted to learn about MCP (Model Context Protocol). As someone whose default programming language is Common Lisp, I naturally decided to build an MCP server using Lisp.
Thanks to the creators of 40ants-mcp, the library provides a nice pattern and code structure that I really like. However, I struggled significantly with installation and getting started. What should have taken minutes ended up taking days.
I'm sharing my experience here so that others who want to build MCP servers in Common Lisp can get started in minutes, not days like I did.
Prerequisites
Before you begin, make sure you have the following installed:
- SBCL - A high-performance Common Lisp compiler
- Roswell - A Common Lisp implementation manager and script runner
- Quicklisp - The de facto package manager for Common Lisp
- Ultralisp - A community-driven distribution of Common Lisp libraries
Installing Ultralisp
In SBCL with Quicklisp, you can enable Ultralisp by:
(ql-dist:install-dist "http://dist.ultralisp.org/" :prompt nil)
(How to install SBCL and Quicklisp is in the appendix.)
The Gotcha: Loading 40ants-mcp
Here's the issue that cost me days: when you try to load 40ants-mcp with:
(ql:quickload :40ants-mcp)
You might encounter errors. The solution is simple but not obvious—load jsonrpc first:
(ql:quickload :jsonrpc)
(ql:quickload :40ants-mcp)
This dependency isn't automatically resolved, which was the source of my frustration.
Creating Your MCP Server
Here's a minimal example from my mcp-exper package:
(in-package :mcp-exper)
(openrpc-server:define-api (mi-tools :title "mi-tools"))
(40ants-mcp/tools:define-tool (mi-tools add) (a b)
(:summary "just add")
(:param a integer "a")
(:param b integer "b")
(:result text-content)
(make-instance 'text-content :text (format nil "~a" (+ a b))))
(defun start-server ()
(40ants-mcp/server/definition:start-server mi-tools))
Key points:
- Use
openrpc-server:define-apito define your API - Use
40ants-mcp/tools:define-toolto define tools - Return
text-contentinstances for text results (MCP requires specific content types)
Running the Server
Create a Roswell script (mi-mcp-server.ros):
#!/bin/sh
#|-*- mode:lisp -*-|#
exec ros -Q -- $0 "$@"
|#
(progn
(ros:ensure-asdf)
#+quicklisp(ql:quickload '(:mcp-exper) :silent t))
(defun main (&rest argv)
(declare (ignorable argv))
(mcp-exper:start-server))
Quick Test
Run directly with Roswell:
ros mi-mcp-server.ros
Production Installation
Build and install as an executable:
ros build mi-mcp-server.ros
install -m 0755 mi-mcp-server $HOME/.local/bin/
Make sure $HOME/.local/bin is in your PATH.
Integrating with Opencode
To enable your MCP server in opencode, add this to ~/.config/opencode/opencode.json:
{
"mcp": {
"mi-tools": {
"type": "local",
"command": ["mi-mcp-server"],
"enabled": true
}
}
}
Conclusion
Building MCP servers with Common Lisp is straightforward once you know the tricks. The 40ants-mcp library is well-designed, and the OpenRPC integration works smoothly.
I hope this guide saves you the days of frustration I experienced. Happy hacking!
The full source code for this example is available at mcp-exper.
Appendix: Installing SBCL
macOS
brew install sbcl
Debian/Ubuntu
apt install sbcl
Arch Linux
pacman -S sbcl
Appendix: Installing Quicklisp
Download and install Quicklisp:
wget https://beta.quicklisp.org/quicklisp.lisp
sbcl --load quicklisp.lisp \
--eval '(quicklisp-quickstart:install)' \
--eval '(ql-util:without-prompting (ql:add-to-init-file))' \
--quit
Top comments (0)