Skip to main content
Glama
sallu-786

Unitree Go2 MCP Server

by sallu-786

πŸ€– MCP Web Portal β€” Unitree Go2 Robot Control Interface

A browser-based control and monitoring portal for the Unitree Go2 robot dog, built with Gradio and ROS2 keeping it 100% pythonic. The portal streams live camera feeds, displays sensor telemetry, provides remote navigation controls, supports autonomous waypoint missions, and integrates LLM-powered scene description. Portal Screenshot

Table of Contents


Related MCP server: WEATHGARDS

Feature Overview

Feature

Description

πŸŽ₯ Live Camera

Front-facing video stream + Intel RealSense RGB/Depth feed, with YOLO object detection overlaid on either camera

πŸ—ΊοΈ Map & Navigation

2D occupancy-grid map rendering with Nav2 goal-setting from the browser

πŸ•ΉοΈ Remote Control

Virtual joystick / directional controller for driving the robot over WirelessController messages

πŸ“‘ Telemetry

Live battery %, pose (x/y/yaw), sport-mode state, and IMU roll/pitch/yaw

🧠 AI Scene Description

LLM-based image analysis of the camera feed (Azure OpenAI or local Ollama models via LiteLLM), with a configurable system prompt

πŸ”Š Audio / Sounds

Upload audio files and play them through the robot's onboard speaker

πŸ’‘ LED Controller

Adjust headlight color and brightness

πŸ“Š ROS Graph View

Auto-generated visual graph of active ROS 2 topics, publishers, and subscribers

πŸ› οΈ Development Tab

Diagnostic tools and dev utilities for debugging the ROS bridge/connections

🌐 MCP Server

Exposes all portal-managed topics/services as MCP tools so an LLM agent can inspect and control the robot

πŸ“ Waypoint Missions

Save and replay autonomous navigation waypoints (data/waypoints/waypoints.json)


Architecture

Architecture Diagram

The robot connects to the host machine via rosbridge (default 127.0.0.1:9090), using either the DDS or WebRTC transport mode (MODE in config.py). All ROS 2 nodes are registered onto a shared executor in main.py, and their live data is surfaced to both the Gradio UI and the MCP server from the same DataStream object in web_backend/data_stream.py.


Project Structure

.
β”œβ”€β”€ main.py                    # App entry point β€” ROS2 init, node registration, Gradio launch
β”œβ”€β”€ config.py                  # Central settings: topic names, LLM config, rosbridge connection, UI flags
β”œβ”€β”€ server.py                  # MCP server exposing ROS2 topics/services as agent tools
β”œβ”€β”€ test.py                    # Test/scratch script
β”œβ”€β”€ pyproject.toml / uv.lock   # uv-managed dependency lockfile
β”œβ”€β”€ requirements.txt           # Full pinned dependency list (ROS2, Gradio, ML, MCP stack)
β”‚
β”œβ”€β”€ web_backend/
β”‚   β”œβ”€β”€ action_sub.py          # SportMode action interface (stand, sit, hello, dance, etc.)
β”‚   β”œβ”€β”€ audio_sub.py           # Access to the Go2's onboard speaker
β”‚   β”œβ”€β”€ bm_status.py           # Battery / motor / IMU (roll, pitch, yaw) status
β”‚   β”œβ”€β”€ camera.py              # Main front camera access
β”‚   β”œβ”€β”€ camera_rs.py           # RealSense RGB/Depth camera access (requires RealSense ROS2 pkg on the Go2)
β”‚   └── data_stream.py         # Core hub: ROS2 subscribers/publishers, YOLO inference, LLM calls, map builder
β”‚
β”œβ”€β”€ web_frontend/
β”‚   β”œβ”€β”€ index.py                # Main tab UI β€” camera, map, telemetry
β”‚   β”œβ”€β”€ action.py                # Actions tab UI β€” waypoints, missions, sport commands
β”‚   β”œβ”€β”€ dev.py                   # Development tab UI β€” diagnostics
β”‚   └── style.css                # Custom CSS
β”‚
β”œβ”€β”€ backends/                  # Additional backend service modules
β”œβ”€β”€ utils/                     # Shared helper utilities
β”‚
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ yolo/best2.pt           # YOLO model weights used for object detection
β”‚   β”œβ”€β”€ waypoints/waypoints.json# Saved navigation waypoints
β”‚   └── sounds/                 # Uploaded audio files for robot playback
β”‚
└── github_media/               # Screenshots / media used in repo documentation

Tech Stack

