A Model Context Protocol (MCP) server for controlling the Reachy Mini robot using FastMCP.
[!NOTE] Looking for the full Conversation App? This repository also contains the full βConversation Stackβ (Hearing + LLM + Conversation Logic) which turns Reachy Mini into an autonomous conversational robot.
The Docker setup is specifically for running this full conversation application.
This MCP server provides a comprehensive set of tools to control Reachy Miniβs head movements, antennas, camera, and perform various gestures and emotional expressions.
speech parameter)operate_robot toollocalhost:8000 (default)The project ships as a pip-installable distribution, reachy-mini-mcp, which
provides both the MCP server and a manager CLI (reachy-mini-mcp).
# Manager CLI only (pure stdlib β show/install/uninstall/doctor/overview):
uv tool install reachy-mini-mcp # or: pipx install reachy-mini-mcp
# To actually run the server, add the [server] extra (and [tts] for speech):
pip install "reachy-mini-mcp[server]" # MCP stdio server
pip install "reachy-mini-mcp[server,tts]" # + Piper TTS
pip install "reachy-mini-mcp[openai]" # OpenAI-compatible HTTP server
From a source checkout, ./setup.sh creates a .venv and installs the package
editable with [server,tts].
reachy-mini-mcp is an MCP-server manager β it tells an agent or operator how to
register and run the server, and can do it for them:
reachy-mini-mcp overview # one-screen status (also the no-arg default)
reachy-mini-mcp show # print the mcp.json snippet for this machine
reachy-mini-mcp explain # how MCP registration works + where configs live
reachy-mini-mcp install --client claude-code --scope project # set it up
reachy-mini-mcp uninstall --client claude-code --scope project # put it down
reachy-mini-mcp doctor # diagnose deps, daemon, and client registration
reachy-mini-mcp serve # run the FastMCP stdio server (what mcp.json calls)
install / uninstall target claude-code (project .mcp.json or user
~/.claude.json), claude-desktop, cursor, or any --path FILE. They merge
into existing config (other servers are preserved) and accept --dry-run to
print the result without writing.
The mcp.json entry to add (also produced by reachy-mini-mcp show):
{
"mcpServers": {
"reachy-mini": {
"command": "reachy-mini-mcp",
"args": ["serve"],
"env": { "REACHY_BASE_URL": "http://localhost:8000" }
}
}
}
Before starting the MCP server, you need to have the Reachy Mini daemon running.
π Follow the official Reachy Mini Daemon Setup Guide
Ensure the daemon is running and accessible (default: http://localhost:8000).
reachy-mini-mcp serve
Equivalent invocations: python -m reachy_mini_mcp serve, or for a source
checkout fastmcp run reachy_mini_mcp/server.py.
The MCP server will now be running and ready to accept connections from MCP clients.
operate_robotThis MCP server exposes one MCP tool that provides access to all robot control functionality:
| Tool | Description |
|---|---|
operate_robot(tool_name, parameters) |
Meta-tool to dynamically execute any robot control operation by name. |
operate_robot(commands) |
Sequence mode to execute multiple operations in sequence. |
This unified interface allows you to call any of the robot control operations either individually or as a sequence:
Single Command Mode:
# Example: Get robot state
operate_robot("get_robot_state")
# Example: Express emotion with parameters
operate_robot("express_emotion", {"emotion": "happy"})
# Example: Move head with multiple parameters
operate_robot("move_head", {"z": 10, "duration": 2.0, "mm": True})
Sequence Mode (NEW!):
# Example: Execute multiple commands in sequence
operate_robot(commands=[
{"tool_name": "perform_gesture", "parameters": {"gesture": "greeting"}},
{"tool_name": "nod_head", "parameters": {"duration": 2.0, "angle": 15}},
{"tool_name": "move_antennas", "parameters": {"left": 30, "right": -30, "duration": 1.5}},
{"tool_name": "look_at_direction", "parameters": {"direction": "left", "duration": 1.0}}
])
Note: The tool name must match exactly. The correct tool is get_robot_state, not get_robot_status.
All operations are accessible through the operate_robot tool. Here are all available operations:
| Operation | Description |
|---|---|
get_robot_state |
Get full robot state including all components |
get_head_state |
Get current head position and orientation |
get_antennas_state |
Get current antenna positions |
get_camera_state |
Get camera status |
get_power_state |
Check if robot is powered on/off |
get_health_status |
Get overall health status |
turn_on_robot |
Power on the robot |
turn_off_robot |
Power off the robot |
stop_all_movements |
Emergency stop all movements |
| Operation | Description |
|---|---|
move_head |
Move head to specific pose (params: x, y, z, roll, pitch, yaw, duration) |
reset_head |
Return head to neutral position |
nod_head |
Make robot nod (params: duration, angle) |
shake_head |
Make robot shake head (params: duration, angle) |
tilt_head |
Tilt head left or right (params: direction, angle, duration) |
look_at_direction |
Look in a direction (params: direction - up/down/left/right, duration) |
| Operation | Description |
|---|---|
move_antennas |
Move antennas to specific positions (params: left, right, duration) |
reset_antennas |
Return antennas to neutral position |
| Operation | Description |
|---|---|
express_emotion |
Express emotion (params: emotion - happy/sad/curious/surprised/confused) |
perform_gesture |
Perform gesture (params: gesture - greeting/yes/no/thinking/celebration) |
| Operation | Description |
|---|---|
get_camera_image |
Capture image from camera |
get_camera_state |
Get camera status |
All operations are called through the operate_robot tool. Here are some examples:
# In your MCP client (e.g., Claude Desktop)
# Move head up 10mm and tilt 15 degrees
operate_robot("move_head", {"z": 10, "roll": 15, "duration": 2.0})
# Return to neutral
operate_robot("reset_head")
# Make the robot look happy
operate_robot("express_emotion", {"emotion": "happy"})
# Make the robot look curious
operate_robot("express_emotion", {"emotion": "curious"})
# Return to neutral
operate_robot("express_emotion", {"emotion": "neutral"})
# Wave hello
operate_robot("perform_gesture", {"gesture": "greeting"})
# Nod yes
operate_robot("perform_gesture", {"gesture": "yes"})
# Shake no
operate_robot("perform_gesture", {"gesture": "no"})
# Turn on the robot
operate_robot("turn_on_robot")
# Check state
state = operate_robot("get_robot_state")
# Make robot look around
operate_robot("look_at_direction", {"direction": "left", "duration": 1.5})
operate_robot("look_at_direction", {"direction": "right", "duration": 1.5})
operate_robot("look_at_direction", {"direction": "forward"})
# Express surprise
operate_robot("express_emotion", {"emotion": "surprised"})
# Perform celebration
operate_robot("perform_gesture", {"gesture": "celebration"})
# Turn off when done
operate_robot("turn_off_robot")
# Move antennas independently
operate_robot("move_antennas", {"left": 30, "right": -30, "duration": 1.0})
# Reset to neutral
operate_robot("reset_antennas")
Execute complex robot behaviors with command sequences:
# Greeting sequence
operate_robot(commands=[
{"tool_name": "express_emotion", "parameters": {"emotion": "happy"}},
{"tool_name": "perform_gesture", "parameters": {"gesture": "greeting"}},
{"tool_name": "nod_head", "parameters": {"duration": 1.5, "angle": 10}},
{"tool_name": "reset_head", "parameters": {}}
])
# Curious behavior - look around
operate_robot(commands=[
{"tool_name": "express_emotion", "parameters": {"emotion": "curious"}},
{"tool_name": "move_antennas", "parameters": {"left": 30, "right": 30, "duration": 1.0}},
{"tool_name": "look_at_direction", "parameters": {"direction": "left", "duration": 1.0}},
{"tool_name": "look_at_direction", "parameters": {"direction": "right", "duration": 1.0}},
{"tool_name": "look_at_direction", "parameters": {"direction": "forward", "duration": 0.5}}
])
# Initialization routine
operate_robot(commands=[
{"tool_name": "turn_on_robot", "parameters": {}},
{"tool_name": "reset_head", "parameters": {}},
{"tool_name": "reset_antennas", "parameters": {}},
{"tool_name": "get_robot_state", "parameters": {}}
])
For more details on command sequences, see SEQUENCE_COMMANDS.md.
The easiest way is to let the manager CLI write the config for you:
reachy-mini-mcp install --client claude-desktop # or claude-code / cursor
To do it by hand, add this to your clientβs config file (the same JSON works for Claude Desktop, Claude Code, and Cursor β see locations below):
{
"mcpServers": {
"reachy-mini": {
"command": "reachy-mini-mcp",
"args": ["serve"],
"env": { "REACHY_BASE_URL": "http://localhost:8000" }
}
}
}
Config file locations:
~/Library/Application Support/Claude/claude_desktop_config.json;
Linux: ~/.config/Claude/claude_desktop_config.json;
Windows: %APPDATA%\Claude\claude_desktop_config.json./.mcp.json; user: ~/.claude.json./.cursor/mcp.json; user: ~/.cursor/mcp.jsonAfter editing the config, restart the client. The Reachy Mini tools will be available in your conversations.
The server includes helpful prompts:
control_prompt - Guidelines for controlling Reachy Minisafety_prompt - Safety guidelines and limitsβββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β MCP Client ββββββββββΊβ FastMCP Server ββββββββββΊβ Reachy Daemon β
β (Claude, etc) β stdio β (reachy-mini-mcp β HTTP β (localhost:8000)β
β β β serve) β β β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β
βΌ
βββββββββββββββββββ
β Reachy Mini β
β Robot/Sim β
βββββββββββββββββββ
This MCP server uses a repository-based approach for defining tools, making it highly extensible and customizable. Tools are defined in JSON files rather than hardcoded in Python.
Key Benefits:
Repository Structure:
tools_repository/
βββ tools_index.json # Root file listing all tools
βββ *.json # Individual tool definitions
βββ scripts/ # Python scripts for complex tools
βββ nod_head.py
βββ shake_head.py
βββ express_emotion.py
βββ perform_gesture.py
Create a Python script in tools_repository/scripts/my_tool.py:
async def execute(make_request, create_head_pose, params):
"""Execute the tool."""
# Your logic here
await make_request("POST", "/api/endpoint1", json_data={...})
await asyncio.sleep(1.0)
await make_request("POST", "/api/endpoint2", json_data={...})
return {"status": "success"}
Then create a JSON file (e.g., tools_repository/my_tool.json):
{
"name": "my_tool",
"description": "Description of what my tool does",
"parameters": {
"required": [
{"name": "param1", "type": "string", "description": "First parameter"}
],
"optional": [
{"name": "param2", "type": "number", "default": 1.0, "description": "Second parameter"}
]
},
"execution": {
"type": "script",
"script_file": "my_tool.py"
}
}
Add to tools_repository/tools_index.json:
{
"name": "my_tool",
"enabled": true,
"definition_file": "my_tool.json"
}
Restart the server - your tool is now available!
Validate your tool definitions:
python test_repository.py
This verifies all JSON files are valid and script files exist.
This project is licensed under the MIT License, including the conversation and hearing app within this repository, and does not extend to the Reachy Mini Daemon.
Contributions are welcome! Please feel free to submit issues or pull requests.
For issues related to: