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
- 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
Production-Grade Architecture (v0.2.0+)
- Command Queue: Priority-based async queue eliminates race conditions
- Circuit Breaker: Prevents infinite hangs when RVR is powered off
- Event Bus: Pub/sub pattern for efficient sensor data distribution
- Atomic State Management: Thread-safe state with validated transitions
- Comprehensive Observability: Structured logging (JSON/console) + Prometheus metrics
- No Hard-Coded Delays: Event-driven connection (no 2-second sleep on connect)
- Robust Error Handling: No silent failures, all errors logged and reported
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+ (3.10.x recommended for Sphero SDK compatibility)
- Sphero RVR with serial connection to Pi
- An MCP-compatible client
Installation
1. Clone the Sphero SDK
The Sphero SDK is not available on PyPI and must be installed manually:
cd ~
git clone https://github.com/sphero-inc/sphero-sdk-raspberrypi-python.git
2. Install the MCP Server
Option A: Install from PyPI (recommended)
pip install sphero-rvr-mcp
# Add Sphero SDK to Python path (add to ~/.bashrc for persistence)
export PYTHONPATH="${PYTHONPATH}:${HOME}/sphero-sdk-raspberrypi-python"
Option B: Install from source
git clone https://github.com/jsperson/sphero_rvr_mcp.git
cd sphero_rvr_mcp
# Install the package in development mode
pip install -e .
# Add Sphero SDK to Python path (add to ~/.bashrc for persistence)
export PYTHONPATH="${PYTHONPATH}:${HOME}/sphero-sdk-raspberrypi-python"
3. 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+)
- Sphero SDK is installed
- FastMCP is installed
- Serial port exists and is accessible
- Current configuration settings
4. Configure Your MCP Client
The server runs via stdio. Configure your MCP client with:
- Command:
python -m sphero_rvr_mcp - Environment:
PYTHONPATHmust include the Sphero SDK path
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"],
"env": {
"PYTHONPATH": "<your-home-dir>/sphero-sdk-raspberrypi-python"
}
}
}
}
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"],
"env": {
"PYTHONPATH": "<path-to>/sphero-sdk-raspberrypi-python"
}
}
}
}
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/ttyS0 |
Serial port for RVR |
RVR_BAUD_RATE |
115200 |
Serial baud rate |
RVR_WAKE_TIMEOUT |
5.0 |
RVR wake timeout (seconds) |
| 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 |
RVR_SENSOR_CACHE_TTL |
2.0 |
Sensor data TTL (seconds) |
| Observability | ||
RVR_LOG_LEVEL |
INFO |
Log level (DEBUG, INFO, WARNING, ERROR) |
RVR_LOG_FORMAT |
json |
Log format (json, console) |
RVR_METRICS_ENABLED |
true |
Enable Prometheus metrics |
RVR_METRICS_PORT |
9090 |
Metrics HTTP server port |
| Circuit Breaker | ||
RVR_CIRCUIT_BREAKER_FAILURE_THRESHOLD |
5 |
Failures before opening |
RVR_CIRCUIT_BREAKER_TIMEOUT |
30.0 |
Timeout before half-open (seconds) |
Usage
Example Commands
Once your MCP client is connected to the server:
You: Connect to the RVR
You: Drive forward slowly for 2 seconds then stop
You: Set all LEDs to blue
You: What's the battery level?
You: Start streaming the accelerometer and gyroscope sensors
You: Turn left 90 degrees
You: What color is under the rover?
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 (9 tools)
| Tool | Description |
|---|---|
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 (6 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) |
Streamable sensors: accelerometer, gyroscope, imu, locator, velocity, speed, quaternion, color_detection, ambient_light, core_time
Battery & System (2 tools)
| Tool | Description |
|---|---|
get_battery_status |
Battery percentage and voltage state |
get_system_info |
Connection info and uptime |
Safety Controls (3 tools)
| Tool | Description |
|---|---|
get_safety_status |
Current safety settings |
set_speed_limit |
Set max speed (0-100%) |
set_command_timeout |
Set auto-stop timeout |
IR Communication (3 tools)
| Tool | Description |
|---|---|
send_ir_message |
Send IR code (0-7) |
start_ir_broadcasting |
Start robot-to-robot IR |
stop_ir_broadcasting |
Stop IR broadcasting |
Architecture
Component Overview
MCP Tool Handlers (FastMCP)
↓
Application Services Layer
├── ConnectionService
├── MovementService
├── SensorService
├── LEDService
├── SafetyService
└── IRService
↓
Core Infrastructure
├── Command Queue (priority + timeout)
├── Event Bus (pub/sub)
├── Circuit Breaker (resilience)
└── State Manager (atomic)
↓
Hardware Abstraction
├── Connection Manager
├── Sensor Stream Manager
└── Safety Monitor
↓
Sphero RVR SDK
Key Design Patterns
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
Circuit Breaker
- States: CLOSED (healthy) → OPEN (failing) → HALF_OPEN (testing)
- Prevents infinite hangs when RVR is powered off
- Automatic recovery with configurable threshold
- Fails fast when unhealthy
Event Bus
- Pub/sub pattern for sensor data distribution
- Decouples streaming from consumers
- Built-in backpressure management
- Multiple subscribers per sensor
Atomic State Management
- Thread-safe with explicit locks
- Validated state transitions
- Separate state objects: SystemState, ConnectionInfo, SafetyState, SensorState
- All changes logged and metered
Observability
Structured Logging
{
"event": "command_submitted",
"command_type": "drive_with_heading",
"speed": 50,
"heading": 90,
"timestamp": "2026-01-14T01:23:45.678Z"
}
Prometheus Metrics (port 9090 by default)
- Command metrics: total, duration, queue depth
- Connection metrics: state, reconnections, uptime
- Safety metrics: emergency stops, speed limits applied
- Sensor metrics: streaming active, data age, events
- Circuit breaker metrics: state, failures, successes
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/ttyS0 - Ensure RVR is powered on and charged
- Verify baud rate (default 115200)
- Check circuit breaker isn't open (connection status)
"RVR wake() timed out"
- RVR might be off or in deep sleep
- Try power cycling the RVR
- Check serial cable connection
- Circuit breaker will open after 5 failures (default)
SDK Issues
"sphero_sdk not found"
- Add SDK to PYTHONPATH:
export PYTHONPATH="${PYTHONPATH}:/path/to/sphero-sdk-raspberrypi-python" - Add to
~/.bashrcfor persistence - Verify SDK is cloned:
ls ~/sphero-sdk-raspberrypi-python
"nest_asyncio required"
- The Sphero SDK requires nested event loops
- This is normal and handled automatically by the server
Performance Issues
Slow response
- Raspberry Pi 3 may be slower than Pi 4/5
- Reduce sensor streaming frequency
- Close unnecessary applications
- Check command queue depth (metrics endpoint)
Commands timing out
- Check circuit breaker state (connection status)
- Verify RVR is not in emergency stop
- Increase command timeout in configuration
- Check system logs for errors
Sensor Issues
Color detection returns all zeros
- This is fixed in v0.2.0+ (SDK key parsing)
- Ensure RVR is on a non-dark surface
- Try increasing stabilization time
- Check that belly LED is working
Sensor streaming not updating
- Verify streaming is started:
get_sensor_data - Check sensor cache TTL (default 2 seconds)
- Verify sensors are in available list
- Check event bus metrics
RVR Behavior Issues
RVR not responding to commands
- Check if emergency stop is active:
get_safety_status - Verify connection:
get_connection_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
Metrics & Monitoring
Access Prometheus metrics at http://<raspberry-pi-ip>:9090/metrics (if enabled).
Key Metrics:
rvr_commands_total- Total commands by type and statusrvr_command_duration_seconds- Command latency histogramrvr_command_queue_depth- Current queue depthrvr_connection_state- Connection state (0=disconnected, 1=connecting, 2=connected, 3=error)rvr_emergency_stops_total- Emergency stop countrvr_sensor_streaming_active- Sensor streaming statusrvr_circuit_breaker_state- Circuit breaker state (0=closed, 1=open, 2=half_open)
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
│
├── core/ # Core infrastructure
│ ├── command_queue.py # Priority command queue
│ ├── circuit_breaker.py # Circuit breaker pattern
│ ├── event_bus.py # Pub/sub event bus
│ ├── 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
│ ├── connection_service.py
│ ├── movement_service.py
│ ├── sensor_service.py
│ ├── led_service.py
│ ├── safety_service.py
│ └── ir_service.py
│
├── tools/ # MCP tool handlers
│ ├── connection_tools.py
│ ├── movement_tools.py
│ ├── sensor_tools.py
│ ├── led_tools.py
│ ├── safety_tools.py
│ └── ir_tools.py
│
└── observability/ # Logging & metrics
├── logging.py # Structured logging
├── metrics.py # Prometheus metrics
└── health.py # Health monitoring
Direct API Usage (Non-MCP)
You can also use the RVR client directly without MCP:
import asyncio
from sphero_rvr_mcp.api import RVRClient
async def main():
client = RVRClient(log_level="INFO", log_format="console")
await client.initialize()
await client.connect()
# Control LEDs
await client.set_all_leds(255, 0, 0) # Red
# Read sensors
color = await client.get_color_detection()
print(f"Color: {color}")
# Movement (if not wired up!)
# await client.drive_with_heading(speed=50, heading=0)
await client.shutdown()
asyncio.run(main())
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.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!)
- Removed hard-coded delays (2s sleep eliminated)
- Enhanced battery status with voltage state
- Improved error handling (no silent failures)
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.1.tar.gz.
File metadata
- Download URL: sphero_rvr_mcp-0.2.1.tar.gz
- Upload date:
- Size: 67.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.12.1.2 requests/2.32.5 setuptools/79.0.1 requests-toolbelt/1.0.0 tqdm/4.67.1 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbfd4f7e77c4bf15daddaf0d8cb4618ef01599af493b56f4cc50a70f2f64b87e
|
|
| MD5 |
2b18e68c3c7c74bb5a0ab3ec33e2e4fb
|
|
| BLAKE2b-256 |
442b915d95b2be3f3ac185fdbf36765cc39db333032852eb6ba1836bec6fe552
|
File details
Details for the file sphero_rvr_mcp-0.2.1-py3-none-any.whl.
File metadata
- Download URL: sphero_rvr_mcp-0.2.1-py3-none-any.whl
- Upload date:
- Size: 52.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.12.1.2 requests/2.32.5 setuptools/79.0.1 requests-toolbelt/1.0.0 tqdm/4.67.1 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3dbaa866046aa58ebd4bb033c1925bb1a112e27c9a6313dd8f400ffde747bfe
|
|
| MD5 |
f48996b9baef3e499d7ff06864f01c9c
|
|
| BLAKE2b-256 |
1e3f1d53339d35ec7be33063013413f3db8cfea8c2c2b35d4eaacb77c492bf15
|