What it is
MCP (Model Context Protocol) is the open standard that lets an AI assistant such as Claude use
tools in the real world. mcp-bridge is an MCP server with fourteen tools for your ctrlPi
agents, so you can simply ask: "Which agents are online?", "Turn on the porch light",
"Pulse the garage relay for 2 seconds", "What is the greenhouse temperature?" or
"Show me the last log lines from the shed Pico", and the assistant does it.
It works with Claude Desktop, Claude Code and any other MCP client, over STDIO or Streamable HTTP. The server holds no GPIO logic: every tool names an agent and passes the call to that agent's REST endpoints, forwarding the agent's own API key.
Quick start: Claude Desktop over STDIO
Nothing to install and nothing to start: Claude Desktop launches the server itself, and
npx fetches it on demand. Add this to claude_desktop_config.json
(Settings → Developer → Edit Config), then restart Claude Desktop:
{
"mcpServers": {
"ctrlpi": {
"command": "npx",
"args": ["-y",
"@ctrlpi/mcp-bridge@latest", "--rotate-keys"]
}
}
}STDIO needs no key: the server is a child process of Claude Desktop, reachable by nothing else. The first launch sweeps the LAN and writes the agent list before answering.
In Claude Code the same thing is one command:
claude mcp add ctrlpi -- npx -y @ctrlpi/mcp-bridge@latest --rotate-keys
/hello, tries the default key, and writes them out for you. If any of the other bridges already set this file, it will use and update it. You do not hand-write an agent list.The tools
| Tool | What it does |
|---|---|
mcp_agents_list | List configured agents with live online status and type (pi/pico), pass rescan to sweep the LAN again |
mcp_gpio_read | Read one pin or all pins of an agent |
mcp_gpio_write | Write 0/1/on/off/toggle, with optional pulse duration |
mcp_gpio_scan | Hardware scan of every pin, with physical levels |
mcp_gpio_config | Configure a pin: type, name, pullup, init, max, reversed, watched |
mcp_gpio_watched | List GPIO pins currently actively watched for interrupts and webhooks |
mcp_sensor_config | Configure or remove a named sensor script and arguments |
mcp_sensor_read | Read a single named sensor or all configured sensors |
mcp_config_read | Read an agent's identity and full config |
mcp_config_write | Update agent config fields |
mcp_config_load | Replace an agent's whole config |
mcp_agent_restart | Restart the agent process (or reboot the device) |
mcp_agent_upgrade | Upgrade the agent in place to the latest release |
mcp_agent_logs | Fetch the agent's recent log lines |
Install & run: over the network (Streamable HTTP)
MCP goes on the pipe or over HTTP, never both. Use HTTP when the machine that can reach your agents is not the one running Claude - a Pi on the same LAN, say. Install it there, in one directory alongside any other ctrlPi bridges, and start it in HTTP mode:
npm install @ctrlpi/mcp-bridge
cd node_modules/@ctrlpi/mcp-bridge
npm start -- --http # Streamable HTTP at /mcp on :8315
Every config lives in ~/.ctrlpi/, outside node_modules, so a
reinstall or an upgrade cannot reach it.
To keep it running after you close the terminal, and across reboots, start it under PM2 from that same folder:
npm install -g pm2 # if you don't already have it pm2 startup # no need to repeat if already done pm2 start server.js --name mcp-bridge -- --http pm2 save
The key that gates HTTP mode starts as your-mcp-key; change
mcp.api_key in ~/.ctrlpi/mcp-config.json before you expose the
port. Claude Desktop does not speak HTTP to an MCP server itself, so it reaches a
running bridge through the mcp-remote
shim, which is a STDIO server that forwards to your URL:
{
"mcpServers": {
"ctrlpi": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://192.168.1.50:8315/mcp",
"--header", "Api-Key:your-mcp-key"]
}
}
}Claude Code speaks Streamable HTTP directly, so it needs no shim:
claude mcp add ctrlpi --transport http http://192.168.1.50:8315/mcp --header "Api-Key: your-mcp-key"Pick one transport per client, not both, and restart the client after saving.
Configuration
Two files in ~/.ctrlpi/, and on a normal setup you hand-write neither. The
agent list is what discovery produces; the other file only exists once you want a key or
a filter.
The shared agent list - ~/.ctrlpi/agents-config.json. Every ctrlPi project
reads and updates this same file, so an agent discovered, moved or re-keyed anywhere is
correct everywhere:
{
"agents": [
{ "name": "garage-pi", "ip": "192.168.1.50", "port": 8314, "api_key": "..." }
]
}
This server's own file - ~/.ctrlpi/mcp-config.json. Both keys are
optional:
{
"mcp": { "api_key": "your-mcp-key" }, // gates --http only; STDIO needs no key
"agents": { "whitelist": [], "blacklist": [] }
}
agents means two different things on purpose: in the shared file it is the
list, here it is a filter over it, matched on agent name, exactly. A whitelist
alone keeps only those, a blacklist alone drops those, both means whitelist minus
blacklist, and neither means every agent in the shared file. The filter is applied after
discovery, never before - the sweep saves the full list, keeping it fresh for every
project, and only then narrows it for this server, so a whitelist here can never delete
an agent another project uses.
Both files are re-read on every tool call, so an edit applies without a restart.
In the family
Drives any mix of pi-gpio-api, pico-gpio-api, and esp32-gpio-api agents. Put the same pins in Apple Home with homebridge-ctrlpi, or test endpoints live in ctrlpi-lab.
To test endpoints, watch webhook events and tail logs in the browser, open ctrlpi-lab, and to find and drive agents from a terminal, use ctrlpi-cli.
To get an agent onto a board over USB, use pico-bay. It can install MicroPython,
manage the board's files and run code on a Pico and an ESP32. pi-bay, for a Raspberry Pi
over the network, is coming soon.
That's deliberate: MCP clients invoke the server, never the other way round, so no webhook landing here could wake the AI as in the other bridges.