R2D2 MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@R2D2 MCP Serverdrive R2D2 forward and play a happy sound"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
R2-D2 Bluetooth Controller
A three-interface system for controlling a real R2-D2 robot toy over Bluetooth Low Energy (BLE):
Interface | File | How you control it |
Web Controller |
| Buttons, sliders and sound board in the browser |
Agentic MCP |
| AI agents (Copilot, Claude, Cursor) call tools to drive and play sounds |
The Force |
| Webcam-based facial & hand gesture control — open hand to drive, tilt to steer, thumbs up or open mouth for sounds |
Project Structure
R2D2/
├── R2D2.html # Browser-based BLE controller (Web Bluetooth API)
├── the-force.html # Gesture controller — face tilt steers, thumbs up plays sounds
├── R2D2-mcp.py # Python MCP server exposing R2D2 controls as AI tools
├── requirements.txt # Python dependencies (bleak, mcp)
├── setup.sh # One-shot environment setup + server launcher
├── screenshot.png # UI screenshot used in this README
├── .vscode/
│ └── mcp.json # VS Code MCP server config (auto-detected by Copilot)
└── README.mdRelated MCP server: sphero-rvr-mcp
The Device — littleBits Star Wars Droid Inventor Kit
The littleBits Star Wars Droid Inventor Kit (Model 680-0011), released in late 2017, is an award-winning STEM construction toy designed around snap-together magnetic blocks (Bits). Physically, it features a transparent plastic chassis revealing a modular electronic core, heavily relying on a central electronic block named the w33 Control Hub to process sensor input and command the droid's motors over Bluetooth Low Energy (BLE). [1] [2] [3]
Hardware Architecture
The internal electronics are driven by individual modular blocks linked together via magnetic, error-proof three-pin connectors carrying Power (VCC, ~5V converted from a 9V battery), Signal (Analog/PWM/Digital data), and Ground (GND). [1]
[ Power Bit ] ──> [ w33 Control Hub ] ──> [ Wire/Splitter Bit ]
│ │
▼ ▼
[ Proximity Sensor ] [ DC Motors / Servo ]Block | Role |
w33 Control Hub | The brain — a BLE peripheral housing a Nordic Semiconductor nRF-series SoC that translates incoming wireless data into hardware output signals |
Proximity Sensor Bit | Infrared (IR) reflection sensor providing real-time distance data for autonomous navigation [4] [5] |
DC Motor Bits | Geared rear-wheel motors driven by PWM voltage signals from the Control Hub [6] |
Servo Motor Bit | Installed in the neck section for head rotation and positioning [7] |
Official App Discontinuation
The original companion app featured over 22 in-app missions including Drive Mode (on-screen joysticks/tilt controls), Self-Nav Mode (autonomous obstacle avoidance via the IR sensor), and Force Mode (proximity-triggered steering). Following Sphero's acquisition of littleBits and the expiration of the Disney/Star Wars IP licence, Sphero completely removed the companion apps from the iOS App Store and Google Play Store. [8] [9] [12]
Without the official app to issue BLE startup and initialisation commands, the w33 Control Hub sits in a standby state (flashing white status LED) and refuses to activate or route any signals to the attached motors. [8] [11] This project exists to replace that lost functionality entirely.
BLE Protocol & Reverse Engineering
Independent reverse-engineering (primarily meetar's littlebits-r2d2-controls [10] [13]) has mapped how the Control Hub processes data packets.
Connection & LED states [11]
When powered on the hub advertises as
w32 ControlHub/w33 Control HubBlinking white LED = advertising, ready to pair
Solid green LED = successfully connected
GATT structure
The device uses a single custom write characteristic to receive all command arrays:
UUID | |
Service |
|
Characteristic |
|
Buffer throttling [13]
The w33 Control Hub's write buffer is notably fragile. Flooding the characteristic with unthrottled data causes buffer overflows and forced disconnects. A delay of 20–50 ms between sequential write commands is required for smooth, reliable operation.
Command maps [13]
Command type | Frames | Format prefix |
Drive (full reverse → full forward) | 62 |
|
Turn (hard left → hard right) | 33 |
|
Sound | 22 |
|
Behaviour notes
R2-D2 only advertises BLE for ~30 seconds after power-on — power-cycle if it doesn't appear in scans
A stop + straight frame is sent immediately on connect (safety behaviour)
If another device is already connected, R2-D2 won't advertise until that connection is released
The droid has an auto-sleep timeout that drops the BLE link after inactivity
References
# | Source |
[1] | |
[2] | |
[3] | |
[4] | |
[5] | |
[6] | |
[7] | |
[8] | |
[9] | |
[10] | |
[11] | |
[12] | |
[13] |
How It Works
Both interfaces communicate with R2-D2 over the same BLE service/characteristic:
Value | |
Service UUID |
|
Characteristic UUID |
|
Commands are pre-encoded hex frames written to the GATT characteristic — the same protocol used by the official R2-D2 app.
Interface 1 — Browser Controller (R2D2.html)
A standalone HTML file with no build step or dependencies. Open it in a Chromium-based browser (Chrome, Edge, Arc) and click Connect R2-D2.
Features
Drive — Speed slider maps −100 → +100 to a table of 62 pre-encoded drive frames
Steering — Steering slider plus Full Left / Centre / Full Right quick buttons
Sounds — Buttons for 22 named R2-D2 sounds (beep, whistle, grump, scream, etc.)
STOP — Prominent emergency stop button
Debug log — Live BLE TX log panel

Requirements
A Chromium-based browser with Web Bluetooth support
The robot in BLE advertising range
A secure context (
localhostorhttps://) — Web Bluetooth does not work on plainhttp://remote hosts
Usage
# Simplest way — open directly from disk
open R2D2.htmlNote: Some browsers block Web Bluetooth when opened as a
file://URL. If the Connect button does nothing, serve the file locally:python3 -m http.server 8080 # then visit http://localhost:8080/R2D2.html
Connecting to R2-D2: When the Bluetooth device picker appears, select w32 ControlHub from the list. If it does not appear, power cycle R2-D2 (switch it off and back on) and click Connect R2-D2 again — the droid advertises for a short window after boot.
Interface 2 — The Force (the-force.html)
A standalone HTML file that uses your webcam to control R2-D2 hands-free.
Tilt your head left/right — steers R2-D2 left or right proportionally. A 5° dead zone keeps it straight when you’re roughly centred. Full lock at ~28° tilt.
Thumbs up (either hand) — plays a random R2-D2 sound. 2.5-second cooldown between sounds.
How it works
Technology | Purpose |
Real-time face mesh (468 landmarks) + hand tracking (21 landmarks per hand) running in WebAssembly | |
Face landmarks 33 & 263 | Outer eye corners — used to compute head roll angle |
Exponential smoothing | Damps jitter in the tilt angle before sending steering commands |
Hand landmarks 4, 2, 6/8, 10/12, 14/16, 18/20 | Thumb tip/MCP + finger tip/PIP pairs for thumbs-up detection |
Web Bluetooth | Same BLE connection as |
Usage
# Must be served over http(s) for Web Bluetooth + camera
python3 -m http.server 8080
# then visit http://localhost:8080/the-force.htmlMake sure R2-D2 is switched on and advertising
Click ⚡ ACTIVATE THE FORCE
Select
w32 ControlHubfrom the Bluetooth pickerAllow camera access when prompted
Tilt your head to steer, give a thumbs up to trigger a sound
Note: Both camera access and Web Bluetooth require a secure context (
localhostorhttps://). The file cannot be opened directly from disk as afile://URL.
Interface 3 — MCP Server (R2D2-mcp.py)
A Python Model Context Protocol server that exposes R2-D2 controls as tools an AI agent (e.g. GitHub Copilot, Claude Desktop, or any MCP-compatible host) can call autonomously.
Exposed Tools
Tool | Description |
| Scan for and connect to R2-D2 over BLE (optional address / name hint) |
| Disconnect from the robot |
| Return current connection state and device address |
| Drive at a given index 0–61 (31 = stop, >31 forward, <31 reverse) |
| Steer at a given index 0–32 (17 = straight) |
| Play one of 22 named sounds (see table below) |
| Immediate emergency stop |
Available Sounds
Pass the exact string in the name parameter of r2d2.play_sound.
Name | Description |
| Short single beep |
| Short bleep |
| Power-on board initialisation sequence |
| Rapid chattering |
| Upbeat happy tones |
| Quick chitter |
| Descending tone sequence |
| Sceptical, uncertain sound |
| Excited high-pitched sequence |
| Grumpy low grumble |
| Affectionate tones |
| Sad, dejected sound |
| Scolding chatter |
| Loud alarmed scream |
| Boot-up sequence |
| Extended narrative sequence |
| Startled surprise sound |
| Raspberry / dismissive sound |
| Contemplative processing sound |
| Clean whistle |
| Anxious worried tones |
| Impressed wow reaction |
Requirements
Python 3.10+ (the
mcppackage requires it; macOS ships 3.9 — use Homebrew:brew install python)Bluetooth adapter accessible to the Python process
macOS: grant Bluetooth permission to Terminal / VS Code in System Settings → Privacy & Security → Bluetooth
Python dependencies are listed in requirements.txt:
bleak>=3.0.2
mcp>=2.1.1Install them (and launch the server) with:
./setup.shOr install manually into an existing environment:
pip install -r requirements.txtRunning the MCP Server
python R2D2-mcp.pyThe server communicates over stdio — point your MCP host at it using the configs below.
VS Code / GitHub Copilot
Step 1 — Install dependencies (once)
Open a terminal in the project folder and run:
./setup.shThis creates a .venv virtual environment and installs bleak and mcp. You only need to do this once.
Step 2 — Grant Bluetooth access (macOS, once)
Go to System Settings → Privacy & Security → Bluetooth and make sure Visual Studio Code is enabled. VS Code may prompt you automatically the first time.
Step 3 — Open the project in VS Code
The included .vscode/mcp.json points VS Code at the MCP server automatically:
{
"servers": {
"r2d2": {
"type": "stdio",
"command": "${workspaceFolder}/.venv/bin/python",
"args": ["${workspaceFolder}/R2D2-mcp.py"]
}
}
}Step 4 — Start a Copilot Agent session
Open the Chat panel with
⌃⌘IClick the mode selector and choose Agent
Click the Tools button (spanner icon) — you should see
r2d2_connect,r2d2_drive, etc. listed
Step 5 — Test it
Try these prompts in the Agent chat:
"Connect to R2-D2" — scans for and connects to the w32 ControlHub over BLE "Play the excited sound" "Drive R2-D2 forward then stop after 2 seconds" "What is the current R2-D2 connection status?"
Copilot will call the MCP tools automatically and report back what happened.
Restarting / resetting the MCP server
You need to restart the server whenever you edit R2D2-mcp.py or after the server process crashes.
Action | How |
Restart the server |
|
View server logs | Output panel ( |
Force full reload |
|
Check tools are registered | Agent chat → Tools button → look for |
Note: Tool names use underscores (
r2d2_connect), not dots. VS Code's MCP spec only allows[a-z0-9_-]in tool names. If you see warnings about invalid tool names in the MCP log, the server is running an older version of the file — restart it.
Tip: If the tools don't appear after a restart, check the Output panel → GitHub Copilot MCP for startup errors. The most common cause is the
.venvnot existing — run./setup.shfirst.
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"r2d2": {
"command": "python",
"args": ["/path/to/R2D2-mcp.py"]
}
}
}Replace /path/to/R2D2-mcp.py with the absolute path to the file. Restart Claude Desktop — the R2-D2 tools will appear in the tools list.
Cursor
Open Settings → MCP and add a new server entry:
{
"r2d2": {
"command": "python",
"args": ["/path/to/R2D2-mcp.py"]
}
}Any other MCP-compatible host
The server uses the standard stdio transport. Configure your host to launch:
python /path/to/R2D2-mcp.pyNo additional flags are needed. The server advertises all tools on startup via the MCP initialize handshake.
Development Notes
MCP SDK Version
This project targets mcp 2.x (MCPServer from mcp.server.mcpserver). The v2 SDK generates JSON schemas automatically from Python type annotations — no manual Schema.json({...}) blocks are needed. If you find older tutorials using ToolRequest/ToolResponse/Schema, those are v1 patterns and will not work here.
Python Version
macOS ships Python 3.9 via /usr/bin/python3. The mcp package requires 3.10+. setup.sh automatically picks up /opt/homebrew/bin/python3 when Homebrew is present, falling back to whichever python3 is on $PATH.
BLE Device Name
The robot advertises as w32 ControlHub over BLE. If it does not appear in scans, power-cycle R2-D2 — it only advertises for a short window after boot.
Safety Behaviour
Both interfaces send a stop frame immediately on connect and on disconnect so the robot never drives away unattended when a connection is established or dropped.
Future Work — LED Control via BLE Sniffing
The w32 ControlHub firmware uses pre-encoded proprietary hex frames for all commands (the same format as the drive and sound commands). To add LED control, the raw bytes for each LED state need to be captured from the official R2-D2 app.
How to capture LED commands using nRF Connect (iOS/Android)
What you need: iPhone or Android with nRF Connect installed (free)
Step 1 — Connect nRF Connect to R2-D2
Power-cycle R2-D2
Open nRF Connect → Scanner tab
Find
w32 ControlHuband tap ConnectTap the Client tab once connected
Step 2 — Enable logging
Tap the ⋮ menu (top right) → Log
Make sure logging is enabled
Step 3 — Observe the characteristic
Expand the service
d9d9e9e0-aa4e-4797-8151-cb41cedaf2adFind characteristic
d9d9e9e1-aa4e-4797-8151-cb41cedaf2adTap the ↓ (notify/indicate) button to subscribe to incoming values
Step 4 — Capture from the official app
Because only one app can hold a BLE connection at a time, disconnect nRF Connect first, let the official Sphero/R2-D2 app connect, change LED colours, then reconnect nRF Connect to review the log. Alternatively use an Android phone with Bluetooth HCI snoop log enabled (Developer Options → Enable Bluetooth HCI snoop log) which passively captures all traffic.
Disconnect nRF Connect
Open the official Sphero app and connect to R2-D2
Change the front LED to red, wait 2 seconds
Change to green, wait 2 seconds
Change to blue, wait 2 seconds
Disconnect the Sphero app
Reconnect nRF Connect and review the Log tab
Step 5 — Share the bytes
The log will show entries like:
Value written to D9D9E9E1: 1A 03 02 FF 00 00 ...Those hex strings can be added to R2D2-mcp.py and R2D2.html using the same pattern as the existing SOUNDS and DRIVE_CONTROLS tables.
LED channels to capture
Channel | What to do in the app |
Front RGB | Set body colour to pure red, green, blue |
Logic displays | Toggle logic display on/off |
Back RGB | Set back colour to pure red, green, blue |
Holo projector | Toggle holographic projector on/off |
Credits & References
This project stands on the shoulders of prior reverse-engineering and open-source work.
Libraries used
Library | Purpose | Link |
bleak | Cross-platform BLE library for Python; handles all BLE scanning and GATT writes | |
mcp (Python SDK) | Model Context Protocol server framework; exposes R2D2 tools to AI agents |
Reference projects
Project | What it contributed | Link |
meetar/littlebits-r2d2-controls | The primary reverse-engineering source for the proprietary BLE command arrays (drive frames, turn frames, sound frames) used in this project. The key discovery that the w33 Control Hub requires packet throttling (20–50 ms between writes) also comes from this work | |
spherov2.py (UPenn AI class) | Full reverse-engineered implementation of the Sphero V2 BLE protocol including R2D2 LED channel names ( | |
spherov2.js | Earlier JS implementation of the Sphero V2 protocol; one of the foundational community reverse-engineering efforts that spherov2.py built upon | |
Sphero SDK (Raspberry Pi) | Official Sphero Python SDK for RVR; useful for understanding the official protocol structure |
Protocol notes from the community
The drive/turn/sound hex frames in this project (140202..., 140201..., 1E01...) were originally reverse-engineered from packet captures of the official Sphero R2-D2 iOS app communicating over BLE. The community approach was to use a BLE sniffer (Wireshark, nRF Sniffer, or HCI logs) while operating the official app to record the raw GATT writes, then tabulate the frames by function.
This server cannot be deployed
Maintenance
Related MCP Connectors
- mcp-serverOAuthcom.make
Give your AI agents the tools to build, manage, and run automation workflows.
Connect AI agents to 1000+ apps with managed authentication and tool-calling.
Drive real Android & iOS devices and web browsers from natural language for mobile + web QA. 290+ tools across device control, app management, automation sessions, browser automation, and flow recording / replay. Bearer-auth — get a token at robotactions.com → Profile → API Tokens.
Verified, pay-per-use API tools for AI agents through one authenticated connection.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceControls toio robot cubes via natural language through BLE, enabling movement, LED control, and sound playback from MCP-compatible clients like Claude Desktop.1-
- AlicenseCqualityDmaintenanceEnables AI assistants to control a Sphero RVR robot via the MCP protocol, including movement, LEDs, sensors, and battery monitoring.55MIT
- AlicenseNot gradedqualityBmaintenanceEnables driving a BitRobot-compatible ground robot (e.g., Earth Rover Mini+ or Waveshare UGV) through high-level verbs like move, turn, look, and capture work, with optional on-chain recording of verifiable robotic work.MIT
- AlicenseAqualityFmaintenanceLets AI assistants control the physical Return to Dark Tower board game tower via Bluetooth, enabling connection, calibration, sound, lights, drum rotation, seal breaking, and game sequences through natural language.3427 npmMIT