MCP server for controlling Arduino GPIO over Firmata (StandardFirmata).
Project description
firmata-client-mcp
An MCP (Model Context Protocol) server that lets an LLM control an Arduino's GPIO over the Firmata protocol.
Built on FastMCP and the
firmata-client library. It exposes digital/analog
I/O, PWM, and basic servo control as MCP tools so an agent can read sensors and drive actuators
on a connected board.
⚠️ This server lets an AI agent drive physical hardware. Please read the Safety section before connecting a board.
Features
- Discover serial ports and connect to a board running StandardFirmata
- Query board capabilities (firmware, pin modes, analog mapping)
- Digital read / write
- Analog (ADC) read with sample averaging
- PWM (analog) write
- Servo control with a settable "origin" reference point
- Structured JSON responses and actionable error messages
Requirements
- Python 3.13+ (required by the
firmata-clientdependency) - An Arduino (or compatible board) flashed with StandardFirmata
(
File → Examples → Firmata → StandardFirmatain the Arduino IDE) - Python dependencies:
mcp[cli]firmata-clientpyserial(installed withfirmata-client)pydantic(installed withmcp)
Installation
Using uv (recommended):
uv pip install firmata-client-mcp
Or via pip:
pip install firmata-client-mcp
Or from source:
git clone https://github.com/NaoNaoMe/firmata-client-mcp.git
cd firmata-client-mcp
uv sync
Configuration
Claude Desktop Config
Add the server to your claude_desktop_config.json:
{
"mcpServers": {
"firmata-client-mcp": {
"command": "uvx",
"args": [
"firmata-client-mcp"
]
}
}
}
A typical first interaction:
arduino_list_ports- find your board's serial portarduino_connect- connect using that portarduino_get_board_info- inspect supported modes per pinarduino_set_pin_mode- configure a pin (e.g. pin 13 ->OUTPUT)arduino_digital_write- turn it on
Tools
| Tool | Description | Read-only |
|---|---|---|
arduino_list_ports |
List available serial ports | ✅ |
arduino_connect |
Connect to a board on a given port | |
arduino_disconnect |
Disconnect from the current board | |
arduino_get_board_info |
Firmware info and per-pin capabilities | ✅ |
arduino_set_pin_mode |
Set a pin to INPUT/OUTPUT/ANALOG/PWM/SERVO | |
arduino_digital_write |
Set a digital pin HIGH/LOW | |
arduino_digital_read |
Read a digital pin (majority of 5 samples) | ✅ |
arduino_analog_write |
Write a PWM value (0-255) | |
arduino_analog_read |
Read an analog channel (averaged) | ✅ |
arduino_set_servo_origin |
Register the current angle as a reference | |
arduino_servo_move |
Move a servo to an angle offset from origin |
A note on pin vs. analog channel
arduino_analog_read takes an analog channel number (0 = A0, 1 = A1, …),
but arduino_set_pin_mode expects the digital pin number. Use the
analog_mapping returned by arduino_get_board_info to translate between them.
A note on servo movement
arduino_servo_move moves the servo to origin + value degrees (clamped to 0-180),
where origin is whatever you registered with arduino_set_servo_origin
(default 0). The move is relative to the origin, not cumulative - calling it
twice with the same value results in the same physical position.
Safety
This server gives an autonomous agent direct control of electrical outputs and moving parts. Before use:
- Supervise it. Do not run it unattended while a board with actuators is powered.
- Mind your wiring. An agent can set any configured pin HIGH or drive PWM/servos; verify current limits, and mechanical end-stops yourself.
- Setting SERVO mode does not define a position. The physical angle right after a mode change is undefined and hardware-dependent.
- The author(s) accept no liability for damage to hardware or surroundings. Use at your own risk.
Limitations
- Single board / single client at a time (module-level connection state, no locking - intended for local stdio use).
- Pin numbers are accepted in the range 0-69 (covers boards up to Arduino Mega); the actual valid range depends on your board.
- Reads block briefly (~100 ms) while sampling.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file firmata_client_mcp-0.1.0.tar.gz.
File metadata
- Download URL: firmata_client_mcp-0.1.0.tar.gz
- Upload date:
- Size: 38.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
166effc66da1b7f7b110e64c03e2d0ce672e4c7b1b41c52fd9a0f20995d39026
|
|
| MD5 |
3ae16363e63edbb6313ca639ad7a7e0c
|
|
| BLAKE2b-256 |
7949451f2de0900ae82cf49f06b1950a8731b991da301b32556fb09cde4e4acd
|
Provenance
The following attestation bundles were made for firmata_client_mcp-0.1.0.tar.gz:
Publisher:
python-publish.yml on NaoNaoMe/firmata-client-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
firmata_client_mcp-0.1.0.tar.gz -
Subject digest:
166effc66da1b7f7b110e64c03e2d0ce672e4c7b1b41c52fd9a0f20995d39026 - Sigstore transparency entry: 1629595306
- Sigstore integration time:
-
Permalink:
NaoNaoMe/firmata-client-mcp@ca61bb7c1a66077fb591ace80873e29156342ae8 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/NaoNaoMe
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@ca61bb7c1a66077fb591ace80873e29156342ae8 -
Trigger Event:
release
-
Statement type:
File details
Details for the file firmata_client_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: firmata_client_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
531efa13b966a6e863dc0ba6ae286620c880f8c72f021ba751eff98792e84cca
|
|
| MD5 |
d7d94142f9993c81318606f6cf1b06bd
|
|
| BLAKE2b-256 |
9b7d3cbc70a57ff0c729c48836f555ef4cffee76ad57f9b5de57c0ee0210efa9
|
Provenance
The following attestation bundles were made for firmata_client_mcp-0.1.0-py3-none-any.whl:
Publisher:
python-publish.yml on NaoNaoMe/firmata-client-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
firmata_client_mcp-0.1.0-py3-none-any.whl -
Subject digest:
531efa13b966a6e863dc0ba6ae286620c880f8c72f021ba751eff98792e84cca - Sigstore transparency entry: 1629595341
- Sigstore integration time:
-
Permalink:
NaoNaoMe/firmata-client-mcp@ca61bb7c1a66077fb591ace80873e29156342ae8 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/NaoNaoMe
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@ca61bb7c1a66077fb591ace80873e29156342ae8 -
Trigger Event:
release
-
Statement type: