About
Minimal MCP JSON-RPC server helpers for CircuitPython
README
Minimal MCP JSON-RPC server helpers for CircuitPython projects using adafruit_httpserver.
This library lets a CircuitPython board run a real MCP server directly on the device. The host computer is only used for deployment, serial logs, and testing.
Implemented HTTP endpoints:
GET /: JSON statusPOST /mcp: MCP JSON-RPC over HTTPGET /mcp: method-not-allowed explanationDELETE /mcp: method-not-allowed explanationOPTIONS /mcp: permissive CORSGET /sseandPOST /messages: legacy SSE compatibility when the installedadafruit_httpserverhasSSEResponse
Status
This repository is ready to clone and install from a local file path with circup.
The bundle command:
circup install circuitpython_mcp_server
will work after this library is accepted into a CircuitPython bundle that circup knows about.
Install From This GitHub Repo
Clone the repo:
git clone https://github.com/speccy88/circuitpython-mcp-server.git
cd circuitpython-mcp-server
Install the core dependency:
circup --path /Volumes/CIRCUITPY install adafruit_httpserver
Install this library from the local checkout:
circup --path /Volumes/CIRCUITPY install ./circuitpython_mcp_server.py
On Linux, replace /Volumes/CIRCUITPY with /media/$USER/CIRCUITPY or /run/media/$USER/CIRCUITPY. On Windows, use the CIRCUITPY drive letter.
Manual Install
Copy the module to the board:
cp circuitpython_mcp_server.py /Volumes/CIRCUITPY/lib/
Then install adafruit_httpserver with circup.
Basic Usage
Create an adafruit_httpserver.Server, pass it to MCPServer, register tool callbacks, then call poll() in the main loop.
from adafruit_httpserver import Server
from circuitpython_mcp_server import MCPServer, schema_object, tool_result
http_server = Server(pool, debug=False)
mcp = MCPServer(
http_server,
name="example-board",
title="Example Board MCP Server",
version="0.1.0",
instructions="Controls hardware on this CircuitPython board.",
)
def hello(arguments):
return tool_result("Hello from CircuitPython.", {"ok": True, "action": "hello"})
mcp.add_tool("hello", "Return a greeting.", schema_object(), hello, title="Hello")
mcp.start(ip_address, port=5000)
while True:
mcp.poll()
Tool callbacks receive the JSON object from params.arguments. Return a valid MCP tool result, usually built with tool_result(). Raise ValueError or ToolError for validation failures; the library maps them to JSON-RPC -32602.
Hermes Agent Setup
This library is installed on the CircuitPython board, not in Hermes. Hermes connects to the board as an HTTP MCP server after the board prints its MCP URL on serial.
Reference: Use MCP with Hermes.
1. Confirm Hermes MCP support
If Hermes was installed with the standard installer, MCP support is already included. If Hermes was installed without extras, add MCP support:
cd ~/.hermes/hermes-agent
uv pip install -e ".[mcp]"
2. Start the Fruit Jam MCP server
Install and run the Fruit Jam NeoPixel demo first. Watch serial at 115200 baud until the board prints:
MCP streamable HTTP URL: http://FRUITJAM_IP:5000/mcp
MCP legacy SSE URL: http://FRUITJAM_IP:5000/sse
Verify the URL from the same machine that runs Hermes:
curl http://FRUITJAM_IP:5000/
python3 tools/test_fruitjam_neopixels.py --url http://FRUITJAM_IP:5000/mcp
Do not continue until this works. Hermes cannot connect if the board IP is unreachable from the Hermes host.
3. Add the board to Hermes config
Edit Hermes config:
hermes config edit
Add this under mcp_servers in ~/.hermes/config.yaml, replacing FRUITJAM_IP with the IP printed by the board:
mcp_servers:
fruitjam_neopixels:
url: "http://FRUITJAM_IP:5000/mcp"
connect_timeout: 20
timeout: 30
tools:
include:
- neopixels_fill
- neopixel_set
- neopixels_clear
- neopixels_brightness
- neopixels_status
resources: false
prompts: false
Use fruitjam_neopixels as the server name because Hermes prefixes MCP tools as mcp_<server>_<tool>. With this config, expected registered tool names include:
mcp_fruitjam_neopixels_neopixels_fillmcp_fruitjam_neopixels_neopixel_setmcp_fruitjam_neopixels_neopixels_clearmcp_fruitjam_neopixels_neopixels_brightnessmcp_fruitjam_neopixels_neopixels_status
The include list uses the original MCP tool names from the board, not the mcp_... prefixed Hermes names.
If your Hermes build expects legacy SSE for this server, use the serial fallback URL instead:
mcp_servers:
fruitjam_neopixels:
url: "http://FRUITJAM_IP:5000/sse"
connect_timeout: 20
timeout: 30
tools:
include:
- neopixels_fill
- neopixel_set
- neopixels_clear
- neopixels_brightness
- neopixels_status
resources: false
prompts: false
4. Start or reload Hermes
Start Hermes:
hermes chat
If Hermes is already running, reload MCP servers inside the chat:
/reload-mcp
5. Verify Hermes loaded the tools
Ask Hermes:
Tell me which MCP-backed tools are available right now.
You should see the mcp_fruitjam_neopixels_... tools listed. If no tools appear, check:
- the board still responds at
http://FRUITJAM_IP:5000/ - the configured URL is
/mcp, or/sseif needed ~/.hermes/config.yamlindentation is valid YAML- the server entry is not set to
enabled: false - the tool names in
tools.includeare the unprefixed board tool names
6. Run a first Hermes command
Use direct prompts first:
Use the Fruit Jam NeoPixel MCP server to set all five NeoPixels purple.
Then verify status:
Use the Fruit Jam NeoPixel MCP server to report the current pixel state and brightness.
Leave the board off when done:
Use the Fruit Jam NeoPixel MCP server to clear all NeoPixels.
7. Keep the tool surface narrow
This demo only exposes five hardware tools. Keep the tools.include allowlist anyway. Hermes supports per-server filtering, and using an allowlist is the safer default when an MCP server controls physical hardware.
Fruit Jam NeoPixel Demo
The demo in examples/fruitjam_neopixel_mcp_demo.py exposes the five onboard Adafruit Fruit Jam NeoPixels as MCP tools.
Tools:
neopixels_fillneopixel_setneopixels_clearneopixels_brightnessneopixels_status
Safer Installer
The recommended demo path is the installer script. It detects the CIRCUITPY drive, copies this library into CIRCUITPY/lib, backs up any existing code.py, preserves settings.toml, and optionally installs CircuitPython dependencies with circup.
python3 tools/install_fruitjam_demo.py
Use an explicit drive if auto-detection is not correct:
python3 tools/install_fruitjam_demo.py --drive /Volumes/CIRCUITPY
If you already installed the CircuitPython dependencies:
python3 tools/install_fruitjam_demo.py --skip-deps
The installer rejects paths that escape the board root. It should not be used as a general local filesystem writer.
Manual Demo Install
Install demo dependencies:
circup --path /Volumes/CIRCUITPY install \
adafruit_httpserver \
adafruit_connection_manager \
adafruit_esp32spi \
adafruit_bus_device \
neopixel \
adafruit_ticks \
adafruit_pixelbuf
Install this MCP library:
circup --path /Volumes/CIRCUITPY install ./circuitpython_mcp_server.py
Create /Volumes/CIRCUITPY/settings.toml:
CIRCUITPY_WIFI_SSID = "your-wifi-ssid"
CIRCUITPY_WIFI_PASSWORD = "your-wifi-password"
Back up any existing board code, then copy the demo:
if [ -f /Volumes/CIRCUITPY/code.py ]; then
cp /Volumes/CIRCUITPY/code.py /Volumes/CIRCUITPY/code.py.bak.$(date +%Y%m%d-%H%M%S)
fi
cp examples/fruitjam_neopixel_mcp_demo.py /Volumes/CIRCUITPY/code.py
sync
Open serial at 115200 baud and look for:
MCP streamable HTTP URL: http://FRUITJAM_IP:5000/mcp
MCP legacy SSE URL: http://FRUITJAM_IP:5000/sse
Smoke test from your computer:
python3 tools/test_fruitjam_neopixels.py --url http://FRUITJAM_IP:5000/mcp
Development
Install development dependencies in a local environment:
python3 -m pip install -e ".[dev]"
Run tests:
pytest
Run lint:
ruff check .
Fruit Jam AirLift Firmware
If the Fruit Jam demo repeatedly prints:
ESP32 timed out on SPI select
the ESP32-C6 AirLift/NINA firmware may be missing or stale. Use the latest non-debug Fruit Jam C6 firmware from:
https://github.com/adafruit/nina-fw/releases/latest
The validated firmware asset for the hardware demo was:
NINA_ADAFRUIT-fruitjam_c6-3.3.0.bin
API Reference
Main imports:
from circuitpython_mcp_server import MCPServer
from circuitpython_mcp_server import ToolError, MCPError
from circuitpython_mcp_server import schema_int, schema_number, schema_object
from circuitpython_mcp_server import tool_result
MCPServer handles:
- MCP
initialize notifications/initializedpingtools/listtools/call- empty
resources/list - empty
prompts/list logging/setLevel- JSON-RPC error mapping
- optional SSE compatibility
License
MIT.
Audit Notes
This repo is intentionally an on-board MCP server library. It is not a host-side MCP server for controlling board files or serial ports. The host-side project at neusse/Codex-Circuitpython-MCP was reviewed for operational patterns; the useful pieces adopted here are path-confined board writes, safer demo installation, clearer platform install documentation, and test coverage.
Installing Circuitpython
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/speccy88/circuitpython-mcp-serverFAQ
Is Circuitpython MCP free?
Yes, Circuitpython MCP is free — one-click install via Unyly at no cost.
Does Circuitpython need an API key?
No, Circuitpython runs without API keys or environment variables.
Is Circuitpython hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install Circuitpython in Claude Desktop, Claude Code or Cursor?
Open Circuitpython on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectCompare Circuitpython with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
