TopicForge

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

v0.6.0 on PyPIPython 3.10 to 3.13MIT

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.

TopicForge 0.6.0 Cyclone DDS examples/dds/02_why_cant_they_talk
  1. user

    "The navigation planner subscribes to the lidar scan but never receives anything. Both programs run, the topic name and type are right. Why?"

  2. tool
    -> list_participants {}
    lidar_driver  (cyclone, active)
    nav_planner   (cyclone, active)
    topicforge    (cyclone, active, observer)
  3. 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
  4. 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"
      }]
    }
  5. answer

    nav_planner's reader on scan requests 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_topics and get_topic_info returned the same names, types and publisher/subscriber counts as the ros2 CLI.
  • analyze_bag returned 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

  1. Install

    pip install topicforge
    pip install "topicforge[dds]"      # adds the DDS tools (Cyclone DDS)

    Python 3.10 to 3.13.

  2. Try it without a robot

    TOPICFORGE_MODE=mock python -m topicforge

    Mock mode answers from fixtures. No robot needed.

  3. 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_ID only if your domain is not 0.

  4. Claude Code

    claude mcp add topicforge -- topicforge
  5. Modes

    TOPICFORGE_MODE is auto by default: live if ros2 is on PATH, else mock fixtures. Set it to live or mock to 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:

Professional integration offers and prices
OfferPriceScope and conditions
Integration Sprint 2 500 EURfixed price
Effort
5 days of work, delivered remotely over about 3 weeks.
Included
setup on the client's ROS 2 / DDS stack, 3 failure scenarios reproduced and diagnosed, compatibility matrix, written report, 1-hour handover session.
Excluded
travel, on-site work, write access to robots (TopicForge is read-only by design).

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.

In exchange
structured feedback and an anonymized case study we can publish.
Team Support 490 EURper month, 3-month minimum
  • Async support by email or issue, best-effort answer within 3 business days (no guaranteed SLA).
  • Help with ROS 2 / DDS upgrades, review of diagnostics, one scheduled call per month.
Enterprise On quote
Annual agreement
signed package, SBOM, maintained versions, no telemetry, path restrictions, validated middleware matrix, private skills.

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