Skip to main content

rl-mcp

Watch, diagnose and steer a live RL training run — from an AI agent, or from your shell.

Training a policy is a slow feedback loop. Launch, wait an hour, squint at TensorBoard, guess which reward weight was wrong, relaunch. rlmcp shortens it.

Attach it to a running job and you can ask the live process what the robot is doing, look at it, measure how jerky it is, change a reward weight, unlock harder terrain, and roll back if that made things worse. No restart.

Two backends ship: mjlab, which is what this is developed against, and IsaacLab. The simulator sits behind a small adapter, so a third is a couple of hundred lines rather than a fork.

⚠️ Experimental. The surface is real and tested, but it is still moving.


🚀 Start here

If you use a coding agent, give it one line:

Wrap my mjlab env with rlmcp (github.com/mktk1117/rl-mcp) and show me how to use it.

(Say IsaacLab instead if that is what you train on — the docs cover both.)

It reads the docs here, adds the wrapper to your training script, and drives the run from there.

If you are driving yourself, jump to Install and Try it.


What it looks like

rlmcp status                                   # iteration, stage, headline metrics
rlmcp diagnose --seconds 4                     # is the gait smooth? is it tracking?
rlmcp shot --where terrain=pyramid_stairs      # look at a robot on the stairs
rlmcp video --seconds 5                        # and one at 0, 50, 100, 200 … unasked
rlmcp view                                     # where the browser view is (a run has one)
rlmcp set reward.action_rate_l2.weight -0.25 --why "ankles chattering at 15 Hz"
rlmcp run set_terrain terrains='["flat","random_rough"]' max_level=4
rlmcp curriculum advance --why "flat is solved"
rlmcp checkpoint before-experiment             # and `rlmcp load` to undo
rlmcp events --interventions                   # everything anyone did, and why
rlmcp play                                     # watch a finished run's policy

Almost all of that is an MCP tool too, so an agent can drive the whole loop, and it sees the screenshots and plots, not just their file paths. play is the exception, and stays CLI-only because it opens a window.

The CLI knows who is reading. A terminal gets aligned tables, and pictures open by themselves. A pipe gets JSON, and each command's JSON is a parsing contract that does not move — the shapes are written down and pinned by tests, in docs/tools.md.

The whole integration

One call, between building the environment and building the runner.

from mjlab.envs import ManagerBasedRlEnv
import rlmcp

env = ManagerBasedRlEnv(cfg=env_cfg, device="cuda:0", render_mode="rgb_array")
env = rlmcp.wrap(env, session_dir=log_dir / "rlmcp", curriculum="terrain")

vec_env = RslRlVecEnvWrapper(env)
runner  = VelocityOnPolicyRunner(vec_env, agent_cfg, str(log_dir), device)
env.attach_runner(runner)          # PPO knobs, checkpoints, iteration boundaries

runner.learn(num_learning_iterations=agent_cfg.max_iterations)

That is it. Details and traps: docs/your-task.md.

The same one call on IsaacLab

IsaacLab launches its own app before anything else can be imported, so the wrap goes after gym.make. Downstream is the same CLI, the same MCP tools and the same JSON, minus what only mjlab can answer — the terrain commands, which an IsaacLab run reports as absent rather than broken.

env = gym.make(task, cfg=env_cfg, render_mode="rgb_array")
env = rlmcp_isaaclab.wrap(env, session_dir=log_dir / "rlmcp", task_id=task,
                          service_every_steps=agent_cfg.num_steps_per_env)

One ANYmal-D walking down pyramid stairs, picked out of 2048 training environments by rlmcp shot

rlmcp shot --env-id 300, taken mid-training from a stock Isaac-Velocity-Rough-Anymal-D-v0 run: one robot on the stairs, with the other 2047 environments carrying on behind it. Nothing about the task was described to rlmcp — 73 tunable knobs came out of walking the environment's own manager configs, the same walk it uses on mjlab. The example script is examples/train_isaaclab.py, and the things that differ — cameras, the robot's name, which Isaac Sim goes with which driver — are in docs/isaaclab.md.

What you get

