Let an AI assistant inspect your ROS 2 robot and its DDS bus.
TopicForge is an open source MCP server. Its 12 tools read ROS 2 topics, bags and the DDS bus, and explain why readers and writers do not talk. It is read-only by architecture: no code path publishes to the robot or commands it.
Install
pip install topicforge
A real diagnosis
A QoS mismatch, captured on 2026-10-05 on a real Cyclone DDS bus. The calls and outputs are real, trimmed to the useful fields. The final answer is written from these outputs.
-
user
"The navigation planner subscribes to the lidar scan but never receives anything. Both programs run, the topic name and type are right. Why?"
-
tool
-> list_participants {}
lidar_driver (cyclone, active) nav_planner (cyclone, active) topicforge (cyclone, active, observer)
-
tool
-> list_endpoints {"topic": "scan"}
writer lidar_driver type LidarScan reliability BEST_EFFORT durability VOLATILE reader nav_planner type LidarScan reliability RELIABLE durability VOLATILE by_topic: scan, 1 writer, 1 reader, same type, same partition
-
tool
-> detect_qos_mismatches {"topic": "scan"}
{ "topic": "scan", "writer_participant_name": "lidar_driver", "reader_participant_name": "nav_planner", "severity": "incompatible", "details": [{ "policy": "Reliability", "requested": "RELIABLE", "offered": "BEST_EFFORT", "rule": "a RELIABLE reader needs a RELIABLE writer" }] } -
answer
nav_planner's reader on
scanrequests RELIABLE, but lidar_driver's writer only offers BEST_EFFORT. DDS never matches that pair and raises no error, so the planner silently receives nothing. Either make the lidar writer RELIABLE, or make the planner's reader BEST_EFFORT (usual for high-rate sensor data). Nothing else blocks the pair: same type, same partition, durability compatible.
Three calls. TopicForge is the third participant in the list, as an observer. The example is in the repository under examples/dds/02_why_cant_they_talk.
The 12 tools
Every tool observes. None of them writes to the robot.
ROS 2 3 tools
- list_topics
- topics of the ROS 2 graph with their types and publisher/subscriber counts.
- get_topic_info
- one ROS 2 topic in detail, including the publishers' reliability and durability.
- sample_messages
- read the latest messages on a ROS 2 topic, decoded into named fields.
Bags 2 tools
- analyze_bag
- summarize a recording (.mcap, .db3, .bag): topics, counts, real per-topic rates.
- peek_bag_samples
- read decoded messages out of a recording.
DDS 6 tools
- list_participants
- the DDS participants (programs) on the bus, with name, vendor and status.
- list_endpoints
- every DDS writer and reader, with its topic, type and full QoS, and topics with no reader or no writer.
- detect_qos_mismatches
- why readers and writers on the same topic do not talk, with the requested and offered values.
- participant_events
- when participants joined and left the bus (crashes, restarts).
- peek_dds_samples
- raw DDS discovery records and samples on a DDS topic.
- topic_metrics
- observed frequency, sequence gaps and latency for a DDS topic.
Health 1 tool
- health_check
- what TopicForge sees right now (live or mock, ROS 2 and DDS backends, domain, version).
What is tested, and what is not
Evidence
- About 970 automated tests.
- A ROS 2 Humble and Jazzy test bench in Docker, run on every relevant change.
- Blind evaluations: AI agents with only the TopicForge tools diagnosed planted faults. 16/16 on the DDS scenarios.
- In the same blind evaluations, correct lidar distances and frequencies on a real ROS 2 robot.
Known limits
- DDS observation is tested live with Cyclone DDS and Fast DDS participants.
- The Fast DDS backend itself has never run on a real bus.
- DDS Security is not supported.
- DDS payloads of user topics are not decoded. The ROS 2 side decodes them.
- A writer that is stuck but still alive is not detectable yet.
External validation: OmniSim (October 2026)
The OmniSim team ran TopicForge against a simulated robot and compared its answers with the simulator's ground truth.
Setup
OmniSim v9.1.3, a simulated Clearpath Husky with a 541-beam SICK LMS111 lidar in a room with walls at known positions, robot stationary. ROS 2 Humble on Ubuntu 22.04 (WSL2), Fast DDS. TopicForge 0.5.3 in live mode for the ROS 2 tools, and 0.5.5 with the Cyclone backend for the DDS tools.
What matched
- Every lidar scan TopicForge sampled was bit-identical to the simulator's raw data on the beams it delivered.
list_topicsandget_topic_inforeturned the same names, types and publisher/subscriber counts as the ros2 CLI.analyze_bagreturned the same duration and message counts as ros2 bag info and an independent read of the bag.- With the Cyclone backend, TopicForge saw all seven Fast DDS participants of the ROS 2 graph without extra configuration, and reported participants leaving within about 10 ms of the actual time.
What it found
Seven discrepancies on TopicForge's side, including lidar arrays cut at 128 values, one message per sample_messages call, zero timestamps on simulated time, and Humble bags that peek_bag_samples could not decode.
All seven are addressed in 0.5.6 and 0.6.0 (full arrays through a max_array_length option) and covered by a ROS 2 Humble/Jazzy test bench; the OmniSim run itself has not been repeated on these versions yet.
The bag recorded during the run is part of TopicForge's test fixtures.
Install and configure
-
Install
pip install topicforge pip install "topicforge[dds]" # adds the DDS tools (Cyclone DDS)Python 3.10 to 3.13.
-
Try it without a robot
TOPICFORGE_MODE=mock python -m topicforgeMock mode answers from fixtures. No robot needed.
-
Claude Desktop
In
claude_desktop_config.json:{ "mcpServers": { "topicforge": { "command": "python", "args": ["-m", "topicforge"], "env": { "TOPICFORGE_MODE": "auto" } } } }For DDS, add to
env"TOPICFORGE_DDS_BACKEND": "cyclone", "TOPICFORGE_DDS_DOMAIN_ID": "<domain>"Set
TOPICFORGE_DDS_DOMAIN_IDonly if your domain is not 0. -
Claude Code
claude mcp add topicforge -- topicforge -
Modes
TOPICFORGE_MODEisautoby default: live ifros2is on PATH, else mock fixtures. Set it toliveormockto choose.
Professional integration
TopicForge is free and open source (MIT) and stays that way. If your team needs it validated on its own ROS 2 / DDS stack, I offer:
| Offer | Price | Scope and conditions |
|---|---|---|
| Integration Sprint | 2 500 EURfixed price |
Multi-vendor stacks (RTI with your license, segmented networks, DDS Security): from 5 000 EUR, on quote. |
| Design partner | 1 500 EURfixed price, first 3 teams only |
Same scope as the Integration Sprint.
|
| Team Support | 490 EURper month, 3-month minimum |
|
| Enterprise | On quote |
|
Prices are net. VAT not applicable, art. 293 B of the French CGI.
Contact: ethvignot.yanis@gmail.com
Project
- Version0.6.0, on PyPI (2026-10-05)
- MCP Registryio.github.yaniswav/topicforge
- External validationOmniSim, October 2026
- Claude plugin listingnot yet submitted
- Sourcegithub.com/yaniswav/TopicForge
- Packagepypi.org/project/topicforge
- ChangesChangelog
- Bugs and questionsGitHub issues
- Contactethvignot.yanis@gmail.com
- LicenseMIT