"""Dance tool for Reachy Mini MCP server."""
from __future__ import annotations
import logging
import random
from typing import Any
from mcp.server.fastmcp import Context, FastMCP
from reachy_mini_mcp.moves import DanceQueueMove
logger = logging.getLogger(__name__)
# Available dance moves
try:
from reachy_mini_dances_library.collection.dance import AVAILABLE_MOVES
DANCE_AVAILABLE = True
DANCE_MOVES = list(AVAILABLE_MOVES.keys())
except ImportError:
logger.warning("Dance library not available")
DANCE_AVAILABLE = False
DANCE_MOVES = []
def register_dance_tool(mcp: FastMCP) -> None:
"""Register the dance tool with the MCP server."""
@mcp.tool()
def dance(
ctx: Context,
move: str = "random",
repeat: int = 1,
) -> dict[str, Any]:
"""Play a dance move on the Reachy Mini robot.
Args:
move: Name of the dance move to play. Use "random" for a random move.
Available moves:
- simple_nod: A simple, continuous up-and-down nodding motion
- head_tilt_roll: A continuous side-to-side head roll
- side_to_side_sway: A smooth, side-to-side sway of the entire head
- dizzy_spin: A circular 'dizzy' head motion
- stumble_and_recover: A simulated stumble and recovery
- interwoven_spirals: A complex spiral motion using three axes
- sharp_side_tilt: A sharp, quick side-to-side tilt
- side_peekaboo: A multi-stage peekaboo performance
- yeah_nod: An emphatic two-part yeah nod
- uh_huh_tilt: A combined roll-and-pitch agreement gesture
- neck_recoil: A quick backward recoil of the neck
- chin_lead: A forward motion led by the chin
- groovy_sway_and_roll: A groovy side-to-side sway with roll
- chicken_peck: A sharp, forward pecking motion
- side_glance_flick: A quick glance to the side
- polyrhythm_combo: A polyrhythmic 3-beat sway and 2-beat nod
- grid_snap: A robotic, grid-snapping motion
- pendulum_swing: A smooth pendulum-like swing
- jackson_square: Traces a rectangle with sharp twitches
repeat: Number of times to repeat the move (default 1).
Returns:
Status dict with "status", "move", and "repeat" keys.
"""
if not DANCE_AVAILABLE:
return {"status": "error", "error": "Dance library not available"}
robot_manager = ctx.request_context.lifespan_context.robot_manager
if not robot_manager.is_connected():
return {"status": "error", "error": "Robot not connected"}
# Handle random move selection
selected_move = move
if move == "random" or move not in DANCE_MOVES:
if move != "random":
logger.warning(f"Unknown move '{move}', selecting random")
selected_move = random.choice(DANCE_MOVES)
# Queue the dance move(s)
for _ in range(repeat):
dance_move = DanceQueueMove(selected_move)
robot_manager.queue_move(dance_move)
logger.info(f"Queued dance: {selected_move} x{repeat}")
return {"status": "queued", "move": selected_move, "repeat": repeat}