Skip to main content
Glama

bus-mcp

An MCP server that gives an AI agent (Claude Desktop, Claude Code, or any MCP-compatible client) direct access to a Raspberry Pi's peripheral buses:

  • CAN / CAN-FD — SocketCAN (PiCAN, Waveshare 2-CH CAN HAT, on-board CAN on CM5)

  • RS485 / UART — pyserial (/dev/ttyUSB*, /dev/ttyAMA*, /dev/ttyS*)

  • I2C — smbus2 (/dev/i2c-1, /dev/i2c-3)

  • SPI — spidev (/dev/spidev0.0, etc.)

  • GPIO — lgpio (Pi 4 / Pi 5 / CM4 / CM5)

The agent gets a small, opinionated tool set — bus_list, can_send, i2c_read, gpio_write, … — plus per-bus *_configure tools so it can retune bitrate / baud / SPI mode mid-session. Point Claude at a Pi and it can talk to a sensor, dump CAN traffic, or wiggle a pin without you writing a Python script first.

bus-mcp architecture

Quick install on a Pi

git clone https://github.com/Pan-Robotics/bus-mcp.git
cd bus-mcp
./packaging/install.sh --allow-write

That's it. The installer:

  • creates ~/.local/share/bus-mcp/.venv with --system-site-packages so apt-packaged lgpio / pyserial / smbus2 / spidev are reused (no SWIG rebuild)

  • installs mcp + python-can + bus-mcp itself

  • adds you to spi / i2c / gpio / dialout groups

  • installs /etc/systemd/system/bus-mcp.service and enables it

  • prints the MCP endpoint URL + the claude mcp add line for your host

It does not touch /boot/firmware/config.txt (CAN HAT overlays vary by HAT variant), pick a CAN bitrate, or open firewall ports.

Re-running is idempotent. To remove everything: ./packaging/install.sh --uninstall.

Related MCP server: wrapmcp

Manual install (PyPI, no systemd)

On a Pi:

pip install "bus-mcp[pi-all]"

On a dev laptop (no hardware) — for editing + tests:

pip install bus-mcp

The bus libraries (python-can, pyserial, smbus2, spidev, lgpio) load lazily. Missing libs only error when you call a tool that needs them.

Run

bus-mcp list                            # show discovered buses
bus-mcp serve                           # stdio (Claude Desktop / Code)
bus-mcp serve --allow-write             # writes on every bus
bus-mcp serve --allow-write=can,serial  # writes only on CAN + serial

# HTTP / streamable-http transport (localhost only by default)
bus-mcp serve --transport http
bus-mcp serve --transport http --port 7820 --allow-write
bus-mcp serve --transport http --host 0.0.0.0 --port 7820  # LAN-reachable

The HTTP endpoint is at http://<host>:<port>/mcp and speaks the MCP streamable-HTTP protocol (POST JSON-RPC, response as a single SSE message). The default bind is 127.0.0.1 so the server is invisible outside the Pi until you opt in with --host 0.0.0.0 (and an SSH tunnel / firewall rule to fence access).

Wire into Claude Desktop (stdio)

Edit ~/.config/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "bus-mcp": {
      "command": "bus-mcp",
      "args": ["serve", "--allow-write"]
    }
  }
}

Wire into Claude Code

stdio (Claude Code on the same Pi):

claude mcp add bus-mcp -- bus-mcp serve --allow-write

HTTP (Pi reachable on pi.local:7820, or substitute your hostname / IP):

claude mcp add --transport http bus-mcp http://pi.local:7820/mcp

The installer prints the exact URL for your host at the end of its run.

Tools the agent gets

Tool

Purpose

bus_list

Enumerate discovered buses

bus_status

Inspect descriptor + stored config (no driver open)

bus_close

Release a bus the agent opened earlier

write_permissions

Show which bus kinds writes are enabled on

can_configure

Set bitrate / fd / data_bitrate / restart_ms

can_send

Transmit a CAN / CAN-FD frame

can_receive

Drain frames with an optional kernel filter

serial_configure

Set baudrate / bytesize / parity / stopbits / flow

serial_send

Write bytes to a UART / RS485 port

serial_receive

Read bytes from a UART / RS485 port

i2c_read

Read from a device register

i2c_write

Write to a device register

i2c_scan

i2cdetect-equivalent address probe

spi_configure

Set max_speed_hz / mode / bits_per_word / lsb_first

spi_xfer

Full-duplex transfer (optional per-call speed)

gpio_configure

Set direction / pull / active-low / debounce

gpio_read

Read a pin

gpio_write

Drive a pin

*_configure tools take all fields as optional kwargs — pass only what you want to change. The bus is closed immediately so the next call reopens it with the new config. Blocking receive tools (can_receive, serial_receive, i2c_scan) run on a worker thread via asyncio.to_thread, so a single client can hold a long receive open while firing other tool calls in parallel.

Safety

Default is read-only on every bus. Write tools refuse cleanly with a message that names the right --allow-write flag. Refusals are returned as MCP tool errors so the agent can surface the state instead of crashing.

There is no protocol-level checking past the write gate: if you enable writes and the agent transmits a destructive CAN frame, your ECU sees it. Use this on bench setups, not on a live vehicle without a hardware kill switch.

Verifying on a Pi — Waveshare 2-CH CAN HAT loopback

The Waveshare 2-CH CAN HAT gives you two MCP2515 channels exposed as can0 + can1. With both channels wired into each other (CAN_H ↔ CAN_H, CAN_L ↔ CAN_L, plus the on-board 120 Ω termination jumpers enabled on both sides), frames sent on one channel land on the other — a perfect end-to-end test.

# 1. Enable SPI + the overlays (one-time).
#    These values are for the ORIGINAL 2-CH CAN HAT (16 MHz crystals,
#    IRQs on GPIO 23 + 25). The HAT+ variant uses different pins —
#    check your HAT before pasting.
sudo raspi-config nonint do_spi 0
sudo tee -a /boot/firmware/config.txt <<'EOF'
dtoverlay=mcp2515-can0,oscillator=16000000,interrupt=23
dtoverlay=mcp2515-can1,oscillator=16000000,interrupt=25
EOF
sudo reboot

# 2. After reboot — bring both interfaces up.
sudo ip link set can0 up type can bitrate 500000
sudo ip link set can1 up type can bitrate 500000

# 3. Install bus-mcp and start it.
./packaging/install.sh --allow-write    # or: bus-mcp serve --transport http --allow-write &

# 4. From an agent (or curl directly), exercise the loopback:
#    - on can0: can_send {arbitration_id=0x123, data_hex="deadbeef"}
#    - on can1: can_receive {timeout_s=1, count=1}
#    expect: one frame with arbitration_id=0x123, data="deadbeef"

A raw curl smoke-test (initialize the MCP session) — substitute your Pi's hostname / IP for <HOST>:

curl -i -X POST -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.0.1"}}}' \
  http://<HOST>:7820/mcp

If the controllers settle into ERROR-PASSIVE instead of ERROR-ACTIVE (ip -details link show can0), the on-board termination jumpers probably aren't enabled. Either turn them on or drop the bitrate to something the bus can sustain (sudo ip link set can0 down; sudo ip link set can0 type can bitrate 250000; sudo ip link set can0 up).

Architecture

See docs/architecture.png for the full layered view, or re-render after editing:

python docs/render_arch.py    # writes docs/architecture.png

The render script (docs/render_arch.py) uses pure-PIL — no dot / mmdc / browser engines — and auto-sizes each layer band from its tallest box, so adding tools or drivers doesn't require manual layout tuning.

Logs + service control (systemd install only)

sudo systemctl status bus-mcp           # is it up?
sudo systemctl restart bus-mcp          # bounce it
sudo journalctl -u bus-mcp -f           # tail logs
sudo journalctl -u bus-mcp --since=-1h  # past hour

Roadmap

  • mDNS advertise so a roaming agent on the LAN can find the Pi

  • Auth on the HTTP endpoint (shared secret header)

  • Filter expressions on can_receive beyond single-id+mask

  • PyPI publish (today: install from source)

License

MIT — see LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    Not graded
    maintenance
    An MCP server that bridges the physical world and AI models by enabling natural language control of IoT hardware via the MQTT protocol. It supports real-time device monitoring, command publishing, and response handling for seamless integration between AI clients and physical devices.
    3
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Universal MCP server that wraps any CLI tool, enabling AI assistants to run commands via natural language.
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    MCP server that gives AI agents direct access to IoT devices via the jettyd platform, enabling control and monitoring of ESP32 devices through natural language.
    5
    35 npm
    MIT