ros2-inspector
ros2-inspector lets an LLM automatically analyze the runtime state of your ROS 2 system. It is an MCP server: ask "how is the system doing?" in natural language, and the model decides which ros2 inspection commands to run, in what order, and how to combine their outputs into a complete analysis.
┌────────────────────────────────────────────┐
│ MCP Host (Claude Code / ZCode / Qoder / │
│ Cursor / Claude Desktop) │
│ │ MCP protocol (stdio) │
└────────────────────┼───────────────────────┘
▼
┌─────────────────────────┐
│ ros2-inspector MCP │
│ pure Python · zero ROS │
│ dependency · read-only │
└────────────┬────────────┘
│ subprocess (auto-detected ROS env)
▼
ROS 2 system (DDS)
Why ros2-inspector?
Figuring out "what is the system doing right now" used to look like this:
ros2 node list→ a wall of nodes → pick one →ros2 node info→ spot a suspicious topic →ros2 topic info -v→ros2 topic echo→ros2 topic hz→ros2 param list… jumping back and forth between a dozen commands;- the command surface is huge and the syntax inconsistent — nobody memorizes it;
- at every step, you read the output and you connect the dots into "who talks to whom, and is the data healthy".
ros2-inspector hands the whole job to an LLM: say "analyze the current system state" once, and it runs the entire loop — overview → spot anomalies → drill down → summarize. Memorizing and composing commands, reading and correlating output: no longer your problem.
You: Analyze the current runtime state of my ROS 2 system.
Model: calls
system_overviewfor the big picture → notices/inspector_demo/chatterhas 0 subscribers → callsget_topic_infoto confirm → callssample_topic/get_topic_rateto verify the data itself is healthy → concludes: "One node is running normally, publishing at 6.1 Hz on/inspector_demo/chatter— but there are no subscribers, so the messages have no readers. Check whether the downstream node was started."
The guarantees that make this safe to actually run:
- Read-only, by construction → a four-layer safety fence; there is simply no entry point for "publish / call a service / set a param / launch a node";
- Zero ROS dependency → the server is pure Python, sources nothing at startup, and runs on machines without ROS 2 installed (it can even tell you whether ROS is installed and whether the machine is fit for it);
- Never blocks → streaming commands are time-boxed ("N messages or T seconds, whichever comes first"), killed on timeout with the partial output already captured;
- Distro-agnostic → auto-detects
/opt/ros/*at runtime; Humble / Jazzy / Lyrical with zero configuration; - Works with any MCP host → Claude Code, ZCode, Qoder, Cursor, Claude Desktop.
🎥 Examples in Action
📌 TODO: demo GIF here (a natural-language ROS 2 inspection session)
Example 1 — System inspection
You: Which nodes are running, and how are they connected?
Model: (calls
system_overview) The system has 1 node,/inspector_demo_talker, publishingstd_msgs/msg/Stringmessages on/inspector_demo/chatter. The topic currently has 0 subscribers…
Example 2 — Data content & rate
You: What does the data on
/inspector_demo/chatterlook like? Is the rate normal?Model: (calls
sample_topicfor 3 messages +get_topic_ratefor 3 seconds) The payload is an incrementing counter string —hello 279,hello 280… Measured average rate: 6.1 Hz…
Example 3 — A machine without ROS
You: Can this machine run ROS?
Model: (calls
machine_readiness) Ubuntu 24.04 — inside the official support matrix (Jazzy / Kilted / Lyrical); 16 GB RAM and 120 GB free disk both sufficient; hostname resolves, network interfaces present. Verdict: ready to install.
🛠 Quick Start
Prerequisites: uv — the only requirement. With ROS 2 on the machine the inspection tools work; without it the server still starts fine.
1. Install
cd ros2-inspector
uv sync
2. Configure your host (universal MCP JSON; adjust the --directory path to your checkout):
{
"mcpServers": {
"ros2-inspector": {
"command": "uv",
"args": ["run", "--directory", "/path/to/ros2-inspector", "ros2-inspector"]
}
}
}
Once published to PyPI this simplifies to
"command": "uvx", "args": ["ros2-inspector"]— path-independent; see the Roadmap. If your host cannot finduv(GUI apps with a restricted PATH), use the absolute path to the uv binary (e.g./home/you/.local/bin/uv) in thecommandfield.
| Host | Where to put it |
|---|---|
| ZCode / Claude Code | .mcp.json at the workspace root, or each app's MCP settings |
| Claude Desktop | the mcpServers block of claude_desktop_config.json |
| Qoder / Cursor | Settings → MCP → add server, paste the same JSON |
3. Ask away
"Analyze the current system state" · "Which nodes are running?" · "What does the data on /chatter look like?" · "Is the environment healthy?"
(Optional) Self-test: see the tests/ directory (safety-fence tests, end-to-end smoke test, and a demo publisher script).
📦 Tools (13, all read-only)
| Tool | Needs ROS? | What it answers |
|---|---|---|
ros_install_info |
No | Is ROS installed? Which distro, where? |
machine_readiness |
No | Is this machine fit to install/run ROS (OS/RAM/disk/network vs. the support matrix)? |
system_overview |
Yes | One-page snapshot: nodes + topics + wiring + counts |
list_nodes / get_node_info |
Yes | Which nodes are running / what a node publishes & subscribes |
list_topics / get_topic_info |
Yes | Which topics exist & their types / type, endpoint counts, QoS |
sample_topic / get_topic_rate |
Yes | What the data looks like (time-boxed) / publish rate (time-boxed) |
params |
Yes | A node's parameter list / a parameter's current value |
actions |
Yes | Action list / one action's detail |
show_interface |
Yes | Field definitions of a message/service/action type |
health_check |
Yes | Environment health: daemon status + doctor verdict |
Protocol primitives: Tools only (works everywhere). Resources are deliberately omitted (our data is live and dynamic — Tool is the right primitive), as are Prompts (the analysis playbook lives in the tool descriptions; revisit in v1.5).
🔒 Safety Design
Read-only is designed, not promised. Four defense layers, centralized in the single executor (runner.py):
- Registration layer: only the 13 read-only functions exist; mutating code is absent from the codebase;
- Allowlist layer: subcommands allowlisted down to "verb + subverb" (13 groups); a banned-token denylist (
pub/call/set/launch/run/pkg…) as backstop; - Validation layer: only flags, ROS names, interface types and numbers pass the format allowlist;
;|&`$are always rejected;shell=Falsethroughout; - Runtime layer: timeout + process-group kill; every call is audit-logged (
audit.log).
❓ Troubleshooting
| Symptom | Fix |
|---|---|
| Tools report "no ROS 2 environment detected" | Confirm with ros_install_info; check that /opt/ros/<distro>/setup.bash exists |
list_nodes empty but the robot is clearly running |
ROS_DOMAIN_ID mismatch between machines; stale daemon → ros2 daemon stop && ros2 daemon start |
sample_topic returns no messages |
The topic may have no publishers; check the publisher count with get_topic_info first |
| "topic/node not found" errors | Names must start with / and are case-sensitive; confirm exact names via list_topics/list_nodes first |
health_check reports an incomplete verdict |
doctor includes network checks and can take up to ~30 s; on slow networks the verdict may be truncated |
🗺 Roadmap
- v1.5: diagnostic Prompt templates (one-click system checkup); read-only
service list/type - v2: PyPI release (
uvx ros2-inspector, path-independent); Docker packaging (zero host dependencies)
🤝 Contributing
Issues and PRs are welcome: new tool suggestions (read-only only), host-configuration feedback, doc improvements.
📜 License
MIT — Copyright (c) 2026 XuChen
Release files for ros2-inspector-mcp 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ros2_inspector_mcp-0.1.1.tar.gz | 91.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ros2_inspector_mcp-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 116.8 kB
Release files / ros2_inspector_mcp-0.1.1.tar.gz
| Download URL | ros2_inspector_mcp-0.1.1.tar.gz |
|---|---|
| Size | 91.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
27482ea59a5f79342210a5bdad1fe867b7848855d55957d737eee71d2bf1c1a8
|
|
BLAKE2b-256 checksum How to use checksums |
60e719a0c6519929ddf37d3e2215949584ce355e0ffc974055a05c64de0ba997
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / ros2_inspector_mcp-0.1.1-py3-none-any.whl
| Download URL | ros2_inspector_mcp-0.1.1-py3-none-any.whl |
|---|---|
| Size | 25.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a796c8168c37363ab5bcc4e865c01370a4b5248d3903b5c353ed8904bebee395
|
|
BLAKE2b-256 checksum How to use checksums |
b859199dadc60c19eadcd9a606847f9748b85c3e68cebdd6358467911ea2dfdd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|