Based on the project's pinned requirements.txt, the portal is built on:

  • Robotics / middleware: ROS 2 (rclpy, ros2cli tooling), rosbridge-suite for the WebSocket bridge to the robot, unitree_go/unitree_api/unitree_hg message packages, and the unitree_sdk2_python SDK (does not need seperate install, requirements.txt alread has it as an editable Git dependency)

  • Navigation: nav2-msgs, nav2-simple-commander, slam-toolbox, cartographer-ros-msgs for occupancy-grid mapping and goal navigation. You may choose any code you like. I used following repo go2_slam_nav2

  • Web UI: gradio (v6.x) and gradio_client for the browser interface; fastapi / starlette / uvicorn underneath

  • Computer vision: opencv-python, ultralytics (YOLO) for object detection, torch / torchvision

  • LLM / agent layer: litellm (unified model API), openai, ollama (Python client), and mcp (the official Model Context Protocol SDK) for the agent-facing tool server

  • Audio: gTTS, pydub for text-to-speech / audio handling

  • Real-time transport: aiortc/aioice/av for optional WebRTC-based video/data channels

  • Misc: pandas, matplotlib/networkx (for the ROS topic/service graph visualization), redis, python-dotenv


Prerequisites

  • Ubuntu 22.04 (recommended)

  • ROS 2 Humble or later

  • Python 3.10+

  • Unitree ROS 2 SDK β€” unitreerobotics/unitree_ros2, installed and sourced

  • rosbridge_suite (```bash ros2 launch rosbridge_server rosbridge_websocket_launch.xml

```) (if you run it on laptop you can find it on 127.0.0.1:9090)

  • Nav2 (optional β€” required only for autonomous waypoint navigation)

  • Intel RealSense ROS 2 package installed on the Go2 (optional β€” required only for the RealSense RGB/Depth tab)

  • Ollama (optional β€” for local LLM inference instead of Azure OpenAI)

  • An NVIDIA GPU is not required, but the pinned requirements include CUDA-enabled torch/nvidia-* wheels for faster YOLO inference if one is available


Installation

1. Clone the repository

git clone https://github.com/sallu-786/Unitree_Go2_Web_Portal.git
cd Unitree_Go2_Web_Portal

2. Install Python dependencies

install from the pinned requirements.txt (note: this file includes ROS 2 Python packages, so it assumes a ROS 2 environment is already sourced/available):

pip install -r requirements.txt

3. Source ROS 2 and the Unitree setup script

source /opt/ros/humble/setup.bash
source /home/<your-user>/unitree_ros2/setup.sh

Update UNITREE_ROS2_SETUP_SH_PATH in config.py to match the actual path on your machine.

4. Configure config.py

At minimum, review and set:

  • ROSBRIDGE_IP / ROSBRIDGE_PORT β€” where rosbridge is running

  • MODE β€” "DDS" or "WEBRTC"

  • INTERFACE β€” your network interface for ROS 2 (ip a to find it)

  • ROBOT β€” a friendly name for your robot

  • Topic names (camera, cmd_vel, LIDAR, pose, odom, map, etc.) if they differ from your setup

  • UNITREE_ROS2_SETUP_SH_PATH and ROS_JS_LIB_PATH

5. (Optional) Set up .env for API keys

Rather than hard-coding credentials in config.py, create a .env file:

# .env
AZURE_API_KEY=your_key_here

config.py hard-coded keys are unsafe β€” load them via python-dotenv instead:

from dotenv import load_dotenv
import os
load_dotenv()
AZURE_API_KEY = os.getenv("AZURE_API_KEY")

6. Run the portal

python main.py

The Gradio app launches at http://0.0.0.0:7860 by default.


Configuration Reference

All settings live in config.py. Key groups:

Connection

Setting

Purpose

ROSBRIDGE_IP / ROSBRIDGE_PORT

Address of the rosbridge WebSocket server (default 127.0.0.1:9090)

MODE

Transport mode β€” "DDS" or "WEBRTC"

INTERFACE

Network interface used for ROS 2 DDS traffic

ROBOT

Display name for the connected robot

LLM / Scene Description

Setting

Purpose

LLM_MODE

"azure" or "ollama"

MODELS

Dict mapping mode β†’ friendly name β†’ LiteLLM model string

DEFAULT_MODEL

Default model per mode

AZURE_API_BASE / AZURE_OPENAI_DEPLOYMENT / AZURE_API_KEY / AZURE_API_VERSION

Azure OpenAI credentials (use .env, not literals)

OLLAMA_API_BASE / OLLAMA_API_KEY