Look at the robot Screenshots and clips of real training steps, not a separate eval. Pick which robot with --where terrain=stairs.
Watch it live Every run serves a browser view; rlmcp view says where. It needs no renderer, so it works on a headless box, and it costs nothing while nobody has the tab open — which is why it is on rather than a flag. The tab has mjlab's own panels: reward bars, term plots, contact and force overlays. Pause view (or rlmcp view --pause) freezes the tab and hands the run back its full speed, without giving the port back. --realtime buffers a window and plays it at the speed the robot actually moves, with mjlab's player in the tab.
Know why it moves badly diagnose measures jerk, chatter, effort, posture and gait, then says which lever to pull. When it cannot measure properly, it says so instead of guessing.
Tune anything, live 97 knobs on the G1 rough task, discovered from the environment. Reward weights, randomization ranges, PPO hyperparameters. Applied between rollout batches, never mid-step.
A ladder that drives itself Curriculum stages promote on earned conditions, not a schedule. Override any of it from a shell.
Undo Checkpoints save weights and parameters, stage and extension state.
Your task's own words Extensions add verbs, metrics and env selectors. They reach the CLI, MCP and curriculum stages at once, without editing rlmcp.
A record that survives Every change is logged with its reason, and every run stamps the code it launched with. Runs are kept as a graph with hypotheses and verdicts, so three failures in a row become one conclusion.
Look at a finished run rlmcp play restores the conditions a checkpoint trained under before rendering it. Otherwise a good policy looks broken.

A worked example: in-hand cube reorientation

A SharpaWave hand turning a cube to a commanded orientation anywhere in SO(3), then holding it there for a full second. This is the final policy, replayed from its checkpoint with rlmcp play:

A SharpaWave hand rotating a cube to match a commanded goal orientation

Ten runs to get there. Three worked, four were falsified, three were stopped early. That is the normal shape of RL work, which is why rlmcp keeps runs as a record instead of a folder of logs. rlmcp record graph writes one self-contained HTML page — no server, no build step — and it looks like this:

The rlmcp records page, story view, showing all ten cube reorientation runs

Each card is one run: the clip it produced, and the one line it concluded. Click any of them and the panel on the right shows what that run predicted, what would have proved it wrong, what changed since its parent, and what it measured.

Runs 005 to 007 are the interesting part. The policy froze, because standing still paid about 2600x more than making progress. Cheap drops broke the freeze and produced the mirror failure: the cube thrown off the palm within half a second. Re-pricing the trade in between failed too. Three failures, one conclusion: the reward was never the binding constraint. 008 changed the action interface instead — an EMA filter — and went from 1.1 to 16.8 goals per minute with the cube held 99.4% of the time.

That conclusion is not in any single run. It is in the shape of three siblings, which is the argument for keeping runs as a graph.

The same page has a tree view for the ancestry itself and a parameters view for what changed across runs. More in docs/records.md.

Install

pip install rl-mcp              # training side: numpy, matplotlib, pillow, imageio
pip install 'rl-mcp[server]'    # adds the MCP SDK for the agent side

uv pip install takes the same lines. For what is on main right now, or to skip installing altogether and run the CLI straight from GitHub:

pip install 'rl-mcp[server] @ git+https://github.com/mktk1117/rl-mcp'
uvx --from 'rl-mcp[server] @ git+https://github.com/mktk1117/rl-mcp' rlmcp sessions

A checkout installs as pip install -e '.[server]'.

The MCP SDK is optional on purpose. The training process does not need it, and the server does not need a simulator. Both mcp>=2 and mcp 1.x work.

The simulator is yours to install. rlmcp asks only that it be importable from the training process — for IsaacLab that means installing into the interpreter Isaac Sim runs under.

Register the server with Claude Code:

claude mcp add rlmcp -- rlmcp-server --root /path/to/logs

Try it

rlmcp tasks         # which ids exist here, before anything has run
# train, with the terrain ladder driving itself
rlmcp-train Mjlab-Velocity-Rough-Unitree-G1 --num-envs 4096

Then, from another shell:

rlmcp sessions      # what is running
rlmcp status
rlmcp curriculum
rlmcp diagnose --seconds 4

