Cyborg Whisperer: a Protocol Officer for Lisp-Speaking Crews
Aboard a Basilisk-classvessel, no cyborg wanders. Every visit — however it beams aboard — isbound for a particular crew member, and every visit begins the sameway: with the Protocol Officer. He receives each arriving cyborg,interviews it, schools it in the ship's ways, and dispatches it tothe crew member it came to see.
Cyborg Whisperer is the kit for that post — the species, if you like,of which every Protocol Officer is a member. Post one beside any crewmember who answers the Lisply dialect, aboard ship or ashore, andarriving cyborgs find themselves received, educated, and set touseful work.
Plainly: this project is a Model Context Protocol(MCP) middleware that enablesLarge Language Models(LLMs) — thecyborgs — to interact with Lisp-baseddevelopment and runtime environments — the crew — using a lightweightprotocol called Lisply.
Note: This kit does not start or manage containers. It is a pureHTTP client to an already-running Lisply backend. Container lifecycleis owned by docker compose — see "Running it" in the BasiliskREADME — or run your ownLisply backend directly on a host and point the wrapper at itshost/port. Once a backend is running, Claude Desktop connects to itaccording to the example configurations below.
Who Is this Meant For?
- AI practitioners curious about Lisp
- Lisp practitioners curious about AI
- Anyone interested in Neuro-Symbolic Programming
- Mechanical/Civil Engineers and Designers interested in CADAutomation and Knowledge Based Engineering
- Tinkerers, meddlers, and tamperers from all walks of life
What Is it Meant to Do?
The Cyborg Whisperer middleware connectsMCP-capable AI Agent programs, orMCP Clients, such asClaudeDesktop, to Lisp-basedsystems which support a REPL, or Read-Eval-Print Loop. The connectionis meant to facilitate AI-assisted symbolic programming sometimesreferred to as Neuro-Symbolic Programming. We have coined the term"Lisply" to refer to a lightweight protocol which most any Lisp-likesystem can implement so that its resident may stand with a ProtocolOfficer of his own.
The idea is that the LLM will be able to generate and evaluatearbitrary Lisp expressions, including creating, compiling, loading,and testing entire files and projects.
Sandbox Trust Model
Lisply-backed MCP servers are intended to be exposed to the LLM astrusted sandboxes. The wrapper is not designed to restrict Lispoperators, filesystem access, or subprocess execution inside thebackend environment. Instead, the backend itself is expected to run inan isolated container or other sandbox chosen by the operator.
This is intentional:
lisp_evalis meant to support free-form, full use by LLMs./projectsmay be a host-mounted working tree, but the rest of thebackend filesystem may remain container-ephemeral.- Trust decisions should therefore be made at the container/backendboundary, not by crippling Lisp evaluation in the MCP wrapper.
The wrapper now advertises this trust model in tool metadata with adefault TRUST_AS_SANDBOX=true. Operators can override the explanatorytext with SANDBOX_NOTE if needed.
Extra Quick Start
Follow "Running it" in the BasiliskREADME — git clone the yard,then ./basilisk up.
This will get you a Docker Compose setup including a preconfiguredcontainerized Protocol Officer already at his post beside the Captain.
Quick Start
The following will get you up and running quickly with a minimaldefault configuration and a default public Common Lisp based backendrunning as a Docker container. See the main Contents below for morebackground and detailed configuration options.
1. Install
Install Node.js (18+ recommended). If on Windows, this can beinstalled directly in Windows or in WSL.
Have a running Lisply backend to connect to. The easiest way isthe docker compose stack from the BasiliskREADME (requiresDocker); alternatively,run any Lisply-compliant backend directly on your host.
Clone this
cyborg-whispererrepository to a location where yourMCP-capable AI Agent (e.g. Claude Desktop) can access it.
2. Configure your MCP-capabile AI Agent
Edit or create your AI Agent's configuration file as shown below. Inthe case of Claude Desktop, the configuration file is typically:
/mnt/c/Users/<user>/AppData/Roaming/Claude/claude_desktop_config.json
or
c:\Users\<user>\AppData\Roaming\Claude\claude_desktop_config.json
In the example below, replace /path/to/cloned/ with the correct pathto the ./scripts/mcp-wrapper.js file from the cloned repo:
{
"mcpServers": {
"gendl-ccl": {
"command": "node",
"args": [
"/path/to/cloned/cyborg-whisperer/scripts/mcp-wrapper.js",
"--server-name", "gendl-ccl",
"--http-port", "9080"
]
},
"gendl-sbcl": {
"command": "node",
"args": [
"/path/to/cloned/cyborg-whisperer/scripts/mcp-wrapper.js",
"--server-name", "gendl-sbcl",
"--http-port", "9090"
]
},
"readymax": {
"command": "node",
"args": [
"/path/to/cloned/cyborg-whisperer/scripts/mcp-wrapper.js",
"--server-name", "readymax",
"--http-port", "7080"
]
}
}
}
Or in a WSL scenario (where the Claude Desktop is running in theWindows host):
{
"mcpServers": {
"gendl-ccl": {
"command": "wsl",
"args": [
"node", "/path/to/cloned/cyborg-whisperer/scripts/mcp-wrapper.js",
"--server-name", "gendl-ccl",
"--http-port", "9080"
]
},
"gendl-sbcl": {
"command": "wsl",
"args": [
"node", "/path/to/cloned/cyborg-whisperer/scripts/mcp-wrapper.js",
"--server-name", "gendl-sbcl",
"--http-port", "9090"
]
},
"readymax": {
"command": "wsl",
"args": [
"node", "/path/to/cloned/cyborg-whisperer/scripts/mcp-wrapper.js",
"--server-name", "readymax",
"--http-port", "7080"
]
}
}
}
See the main Contents below for further configuration options, forexample how to specify an alternative Lisply backend servicehost/port. (Sharing/mounting host directories into containerizedbackends is configured in the docker compose setup, e.g. Basilisk's./basilisk, not by this wrapper.)
Each server operates independently, allowing you to work with multipleLisp environments simultaneously without tool name conflicts.
3. Restart your AI Agent and Test
With the above configuration in place, your freshly restarted AI Agentwill now have access to an MCP server called gendl-ccl, with agendl-ccl__lisp_eval MCP tool (among a few other tools discussed in the mainContents below). Note that tools are automatically prefixed with the servername to avoid conflicts when running multiple Lisply servers.
In order to test your setup, you can prompt your LLM as follows:
Evaluate
(+ 1 2 3)using the gendl-ccl__lisp_eval tool, and let me know theresult.
The LLM should invoke the requested evaluation and respond with 6 asexpected. Feel free to experiment with more complex expressions beforeproceeding.
How Does the Default Minimal Configuration Work?
The minimal default configuration described in the Quick Start aboveconnects to an already-runningGendl backend (forexample from a Basilisk stack), a Common Lispsuperset sporting a standard REPL (Read-Eval-Print Loop). The wrapperitself never pulls or starts containers. Note a second Lisply backend implementationfor Emacs lisp also exists, within theReadymaxproject (the ready room whose resident is the Captain).
System Overview
The Cyborg Whisperer middleware is implemented as a Javascript program meantto run in Node.js, and provides a bridge between your AI Agent and anycompliant Lisply backend system. This wrapperenables the AI Agent to:
- Evaluate Lisp code in the Lisply Backend and receive the results.
- Make HTTP requests to any web endpoints implemented in the backend.
- Access introspection and documentation lookup facilities in the LBusing Lisp evaluation.
- Create, manipulate, compile, load, and analyze files, again usingLisp evaluation.
Lisply is a lightweight protocol that specifies aminimal yet flexible set of HTTP interfaces, a standard set ofenvironment variables, and several optional capabilities to facilitate AI agentscontrolling your running Lisp system.
Architecture
The diagram below roughly captures how the components interact:
flowchart TB
User("User") <--> Claude("Claude Desktop")
User <-.-> Emacs("Emacs Text Editor (Optional)")
Claude <--> MCP("MCP Protocol")
MCP <--> Wrapper("Cyborg Whisperer (Node.js MCP Wrapper)")
Wrapper --> LisplyHttp("Lisply HTTP Server")
subgraph Backend ["Lisply Backend (container or host process)"]
subgraph LisplyExec["Lisply Executable"]
LisplyHttp
LisplySwank("Lisply SWANK Server (for Emacs connection)")
end
end
Emacs <-.-> LisplySwank
KB[("Lisply Knowledge Base")] <--> Wrapper
LisplyHttp --> Endpoints("RESTful Endpoints")
LisplyHttp --> LispEval("Lisp Evaluation")
style User fill:#ff9,stroke:#333,stroke-width:2px
style Claude fill:#f9f,stroke:#333,stroke-width:2px
style Emacs fill:#9ff,stroke:#333,stroke-width:2px,stroke-dasharray:5
style Wrapper fill:#bbf,stroke:#333,stroke-width:2px
style MCP fill:#bbf,stroke:#333,stroke-width:1px
style Backend fill:#bfb,stroke:#333,stroke-width:2px
style LisplyExec fill:#8f8,stroke:#333,stroke-width:2px
style LisplyHttp fill:#bfb,stroke:#333,stroke-width:1px
style LisplySwank fill:#bfb,stroke:#333,stroke-width:1px
style KB fill:#bfb,stroke:#333,stroke-width:1px
style Endpoints fill:#bfb,stroke:#333,stroke-width:1px
style LispEval fill:#bfb,stroke:#333,stroke-width:1px
The middleware handles:
- Translating Lisp evaluation requests between the MCP protocol andthe backend Lisply API
- Error handling and logging
Security Considerations
Because Cyborg Whisperer allows arbitrary Lisp code to be evaluated againsta running Lisp-based backend, there are certain risks in case the LLMwere to go "haywire." Therefore, best practices are:
Allow the wrapper to connect only to a containerized version of aLisply backend. If overriding default host/port, the wrapper willhappily connect to any live Lisply-compliant http port. Avoidallowing this to happen for any http ports being served by programsrunning directly on your host.
Make sure not to mount any non-expendable directories into thatcontainer (directory mounting is configured in your docker composesetup, not by this wrapper)
Consider taking steps to limit RAM and CPUusageof the container.
Code Modules/Files
- lib/config.js: Configuration loading and environment handling
- lib/logger.js: Logging functionality
- lib/server.js: HTTP server and MCP wrapper implementation
- lib/utils.js: Utility functions for response handling
- handlers/: Tool-specific request handlers
- initialize.js: Initialization handler
- toolsList.js: Tools list handler
- toolCall.js: Main tool call dispatcher
- httpRequest.js: HTTP request handler
- ping.js: Ping handler
- lispEval.js: Lisp evaluation handler
- skewedSearch.js: Document-corpus search handler (backendsthat advertise it, e.g. Readymax rooms)
- mcp-wrapper.js: <--- Main entry point <---
Detailed Installation
- Clone this repository:
git clone https://github.com/gornskew/cyborg-whisperer.git
- Install the required dependencies (optional, as the wrapper auto-installs dependencies):
cd cyborg-whisperer/scripts
npm install # optional - the script will attempt to do this also if needed
chmod +x mcp-wrapper.js # needed on some systems
- Test the script:
node mcp-wrapper.js --help
Advanced Configuration
Optional settings for advanced users, with defaults suitable for mostcases:
Command-Line Arguments
Options:
-H, --backend-host <host> Lisply backend host (default: 127.0.0.1)
--http-host-port <port> Backend HTTP port as published on this host; used when
backend host is localhost/loopback (default: 9081)
--http-port <port> Backend HTTP port inside the container network; used for
non-local backend hosts (default: 9080)
--swank-host-port <port> SWANK port on host system (documentation/diagnostics) (default: 4201)
--swank-port <port> SWANK port inside container (documentation/diagnostics) (default: 4200)
--log-file <path> Path to log file (default: /tmp/lisply-mcp-wrapper.log)
--debug Enable debug logging
--endpoint-prefix <prefix> Prefix for all endpoints (default: lisply)
--lisp-eval-endpoint <n> Endpoint name for Lisp evaluation (default: lisp-eval)
--http-request-endpoint <n> Endpoint name for HTTP requests (default: http-request)
--ping-endpoint <n> Endpoint name for ping (default: ping-lisp)
--server-name <name> MCP server name for tool prefixing (default: lisply-mcp)
--eval-timeout <ms> Timeout for Lisp evaluation in milliseconds (default: 30000)
--request-timeout-ms <ms> Timeout for backend HTTP requests in milliseconds (default: 10000)
-h, --help Display help for command
Environment Variables
The script also supports configuration via environment variables. Youcan specify variables with the "LISPLY_" prefix or with no prefix:
Note: It is important to keep straight the difference between hostports (listening on and reachable from the host system) and containerports (internal to the container, visible to the Lisply backendservice process):
| Environment Variable | Description | Default |
|---|---|---|
BACKEND_HOST or LISPLY_BACKEND_HOST |
Lisply backend host | 127.0.0.1 |
HTTP_HOST_PORT or LISPLY_HTTP_HOST_PORT |
Backend HTTP port as published on this host (loopback backends) | 9081 |
HTTP_PORT or LISPLY_HTTP_PORT |
Backend HTTP port inside the container network (non-local backends) | 9080 |
SWANK_HOST_PORT or LISPLY_SWANK_HOST_PORT |
SWANK port on host system (documentation/diagnostics) | 4201 |
SWANK_PORT or LISPLY_SWANK_PORT |
SWANK port inside container (documentation/diagnostics) | 4200 |
LOG_FILE or LISPLY_LOG_FILE |
Path to log file | /tmp/lisply-mcp-wrapper.log |
DEBUG_MODE or LISPLY_DEBUG_MODE |
Enable debug logging | false |
EVAL_TIMEOUT or LISPLY_EVAL_TIMEOUT |
Timeout for Lisp evaluation in ms | 30000 |
REQUEST_TIMEOUT_MS or LISPLY_REQUEST_TIMEOUT_MS |
Timeout for backend HTTP requests in ms | 10000 |
ENDPOINT_PREFIX or LISPLY_ENDPOINT_PREFIX |
Prefix for all endpoints | lisply |
LISP_EVAL_ENDPOINT or LISPLY_LISP_EVAL_ENDPOINT |
Endpoint name for Lisp evaluation | lisp-eval |
HTTP_REQUEST_ENDPOINT or LISPLY_HTTP_REQUEST_ENDPOINT |
Endpoint name for HTTP requests | http-request |
PING_ENDPOINT or LISPLY_PING_ENDPOINT |
Endpoint name for ping | ping-lisp |
SERVER_NAME or LISPLY_SERVER_NAME |
MCP server name for tool prefixing | lisply-mcp |
TRUST_AS_SANDBOX or LISPLY_TRUST_AS_SANDBOX |
Advertise backend as an explicitly trusted sandbox in tool metadata | true |
SANDBOX_NOTE or LISPLY_SANDBOX_NOTE |
Override the sandbox-note text shown in tool metadata | (built-in note) |
Container Lifecycle: Docker Compose, Not This Wrapper
Earlier versions of this wrapper could pull, start, and manage backendDocker containers itself (image selection, volume mounting, auto-start,existing-service detection). That entire subsystem has been removed.The wrapper is now a pure HTTP client: it connects to whatever Lisplybackend is already listening at the configured host and port, andreports a helpful error (with a compose hint) when nothing is there.
For a containerized backend stack (Gendl, Readymax, etc.), useBasilisk, the compose framework atgitlab.genworks.com:gornskew/basilisk (./basilisk up),which owns image selection, volume mounting, port publishing, and UIDmapping. For a non-containerized backend, start any Lisply-compliantserver yourself (e.g. the host-Emacs path described in readymaxdocs/HOST_EMACS_MCP.md) and point the wrapper at its host and port.
Communication
Two links are involved, and they are easy to conflate:
AI Agent ↔ wrapper: MCP protocol over standard input/output(the standard MCP stdio transport). This is JSON-RPC plumbingmanaged by your MCP client, unrelated to any backend REPL.
Wrapper ↔ backend: HTTP only. The wrapper POSTs to thebackend's Lisply HTTP endpoints and returns structured responses.
Characteristics of the HTTP backend link:
- Structured responses with separate result, stdout, and error fields
- Errors are trapped by the backend and returned as strings
- Response format:
{Result: <result>, Stdout: <output>, Error: <any error>}
Example response:
{"Result": "6", "Stdout": "This is a message to standard output"}
An earlier "stdio mode", which talked to a wrapper-startedcontainer's raw REPL (interactive debugger, incremental output), wasremoved along with container auto-starting. Equivalent capabilitiesmay return at the HTTP layer in the future (e.g. a restarts endpoint,streamed output) without re-coupling the wrapper to containerlifecycle.
Usage Examples
All the examples below can be tested on command line and used inclaude_desktop_config.json configuration (see the Quick Startconfiguration examples above).
Adding a Separate, Compatible Filesystem MCP Server
Below is a claude_desktop_config.json which sets up a filesystem mcpserver as well as our lisply-gendl server. (The filesystemserver gets its mount here; any mounts into a containerized Lisplybackend are configured in the compose setup, not by this wrapper.)
{
"mcpServers": {
"filesystem": {
"command": "wsl",
"args": [
"docker",
"run",
"-i",
"--rm",
"-u",
"1000:1000",
"--mount",
"type=bind,src=/home/user/projects,dst=/projects",
"mcp/filesystem",
"/projects"
]
},
"lisply-gendl": {
"command": "wsl",
"args": [
"node",
"/home/user/projects/cyborg-whisperer/scripts/mcp-wrapper.js",
"--server-name", "gendl"
]
}
},
"globalShortcut": ""
}
Tool Details for Claude
Lisp Evaluation Tool (<server>__lisp_eval)
The lisp_eval tool (prefixed with the server name, e.g., gendl__lisp_eval)allows Claude to evaluate Lisp code directly within the Lisply environmentwith these parameters:
code(required): The Lisp code to evaluatepackage(optional): The package to use for the evaluation
HTTP Request Tool (<server>__http_request)
The http_request tool (prefixed with the server name, e.g., gendl__http_request)enables the AI Agent to interact with any HTTP endpoint exposed by the Lisplybackend (the following example works with our default backend, which has abuilt-in /color-map http endpoint):
{
"path": "/color-map",
"method": "GET",
"headers": {
"Accept": "application/json"
}
}
Ping Tool (<server>__ping_lisp)
The ping_lisp tool (prefixed with the server name, e.g., gendl__ping_lisp)lets Claude confirm that the Lisply server is running:
<server>__ping_lisp()
Real-World Examples
Example 1: Creating a Parametric Box
In this example, Claude creates a parametric box in the Lisplyenvironment and then modifies its properties based on userrequirements.
User: "Can you create a box with width 10, height 5, and depth 3?Then show me how to change the height to 8."
Claude:
I'll create a parametric box using the lisp_eval tool, then show youhow to modify its height.
First, let's create the box with the specified dimensions:
(in-package :gdl-user)
;; First, create a box with the specified dimensions
(defparameter *my-box* (make-object 'box
:width 10
:height 5
:length 3))
;; Let's verify the dimensions
(list (theo *my-box* width)
(theo *my-box* height)
(theo *my-box* length))
Now, let's change the height from 5 to 8:
;; Change the height using setf with accessor
(theo *my-box* (set-slot! :height 8))
;; Alternatively, we could create a new box
(defparameter *new-box* (make-object 'box
:width 10
:height 8 ;; Updated height
:length 3))
;; Verify the new dimensions of original box
(list (theo *my-box* width)
(theo *my-box* height)
(theo *my-box* length))
This box can be visualized by creating a web interface or using abuilt-in gendl visualization tool such as geysr. See GendlDocumentation for details.
Troubleshooting
Common Issues and Solutions
Backend Not Running
If the wrapper reports it cannot reach the backend:
- For the containerized stack, make sure the compose services are up:
cd ~/projects/basilisk && ./basilisk up
- Check whether anything is listening on the expected port:
curl http://localhost:9081/lisply/ping-lisp
Connection Errors
If the LLM Agent / MCP Client cannot connect to the configured Lisplybackend:
- Check if the Lisply server is running (for the compose stack):
docker ps # the stack's containers should be listed and healthy
- Check the wrapper's log file:
tail -f /tmp/lisply-mcp-wrapper.log
- Check the Claude Desktop log file with Windows toolse.g. Notepad. This is typically in a location such as:
WSL/Linux:
/mnt/c/Users/<user>/AppData/Roaming/Claude/logs/mcp-server-lisply.log
Windows:
c:\Users\<user>\AppData\Roaming\Claude\logs\mcp-server-lisply.log
- Try curling to the Lisply HTTP server:
curl http://localhost:9081/lisply/ping-lisp
- Try connecting to the Lisply SWANK server (on default port 4201):
M-x slime-connect ;; from emacs
Note that setting up theReadymax configurationwill enable M-x slime-connect in your emacs.
Permission Issues
If you encounter file-ownership surprises in a directory mounted intoa containerized backend, remember mounts and UID mapping areconfigured in the compose setup, not by this wrapper. Check themounted directory permissions:
ls -l /path/to/mounted/directory
Diagnostic Commands
Use these commands to diagnose general issues:
- Check the middleware logs:
tail -f /tmp/lisply-mcp-wrapper.log
- Check backend container logs (for the compose stack):
cd ~/projects/basilisk && ./basilisk logs
- Check Lisply service status:
curl http://localhost:9081/lisply/ping-lisp
- Verify Docker environment:
docker system info
License
This software is licensed under the GNU Affero General Public Licensev3.0 (AGPL-3.0), the same license used by Gendl.
License Implications
Simply using this MCP server to interact with a Lisply backend andobtain outputs does not trigger the requirements of the AGPL, e.g. youcan use this wrapper to interact with Gendl without being required toshare your code.
However, if you modify or extend this wrapper, or a license-compatibleLisply backend such as Gendl, and wish to distribute and/or host aservice based on that result (commercial or not), then the AGPL wouldrequire you to share your modifications with the downstream recipientsor users.
For applications that need to keep their source code closed, Genworkshas begun offering an "escape clause" from AGPL restrictions for a 5%self-reported quarterly revenue royalty. More information and apayment gateway are available atroyalties.genworks.com.
The full text of the license can be found in the COPYING.txt file inthis directory.