Local Ollama endpoint (default http://localhost:11434)

SYSTEM_PROMPT / LLM_PROMPT

Prompts used for periodic scene description; the shipped example is tuned for factory-floor PPE/hazard detection

MCP_AGENT_PROMPT

System prompt for the MCP-connected agent, instructing it to use tools for robot state/control and never claim success without a confirmed tool result

UI Feature Flags

Setting

Purpose

SHOW_CAMERA / SHOW_TOPICS / SHOW_SERVICES / SHOW_CONTROLLER / SHOW_DESCRIPTION / SHOW_LIDAR

Toggle individual UI panels on/off

TTS_LANGUAGE

Language code for text-to-speech ("ja" by default in the sample config)

UPDATE_INTERVAL

Seconds between periodic scene-description calls

IMAGE_HEIGHT / IMAGE_WIDTH

Camera stream display dimensions

YOLO_MODE

"main" for the front camera or "rs" for RealSense as the YOLO detection source

Paths

Setting

Purpose

YOLO_MODEL

Path to YOLO weights (data/yolo/best2.pt)

SOUNDS_DIR

Directory for uploaded playback audio

WAYPOINT_FILE

JSON file storing saved navigation waypoints

UNITREE_ROS2_SETUP_SH_PATH

Path to the Unitree ROS 2 setup.sh

ROS_JS_LIB_PATH

Path to the JS library used for browser-side map/nav rendering

Topic Names β€” all remappable to match your robot's actual topic names: CAMERA_TOPIC_NAME, REALSENSE_CAMERA_COLOR, REALSENSE_CAMERA_DEPTH, CMD_VEL_PUB_TOPIC_NAME (+ _TYPE), LIDAR (+ LIDAR_MAX_POINTS), POSE (+ POSE_HEADER_FRAME_ID), ODOM, MAP, SPORTS, LFLOWCMD.

ROS Graph Styling β€” TOPIC_COLOR, PUBLISHER_COLOR, SUBSCRIBER_COLOR, NODE_SIZE, TOPIC_SIZE, PLOT_WIDTH, PLOT_HEIGHT control the appearance of the topic/service graph shown in the UI.


Running the MCP Portal

python main.py

This will:

  1. Initialize rclpy, instantiate all ROS 2 subscriber/publisher nodes defined in web_backend/, and register them on a shared executor.

  2. Launch the Gradio app with tabs for the main dashboard, actions/waypoints, and development diagnostics.

  3. Start the background loop that periodically grabs a camera frame, runs it through the configured LLM, and updates the on-screen scene description.


MCP Server & LLM Agent Integration

server.py starts an MCP server that mirrors the robot's ROS 2 surface as callable tools β€” battery/pose/telemetry reads, topic/service introspection, and movement/action commands. Any MCP-compatible client (a custom agent script, an IDE assistant, or a chat UI wired up with an MCP connector) can attach to it and:

  • List and inspect active ROS 2 topics and services

  • Read live telemetry (battery, pose, sport-mode state, sensors)

  • Issue movement or action commands through the exposed tools

  • Get grounded, tool-verified answers rather than the model guessing at robot state

The MCP_AGENT_PROMPT in config.py explicitly instructs the connected agent to rely on tool calls for anything robot-related and to never report success unless a tool call actually confirms it β€” useful guardrails when letting an LLM drive a physical robot.

To customize which model powers the natural-language side of the agent, add entries to MODELS in config.py using LiteLLM's model string format, e.g.:

MODELS = {
    "ollama": {
        "Gemma3": "ollama/gemma3:latest",
        "Llama3": "ollama/llama3:latest",   # ← new entry
    }
}

Accessing the Portal

Access Type

URL

Local (same machine)

http://localhost:7860

LAN (other devices)

http://<robot-host-ip>:7860


Extending the Project

Add a new ROS 2 subscriber

  1. Create a new subscriber class in web_backend/, following the pattern of an existing one (e.g. the camera subscribers).

  2. Instantiate it inside DataStream.__init__() in web_backend/data_stream.py.

  3. Register the node with the executor in main.py:

    executor.add_node(launcher.your_new_subscriber)
  4. Expose the data via a property or method on DataStream so the frontend can read it.

Add a new UI tab

  1. Create web_frontend/my_tab.py and define a get_my_tab_page(demo, launcher) function using Gradio components.

  2. Wire it into main.py inside the gr.Tabs() block:

    with gr.Tab("My Tab"):
        get_my_tab_page(demo, launcher)

Change the LLM scene-description prompt

Edit SYSTEM_PROMPT and LLM_PROMPT in config.py:

SYSTEM_PROMPT = "You are a robot assistant."
LLM_PROMPT = "Describe the scene and highlight any hazards."

Add a new LLM model β€” add an entry to the MODELS dict as shown above in the MCP section.


License & Acknowledgements

This project is intended for internal/research use. Please respect the licenses of its third-party dependencies, including Gradio, ROS 2, the Unitree SDK, and the MCP SDK. See the repository's LICENSE file for details.

Acknowledgements:

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables controlling a Unitree GO2 robot's motion (forward, backward, left, right, stop, stand up, and move with custom velocities) via MCP tools, using ROS2 commands.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for controlling Unitree robots via DDS protocol, supporting G1, Go2, H1, H2, B2, A2, R1 and more. Enables LLM agents to command robots with high-level actions and safety guards.
    3
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sallu-786/Unitree_Go2_Web_Portal'

If you have feedback or need assistance with the MCP directory API, please join our Discord server