The explicit-curriculum version of the same run is in examples/train_g1_rough_curriculum.py. On IsaacLab, the equivalent first run is examples/train_isaaclab.py — same four commands afterwards.

Documentation

page for
docs/tools.md Every tool, one entry each. Shell command, MCP call, what comes back, and the traps. Start here for either audience.
docs/mcp-server.md Agents: setup, session pinning, liveness, deferred jobs, a worked steering session.
docs/tuning.md The tuning loop: verify the task first, watch the right numbers, read symptoms into levers. The distilled findings of two campaigns.
docs/your-task.md Putting rlmcp on your own task, in five steps.
docs/curriculum.md Writing the stage ladder.
docs/extensions.md Teaching rlmcp your task's vocabulary.
docs/isaaclab.md Driving an IsaacLab run: the one line, cameras, what differs from mjlab.
docs/records.md Hypotheses, verdicts, feedback, code snapshots, the record graph.
docs/design.md How it fits together, how parameters are found, other simulators.
docs/style.md The style guide: two spaces, what ruff checks, and why each rule is on or off.
AGENTS.md Contributing to this repository.

How it works, briefly

Two processes that never touch each other. They talk through a plain directory of JSON files.

   training process                session directory                 agent process
  torch + a simulator               plain JSON files                  no simulator
┌───────────────────┐             ┌─────────────────┐             ┌──────────────────┐
│ rlmcp-wrapped env │             │ status.json     │             │ MCP server       │
│ publishes metrics │ ──writes──► │ metrics.jsonl   │ ───reads──► │ (38 tools)       │
│ records frames    │             │ events.jsonl    │             │ rlmcp CLI        │
│ runs commands     │ ◄──reads─── │ artifacts/      │ ◄──writes── │ your own scripts │
│ between batches   │             │ inbox/  outbox/ │             │                  │
└───────────────────┘             └─────────────────┘             └──────────────────┘

Telemetry flows left to right. Commands flow right to left. Nobody shares memory, and nothing blocks the training loop.

That buys four things:

  • The agent side never imports torch or a simulator, so starting or killing it cannot disturb training.
  • Commands run between rollout batches, so an edit can never race the simulator.
  • Everything is inspectable with cat when something goes wrong.
  • A run leaves a complete, replayable record behind after it exits.

The same split is why a second backend was cheap. Parameter discovery, trace sampling, metrics and the wrapper are written against the shape mjlab and IsaacLab share; each adapter is only what is genuinely its own — how a robot is found in the scene and how a frame is rendered. More in docs/design.md.

Tests

pytest tests -q     # 855 tests, ~10s, no GPU and no simulator required

License

Apache-2.0 — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rl_mcp-0.1.1.tar.gz (750.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rl_mcp-0.1.1-py3-none-any.whl (622.1 kB view details)

Uploaded Python 3

File details

Details for the file rl_mcp-0.1.1.tar.gz.

File metadata

  • Download URL: rl_mcp-0.1.1.tar.gz
  • Upload date:
  • Size: 750.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for rl_mcp-0.1.1.tar.gz
Algorithm Hash digest
SHA256 3be25172e2c17f8418448d5d3713c46bb53050dcfc8a68268fcbbd6f2b21ac3c
MD5 3f86c19a8d9129a185b91e7a5a4e17fe
BLAKE2b-256 897532cf0433d2a4a1c8bdfb0890fa7da912501c2a1e5fd78d4d4afff58c00cc

See more details on using hashes here.

Provenance

The following attestation bundles were made for rl_mcp-0.1.1.tar.gz:

Publisher: publish.yml on mktk1117/rl-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rl_mcp-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: rl_mcp-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 622.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for rl_mcp-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3f3e5aeaab6af2cb6b7957b0942925ca78e8788aa811989c35d48571e8ee4e27
MD5 c610e06993587eab68a260619b6441f5
BLAKE2b-256 68524aa97a54296ec8799d7b1974a5d30eb6de45d267209d928c486c14790a63

See more details on using hashes here.

Provenance

The following attestation bundles were made for rl_mcp-0.1.1-py3-none-any.whl:

Publisher: publish.yml on mktk1117/rl-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page