Skip to main content
Glama
bazucas

webots-mcp

by bazucas

webots-mcp

Give AI agents full control and observability of the Webots robot simulator.

An MCP (Model Context Protocol) server that lets any MCP client — Claude Code, Codex, opencode, or your own agent — launch simulations, train reinforcement-learning models, watch what the robot is doing while it trains (without slowing training down), evaluate trained models, and manipulate the scene interactively.

Ask your agent things like:

"Start a 200k-step training run and tell me how it's going every few minutes." "Why did the reward drop? Show me a screenshot of what the robot is doing right now." "Evaluate the latest model for 10 episodes and compare it with yesterday's run." "Freeze the sim, put a box 25 cm in front of the robot, step 100 timesteps, and show me the sensor readings."

How it works

The Webots simulation state is only reachable from a controller inside the simulation loop — and during training, the robot's controller is the RL trainer. This server solves that with two independent channels that never touch the training hot path:

┌────────────── AI agent (any MCP client) ──────────────┐
│                 webots-mcp  (server.py)                │
└──────┬──────────────────┬──────────────────┬──────────┘
       │ JSON-TCP         │ files            │ subprocess
       ▼                  ▼                  ▼
  OBSERVER robot     runs/*/metrics.jsonl   webots / training
  (webots_bridge.py, (written by the SB3    (start, stop,
   2nd supervisor,    callback in           monitor)
   <extern>)          rl_metrics.py)
       │                  ▲
       ▼                  │
    Webots ◄────── training controller (untouched)
  1. Observer robot — an invisible second supervisor robot (synchronization FALSE) runs webots_bridge.py as an extern controller. It reads pose, velocity, scene objects and takes screenshots in parallel with training, costing ~3% fps.

  2. Metrics files — a Stable-Baselines3 callback (rl_metrics.py) appends one JSON line per episode (with per-component reward breakdown and termination reason) and per policy update (fps, losses). The server only reads files: training never waits for the agent.

  3. Process management — the server launches and stops Webots instances as subprocesses, each on its own port.

All state lives in files — killing the server loses nothing.

Quick start

Requirements: Python ≥ 3.10 and Webots (tested with R2025a on macOS; Linux/Windows install layouts are auto-detected via WEBOTS_HOME or PATH).

pip install git+https://github.com/bazucas/Webots_MCP        # installs the `webots-mcp` command
# with the RL extras (stable-baselines3 + gymnasium, for training/eval):
pip install "webots-mcp[rl] @ git+https://github.com/bazucas/Webots_MCP"

Or without installing anything, via uv:

uvx --from git+https://github.com/bazucas/Webots_MCP webots-mcp

Or clone and run from source: git clone ... && pip install -e ".[dev]".

Then register the server in your MCP client — the working directory must be your Webots project root (that's how the server finds worlds and runs/):

Claude Code

.mcp.json in the project root (cwd is the project automatically):

{
  "mcpServers": {
    "webots": { "command": "webots-mcp" }
  }
}

(If you didn't pip install, use "command": "python3", "args": ["/path/to/webots_mcp/webots_mcp/server.py"] — same for the clients below.)

opencode

opencode.json in the project root:

{
  "mcp": {
    "webots": { "type": "local", "command": ["webots-mcp"] }
  }
}

OpenAI Codex CLI (GPT)

~/.codex/config.toml (set WEBOTS_MCP_PROJECT since cwd may vary):

[mcp_servers.webots]
command = "webots-mcp"
env = { WEBOTS_MCP_PROJECT = "/home/you/my-webots-project" }

Cursor

.cursor/mcp.json in the project root — same shape as Claude Code's .mcp.json.

Claude Desktop / other MCP clients

Any client that speaks MCP over stdio works: command webots-mcp (or python3 /path/to/webots_mcp/webots_mcp/server.py). If the client doesn't launch it from your project directory (Claude Desktop doesn't), set WEBOTS_MCP_PROJECT in the server's env.

Restart/reconnect the client and approve the server — 23 tools appear.

Configuration

Everything is overridable with environment variables:

Variable

Meaning

Default

WEBOTS_MCP_PROJECT

project root

current working dir

WEBOTS_HOME

Webots installation

/Applications/Webots.app

WEBOTS_MCP_RL_WORLD

training world (.wbt)

<project>/thymio_rl_template/worlds/thymio_rl_env.wbt

WEBOTS_MCP_EVAL_WORLD

evaluation world (.wbt)

.../thymio_eval.wbt

WEBOTS_MCP_BRIDGE_WORLD

interactive world (.wbt)

.../thymio_bridge.wbt

Tools

Process management

Tool

What it does

webots_start(world?, mode?)

Launch a headless Webots instance; returns port, pid, log path

webots_stop(port)

Graceful SIGTERM (trainers save models in their finally)

webots_list()

Managed instances and liveness

Training

Tool

What it does

train_start(total_timesteps?, mode?)

Start a training run; returns run_id

train_status(run_id?)

timesteps done/target, fps, mean reward, termination breakdown, ETA, alive/finished

metrics_summary(run_id?, last_n?)

Aggregates of the last N episodes + per-component reward breakdown + first-vs-last trend

metrics_tail(run_id?, n?)

Raw last lines of metrics.jsonl

runs_list() / runs_compare(run_ids?)

All runs; side-by-side comparison

Live observation (during training)

Tool

What it does

observer_attach(port, robot_name?)

Connect the bridge — "observer" watches a training run; "" connects to the robot of an interactive world

sim_status()

Sim time, robot pose and velocity, obstacle inventory (including ones that fell off the arena)

sim_screenshot()

JPEG of the 3D view, returned inline to the agent

scene_query(def_name)

Fields of any scene node by DEF

observer_detach()

Disconnect

Evaluation

Tool

What it does

eval_run(algo?, episodes?, deterministic?)

Run a trained model N episodes; returns mean/min/max reward, episode lengths, terminations

Interactive control (world with the robot's controller set to <extern>)

Tool

What it does

sim_get_obs() / sim_set_motors(l, r)

Read sensors / drive motors

sim_pause(bool) / sim_step(n)

Deterministic stepping: freeze → act → step N → read

sim_set_pose() / sim_reset_robot()

Teleport / reset to initial pose

sim_spawn_box(x, y, z?, size?) / sim_clear_boxes()

Ad-hoc obstacles with physics

Plus the MCP prompt diagnose_training(run_id?) — a guided training-health analysis (trend, terminations, reward hacking signals, concrete shaping suggestions) — and resources webots://runs and webots://runs/{run_id}/metrics.

Adapting it to your project

The defaults reflect the project this was born in (Thymio II, ISCTE-IUL robotics course), but the pattern is generic. Your project needs:

  1. An observer robot in the training world (this is the whole trick — copy-paste):

Robot {
  name "observer"
  supervisor TRUE
  synchronization FALSE
  controller "<extern>"
}
  1. A DEF ROBOT on your main robot node, so the bridge can find it from outside.

  2. The metrics callback in your SB3 training script:

from rl_metrics import MetricsCallback
model.learn(total_timesteps=n, callback=MetricsCallback(run_name="ppo", runs_root="/path/to/project/runs"))

For per-component reward breakdowns, have your env put them in the info dict at episode end:

info["episode_metrics"] = {
    "termination_reason": "fall",           # your labels
    "steps": steps, "distance": d, "cells_visited": c,
    "reward_components": {"forward": 12.3, "cliff": -5.0},
}
  1. For train_start, your training controller should read TRAIN_TIMESTEPS from the environment; for eval_run, your evaluation controller should read EVAL_ALGO, EVAL_EPISODES, EVAL_DETERMINISTIC and write results to the JSON path in EVAL_OUT.

webots_bridge.py also works standalone without MCP — one JSON command per TCP connection on 127.0.0.1:10101:

printf '{"cmd":"get_obs"}\n' | nc -w 5 127.0.0.1 10101

Tests

pip install -e ".[dev]"
pytest tests -m "not webots"   # unit + MCP stdio protocol (~1 s, no Webots needed)
pytest tests -m webots         # integration against a real Webots (~20 s)
pytest tests                   # everything (37 tests)

CI (GitHub Actions) runs the fast suite on Linux, macOS and Windows for every push.

Integration tests need a real Webots project (WEBOTS_MCP_PROJECT); they skip cleanly if it's missing. Bridge unit tests inject a fake Webots API via sys.modules["controller"], so most of the suite runs anywhere.

Limitations & notes

  • Webots itself was only exercised on macOS (R2025a). The server code runs on Linux/Windows in CI, and install layouts for all three OSes are covered by unit tests, but end-to-end Webots runs on Linux/Windows are untested — PRs welcome.

  • The bridge listens on localhost only, with no authentication — don't expose the port.

  • One bridge connection at a time (BRIDGE_PORT, default 10101).

  • Avoid simulationReset in training code (it kills the observer's connection); reset by teleporting the robot instead.

License

MIT — do whatever you want with it.

-
license - not tested
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

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

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/bazucas/Webots_MCP'

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