MCP server for controlling Sphero RVR with Claude AI - featuring direct serial protocol for low-latency control
Project description
Sphero RVR MCP Server
An MCP (Model Context Protocol) server that enables AI assistants to control a Sphero RVR robot. Run this on a Raspberry Pi connected to your RVR, and use any MCP-compatible client to drive, control LEDs, read sensors, and more.
Features
Core Capabilities
- Full RVR Control: Movement, LEDs, sensors, battery monitoring, IR communication
- Distance-Based Movement: Drive forward/backward by meters, pivot by degrees
- Safety System: Configurable speed limits, auto-stop timeout, emergency stop
- Sensor Streaming: Background streaming with cached data access
- Natural Language Control: Let AI drive your robot with conversational commands
- Client Agnostic: Works with any MCP-compatible client
Low-Latency Architecture (v0.2.1+)
- Direct Serial Protocol: Bypasses SDK for sub-millisecond command latency
- Distance Control:
drive_forward(0.5)moves exactly 0.5 meters - Angle Control:
pivot(90)rotates exactly 90 degrees - Command Queue: Priority-based async queue eliminates race conditions
- Atomic State Management: Thread-safe state with validated transitions
- Structured Logging: JSON or console format for easy debugging
Compatible MCP Clients
- Claude Code (CLI)
- Claude Desktop
- Cursor
- Zed
- Continue.dev
- Any custom client using the MCP SDK
Requirements
- Raspberry Pi 3 or newer (connected to Sphero RVR via serial)
- Python 3.10+
- Sphero RVR with serial connection to Pi
Installation
Install from PyPI (recommended)
pip install sphero-rvr-mcp
Install from source
git clone https://github.com/jsperson/sphero_rvr_mcp.git
cd sphero_rvr_mcp
pip install -e .
Verify Installation
Run the pre-flight check to verify everything is set up correctly:
sphero-rvr-mcp --check
This will verify:
- Python version (requires 3.10+)
- FastMCP is installed
- Serial port exists and is accessible
- Current configuration settings
Configure Your MCP Client
The server runs via stdio. Configure your MCP client with:
- Command:
python -m sphero_rvr_mcp
Claude Code
claude mcp add sphero-rvr -c "python -m sphero_rvr_mcp"
Or edit ~/.claude.json:
{
"mcpServers": {
"sphero-rvr": {
"type": "stdio",
"command": "python",
"args": ["-m", "sphero_rvr_mcp"]
}
}
}
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"sphero-rvr": {
"command": "python",
"args": ["-m", "sphero_rvr_mcp"]
}
}
}
Other Clients
Refer to your client's MCP configuration documentation. The server uses stdio transport.
Configuration
Environment variables (optional):
| Variable | Default | Description |
|---|---|---|
| Connection | ||
RVR_SERIAL_PORT |
/dev/ttyAMA0 |
Serial port for RVR |
RVR_BAUD_RATE |
115200 |
Serial baud rate |
| Safety | ||
RVR_MAX_SPEED_PERCENT |
50.0 |
Default speed limit (0-100) |
RVR_COMMAND_TIMEOUT |
5.0 |
Auto-stop timeout (seconds, 0=disabled) |
| Performance | ||
RVR_COMMAND_QUEUE_SIZE |
100 |
Max queued commands |
| Observability | ||
RVR_LOG_LEVEL |
INFO |
Log level (DEBUG, INFO, WARNING, ERROR) |
RVR_LOG_FORMAT |
json |
Log format (json, console) |
Usage
Example Commands
Once your MCP client is connected to the server:
You: Connect to the RVR
You: Drive forward 6 inches
You: Pivot 90 degrees to the right
You: Drive forward 1 meter
You: Set all LEDs to blue
You: What's the battery level?
You: Which direction is the rover facing?
# Returns heading in degrees and cardinal direction (N, NE, E, SE, S, SW, W, NW)
You: Turn left 45 degrees and drive backward 0.5 meters
You: Emergency stop!
Available Tools
Connection (3 tools)
| Tool | Description |
|---|---|
connect |
Connect to RVR and wake it up |
disconnect |
Safely disconnect |
get_connection_status |
Get connection state, uptime, firmware |
Movement (11 tools)
| Tool | Description |
|---|---|
drive_forward |
Drive forward by distance in meters |
drive_backward |
Drive backward by distance in meters |
pivot |
Turn in place by degrees (positive=right, negative=left) |
drive_with_heading |
Drive at speed toward heading (0-359) |
drive_tank |
Tank drive with left/right velocities (m/s) |
drive_rc |
RC-style with linear + yaw velocity |
stop |
Normal stop |
emergency_stop |
Immediate stop, blocks movement |
clear_emergency_stop |
Allow movement after e-stop |
reset_yaw |
Set current heading as 0 |
reset_locator |
Set current position as origin |
LEDs (3 tools)
| Tool | Description |
|---|---|
set_all_leds |
Set all LEDs to RGB color |
set_led |
Set specific LED group to RGB |
turn_leds_off |
Turn off all LEDs |
LED groups: headlight_left, headlight_right, brakelight_left, brakelight_right, status_indication_left, status_indication_right, battery_door_front, battery_door_rear, power_button_front, power_button_rear, undercarriage_white, all
Sensors (12 tools)
| Tool | Description |
|---|---|
start_sensor_streaming |
Start background sensor streaming |
stop_sensor_streaming |
Stop all streaming |
get_sensor_data |
Get cached sensor readings |
get_ambient_light |
Query light sensor directly |
enable_color_detection |
Enable/disable color sensor LED |
get_color_detection |
Query color sensor (auto LED) |
get_magnetometer |
Get compass heading and cardinal direction |
calibrate_magnetometer |
Calibrate compass (rotate 360°) |
get_temperature |
Get motor temperatures (°C) |
get_encoder_counts |
Get wheel encoder tick counts |
get_motor_thermal_protection_status |
Motor thermal state (ok/warning/critical) |
get_ir_readings |
Get IR sensor readings (bot-to-bot, 255=no signal) |
Streamable sensors: accelerometer, gyroscope, imu, locator, velocity, speed, quaternion, color_detection, ambient_light, core_time
Battery & System (11 tools)
| Tool | Description |
|---|---|
get_battery_status |
Battery percentage |
get_battery_voltage |
Battery voltage in volts |
get_battery_voltage_state |
Battery state (ok/low/critical) |
get_battery_thresholds |
Voltage thresholds (critical/low/hysteresis) |
get_safety_status |
Current safety settings |
get_firmware_version |
Firmware versions (Nordic + MCU) |
get_processor_name |
Processor identifiers (Nordic/ST) |
get_mac_address |
Bluetooth MAC address |
get_board_revision |
PCB board revisions |
get_sku |
Product SKU |
get_core_uptime |
Uptime in milliseconds |
Safety & Motor Protection (5 tools)
| Tool | Description |
|---|---|
set_speed_limit |
Set max speed (0-100%) |
set_command_timeout |
Set auto-stop timeout |
get_motor_fault_state |
Check for motor faults |
enable_motor_stall_notify |
Enable/disable stall detection |
enable_motor_fault_notify |
Enable/disable fault detection |
IR Communication (7 tools)
| Tool | Description |
|---|---|
send_ir_message |
Send IR code (0-7) |
start_ir_broadcasting |
Start robot-to-robot IR broadcasting |
stop_ir_broadcasting |
Stop IR broadcasting |
start_ir_following |
Follow an IR-broadcasting robot |
stop_ir_following |
Stop following |
start_ir_evading |
Evade an IR-broadcasting robot |
stop_ir_evading |
Stop evading |
Architecture
Component Overview
MCP Tool Handlers (FastMCP)
|
v
Direct Serial Protocol (low-latency)
|
+-- Packet Builder (commands.py)
+-- Serial Connection (direct_serial.py)
|
v
Sphero RVR (via /dev/ttyAMA0)
Key Design Patterns
Direct Serial Protocol
- Bypasses the Sphero SDK for minimal latency
- Constructs raw SOP/EOP packets directly
- Sub-millisecond command transmission
- Supports all core RVR commands
Distance-Based Movement
drive_forward(distance)- Uses RVR's internal position controllerdrive_backward(distance)- Accurate reverse movementpivot(degrees)- Precise rotation using heading control
Command Queue
- All hardware commands go through async priority queue
- Priority levels: EMERGENCY (0) -> HIGH (1) -> NORMAL (2) -> LOW (3)
- Per-command timeout enforcement
- Eliminates race conditions in concurrent access
Atomic State Management
- Thread-safe with explicit locks
- Validated state transitions
- All changes logged
Observability
Structured Logging
{
"event": "command_submitted",
"command_type": "drive_forward",
"distance": 0.5,
"timestamp": "2026-01-15T01:23:45.678Z"
}
Safety Features
Speed Limiting
All movement commands are limited to a configurable percentage of max speed (default 50%). This prevents accidental high-speed collisions.
You: Set the speed limit to 25%
You: Now drive forward at full speed
# RVR will only go at 25% of max speed
Command Timeout
If no movement command is received within the timeout period (default 5 seconds), the RVR automatically stops. This prevents runaway situations if connection is lost.
Emergency Stop
Immediately stops all movement and blocks further motion until explicitly cleared. Has highest priority in the command queue.
You: Emergency stop!
# RVR stops immediately
# All movement commands will fail until:
You: Clear the emergency stop
Troubleshooting
Connection Issues
"Failed to connect to RVR"
- Check serial connection:
ls -l /dev/ttyAMA0 - Ensure RVR is powered on and charged
- Verify baud rate (default 115200)
Connection times out
- RVR might be off or in deep sleep
- Try power cycling the RVR
- Check serial cable connection
Performance Issues
Slow response
- Raspberry Pi 3 may be slower than Pi 4/5
- Reduce sensor streaming frequency
- Close unnecessary applications
Sensor Issues
Color detection returns all zeros
- Ensure RVR is on a non-dark surface
- Try increasing stabilization time
- Check that belly LED is working
RVR Behavior Issues
RVR not responding to commands
- Check if emergency stop is active:
get_safety_status - Check speed limit isn't set to 0%
- Try disconnecting and reconnecting
RVR stops on its own
- Check command timeout setting (default 5 seconds)
- Disable auto-stop:
set_command_timeout(0) - Verify battery level isn't critical
Project Structure
sphero_rvr_mcp/
├── pyproject.toml # Package configuration
├── README.md # This file
├── LICENSE # MIT License
└── src/sphero_rvr_mcp/
├── __init__.py
├── __main__.py # Entry point
├── config.py # Configuration
├── api.py # Direct API (non-MCP)
├── server.py # MCP server & tool handlers
│
├── protocol/ # Direct serial protocol
│ ├── __init__.py
│ ├── commands.py # Packet builders
│ ├── direct_serial.py # Serial connection
│ └── packet.py # SOP/EOP framing
│
├── core/ # Core infrastructure
│ ├── command_queue.py # Priority command queue
│ ├── state_manager.py # Atomic state management
│ └── exceptions.py # Exception hierarchy
│
├── hardware/ # Hardware abstraction
│ ├── connection_manager.py # Connection lifecycle
│ ├── sensor_stream_manager.py # Sensor streaming
│ └── safety_monitor.py # Safety system
│
├── services/ # Application services
│ ├── movement_service.py
│ ├── led_service.py
│ └── ir_service.py
│
└── observability/ # Logging
└── logging.py # Structured logging
Development
Running Tests
# Unit tests
pytest tests/unit/
# Integration tests (requires RVR)
pytest tests/integration/
# All tests
pytest
Viewing Logs
# Console format (human-readable)
RVR_LOG_FORMAT=console python -m sphero_rvr_mcp
# JSON format (machine-readable)
RVR_LOG_FORMAT=json python -m sphero_rvr_mcp
Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
Changelog
v0.2.3 (2026-01-29)
- 22 new MCP tools for full SDK sensor coverage:
- Temperature & thermal protection monitoring
- System info (firmware, MAC, SKU, uptime, board revision)
- Extended battery info (voltage, state, thresholds)
- Motion sensors (encoders, magnetometer with compass heading)
- Motor protection (fault state, stall/fault notifications)
- IR follow/evade behaviors
- Fixed 4 sensor commands with incorrect protocol parameters
- Magnetometer heading - returns
heading(degrees) andcardinaldirection
v0.2.1 (2026-01-15)
- Direct serial protocol for low-latency control (bypasses SDK)
- Added
drive_forwardanddrive_backwardwith distance in meters - Added
pivotcommand for precise angle rotation - Removed unused dependencies (prometheus-client, tenacity, nest-asyncio)
- Added pyserial dependency
- Simplified architecture (removed circuit breaker, event bus, metrics)
- No longer requires Sphero SDK installation
v0.2.0 (2026-01-14)
- Complete architectural rewrite for production reliability
- Added command queue with priority levels
- Added circuit breaker for connection resilience
- Added event bus for sensor distribution
- Added atomic state management
- Added comprehensive observability (logging + metrics)
- Fixed SDK response key parsing (color sensor now works!)
v0.1.1 (2024-12-XX)
- Added connection timeouts
- Updated documentation
v0.1.0 (2024-12-XX)
- Initial release
License
MIT
Credits
Project details
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 sphero_rvr_mcp-0.2.3.tar.gz.
File metadata
- Download URL: sphero_rvr_mcp-0.2.3.tar.gz
- Upload date:
- Size: 67.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
173f24d21f1e9619a2cf74b2ce03447dc50e822387092883fbeb4f8216d8c845
|
|
| MD5 |
08959f897ce97d8ff152399a77bb29ca
|
|
| BLAKE2b-256 |
08e528c6c6e5484305e705abd2d1cce71cd891185b3401de3ce267348cebc958
|
File details
Details for the file sphero_rvr_mcp-0.2.3-py3-none-any.whl.
File metadata
- Download URL: sphero_rvr_mcp-0.2.3-py3-none-any.whl
- Upload date:
- Size: 60.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a49191df56de9342ff73b6f8845ceeaa6d7259ddead6a9414bad5e7e9ec794a2
|
|
| MD5 |
fcc31166f4c57ed447aae6b6be5f8325
|
|
| BLAKE2b-256 |
80c77129f257c79dd34dfd2a6e9cd2533e5ab7191a4da19f2ebd4ecb6e0f09a4
|