topicforge 0.6.0__tar.gz → 0.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. {topicforge-0.6.0 → topicforge-0.6.2}/CHANGELOG.md +56 -1
  2. {topicforge-0.6.0 → topicforge-0.6.2}/PKG-INFO +10 -4
  3. {topicforge-0.6.0 → topicforge-0.6.2}/README.md +9 -3
  4. topicforge-0.6.2/docs/CLIENTS.md +280 -0
  5. {topicforge-0.6.0 → topicforge-0.6.2}/docs/TESTING.md +3 -3
  6. topicforge-0.6.2/docs/VALIDATION.md +15 -0
  7. topicforge-0.6.2/plugin/LICENSE +21 -0
  8. topicforge-0.6.2/plugin/README.md +60 -0
  9. {topicforge-0.6.0 → topicforge-0.6.2}/pyproject.toml +2 -2
  10. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/__init__.py +1 -1
  11. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/cdr_decoder.py +11 -1
  12. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/composite.py +5 -0
  13. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/adapter.py +10 -0
  14. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/ros2_mock/fixtures.py +12 -5
  15. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/models/schemas.py +30 -5
  16. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/services/bag_service.py +17 -5
  17. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/services/health.py +16 -0
  18. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/tools/handlers.py +6 -1
  19. {topicforge-0.6.0 → topicforge-0.6.2}/tests/integration/ros2/test_live_adapter.py +24 -0
  20. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_bag_omnisim_humble.py +16 -0
  21. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_cdr_decoder.py +7 -0
  22. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_health.py +56 -0
  23. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_live_sample_notes.py +3 -1
  24. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_mock_adapter.py +10 -0
  25. topicforge-0.6.2/tests/test_version_consistency.py +150 -0
  26. {topicforge-0.6.0 → topicforge-0.6.2}/.gitignore +0 -0
  27. {topicforge-0.6.0 → topicforge-0.6.2}/LICENSE +0 -0
  28. {topicforge-0.6.0 → topicforge-0.6.2}/docs/DDS_QUICKSTART.md +0 -0
  29. {topicforge-0.6.0 → topicforge-0.6.2}/docs/TROUBLESHOOTING.md +0 -0
  30. {topicforge-0.6.0 → topicforge-0.6.2}/docs/TUTORIEL.md +0 -0
  31. {topicforge-0.6.0 → topicforge-0.6.2}/docs/dds-interop-matrix.md +0 -0
  32. {topicforge-0.6.0 → topicforge-0.6.2}/examples/README.md +0 -0
  33. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/00_hello_pub_sub/README.md +0 -0
  34. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/01_who_is_on_the_bus/README.md +0 -0
  35. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/02_why_cant_they_talk/README.md +0 -0
  36. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/03_a_node_crashed/README.md +0 -0
  37. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/04_late_joiner_misses_data/README.md +0 -0
  38. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/05_reliability_in_code/README.md +0 -0
  39. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/06_durability_late_joiner_in_code/README.md +0 -0
  40. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/07_deadline_in_code/README.md +0 -0
  41. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/08_crash_seen_from_inside/README.md +0 -0
  42. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/10_lidar_silent_after_driver_swap/README.md +0 -0
  43. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/11_who_talks_to_whom/README.md +0 -0
  44. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/12_safety_monitor_dropout/README.md +0 -0
  45. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/13_deadline_not_offered/README.md +0 -0
  46. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/14_restart_loop/README.md +0 -0
  47. {topicforge-0.6.0 → topicforge-0.6.2}/examples/dds/README.md +0 -0
  48. {topicforge-0.6.0 → topicforge-0.6.2}/scripts/agent_eval/README.md +0 -0
  49. {topicforge-0.6.0 → topicforge-0.6.2}/scripts/integration/README.md +0 -0
  50. {topicforge-0.6.0 → topicforge-0.6.2}/scripts/integration/publishers/cyclone_c/README.md +0 -0
  51. {topicforge-0.6.0 → topicforge-0.6.2}/scripts/integration/publishers/cyclone_cpp/README.md +0 -0
  52. {topicforge-0.6.0 → topicforge-0.6.2}/scripts/integration/publishers/cyclone_rust/README.md +0 -0
  53. {topicforge-0.6.0 → topicforge-0.6.2}/scripts/integration/publishers/dust_py/README.md +0 -0
  54. {topicforge-0.6.0 → topicforge-0.6.2}/scripts/integration/publishers/fast_publisher_cpp/README.md +0 -0
  55. {topicforge-0.6.0 → topicforge-0.6.2}/scripts/integration/publishers/fast_py/README.md +0 -0
  56. {topicforge-0.6.0 → topicforge-0.6.2}/scripts/integration/publishers/opensplice_publisher/README.md +0 -0
  57. {topicforge-0.6.0 → topicforge-0.6.2}/scripts/integration/publishers/rti_c/README.md +0 -0
  58. {topicforge-0.6.0 → topicforge-0.6.2}/scripts/integration/publishers/rti_cpp/README.md +0 -0
  59. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/__main__.py +0 -0
  60. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/__init__.py +0 -0
  61. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/base.py +0 -0
  62. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/__init__.py +0 -0
  63. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/dds_helpers.py +0 -0
  64. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/dds_introspection.py +0 -0
  65. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/discovery_tracker.py +0 -0
  66. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/endpoints.py +0 -0
  67. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/lifecycle.py +0 -0
  68. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/metrics_buffer.py +0 -0
  69. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/qos_analyzer.py +0 -0
  70. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/qos_endpoints.py +0 -0
  71. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/qos_normalize.py +0 -0
  72. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/qos_scan.py +0 -0
  73. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/topic_filter.py +0 -0
  74. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/common/xtypes.py +0 -0
  75. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/dds_cyclone/__init__.py +0 -0
  76. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/dds_cyclone/adapter.py +0 -0
  77. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/dds_dust/__init__.py +0 -0
  78. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/dds_dust/adapter.py +0 -0
  79. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/dds_fast/__init__.py +0 -0
  80. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/dds_fast/adapter.py +0 -0
  81. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/dds_opendds/__init__.py +0 -0
  82. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/dds_opendds/adapter.py +0 -0
  83. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
  84. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/echo_parser.py +0 -0
  85. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/echo_stream.py +0 -0
  86. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/parsers.py +0 -0
  87. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/process_tree.py +0 -0
  88. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
  89. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/adapters/ros2_mock/adapter.py +0 -0
  90. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/config/__init__.py +0 -0
  91. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/config/settings.py +0 -0
  92. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/constants.py +0 -0
  93. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/models/__init__.py +0 -0
  94. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/server/__init__.py +0 -0
  95. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/server/app.py +0 -0
  96. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/services/__init__.py +0 -0
  97. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/services/bag_stats.py +0 -0
  98. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/services/factory.py +0 -0
  99. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/services/inspector.py +0 -0
  100. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/services/sample_budget.py +0 -0
  101. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/telemetry/__init__.py +0 -0
  102. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/telemetry/client.py +0 -0
  103. {topicforge-0.6.0 → topicforge-0.6.2}/src/topicforge/tools/__init__.py +0 -0
  104. {topicforge-0.6.0 → topicforge-0.6.2}/tests/__init__.py +0 -0
  105. {topicforge-0.6.0 → topicforge-0.6.2}/tests/conftest.py +0 -0
  106. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_echo/image_noarr.yaml +0 -0
  107. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_echo/image_trunc4.yaml +0 -0
  108. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_echo/scan_edge.yaml +0 -0
  109. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_echo/scan_noarr.yaml +0 -0
  110. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_echo/scan_trunc128.yaml +0 -0
  111. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_echo/scan_trunc3.yaml +0 -0
  112. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_echo/string_latched.yaml +0 -0
  113. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_echo/twist.yaml +0 -0
  114. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_topic_info_verbose_cmd_vel.txt +0 -0
  115. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_topic_info_verbose_parameter_events.txt +0 -0
  116. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_topic_info_verbose_scan.txt +0 -0
  117. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_topic_info_verbose_tf.txt +0 -0
  118. {topicforge-0.6.0 → topicforge-0.6.2}/tests/fixtures/ros2_topic_info_verbose_tf_static.txt +0 -0
  119. {topicforge-0.6.0 → topicforge-0.6.2}/tests/integration/__init__.py +0 -0
  120. {topicforge-0.6.0 → topicforge-0.6.2}/tests/integration/ros2/Dockerfile +0 -0
  121. {topicforge-0.6.0 → topicforge-0.6.2}/tests/integration/ros2/__init__.py +0 -0
  122. {topicforge-0.6.0 → topicforge-0.6.2}/tests/integration/ros2/entrypoint.sh +0 -0
  123. {topicforge-0.6.0 → topicforge-0.6.2}/tests/integration/ros2/publisher.py +0 -0
  124. {topicforge-0.6.0 → topicforge-0.6.2}/tests/integration/ros2/run_bench.py +0 -0
  125. {topicforge-0.6.0 → topicforge-0.6.2}/tests/integration/test_real_bus.py +0 -0
  126. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_analyze_bag_multi_format.py +0 -0
  127. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_bag_service.py +0 -0
  128. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_bag_stats.py +0 -0
  129. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_composite_adapter.py +0 -0
  130. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_config.py +0 -0
  131. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_cyclone_adapter.py +0 -0
  132. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_dds_cross_vendor.py +0 -0
  133. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_dds_helpers.py +0 -0
  134. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_dds_inactive_reason.py +0 -0
  135. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_dds_introspection.py +0 -0
  136. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_dds_qos_normalization.py +0 -0
  137. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_dds_schemas.py +0 -0
  138. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_discovery_tracker.py +0 -0
  139. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_dust_adapter.py +0 -0
  140. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_echo_parser.py +0 -0
  141. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_echo_stream.py +0 -0
  142. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_endpoints.py +0 -0
  143. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_example_node_spec.py +0 -0
  144. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_factory.py +0 -0
  145. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_fast_adapter.py +0 -0
  146. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_history_and_hints.py +0 -0
  147. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_honest_outputs.py +0 -0
  148. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_inspector.py +0 -0
  149. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_lifecycle_buffer.py +0 -0
  150. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_live_adapter_graph.py +0 -0
  151. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_live_adapter_parse.py +0 -0
  152. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_live_adapter_subprocess.py +0 -0
  153. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_metrics_buffer.py +0 -0
  154. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_opendds_adapter.py +0 -0
  155. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_peek_bag_samples.py +0 -0
  156. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_process_tree.py +0 -0
  157. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_qos_analyzer.py +0 -0
  158. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_qos_endpoints.py +0 -0
  159. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_qos_scan.py +0 -0
  160. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_sample_options.py +0 -0
  161. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_telemetry.py +0 -0
  162. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_tools_integration.py +0 -0
  163. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_topic_metrics.py +0 -0
  164. {topicforge-0.6.0 → topicforge-0.6.2}/tests/test_xtypes.py +0 -0
@@ -5,6 +5,59 @@ All notable changes to TopicForge are documented in this file.
5
5
  The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [0.6.2] - 2026-10-06
11
+
12
+ ### Added
13
+
14
+ - [docs/CLIENTS.md](docs/CLIENTS.md): ready-to-paste local stdio configs for Claude
15
+ Code, Claude Desktop, Cursor, VS Code / Copilot, Windsurf / Devin, Cline, Roo Code,
16
+ Continue, Zed, JetBrains, Codex CLI, Gemini CLI, Goose, LM Studio, Amazon Q / Kiro
17
+ and Warp, with one-click install links for Cursor and VS Code, and a note on why
18
+ ChatGPT web and a hosted endpoint are out of scope. Linked from the README.
19
+ - `gemini-extension.json` and `GEMINI.md`: install in Gemini CLI with
20
+ `gemini extensions install https://github.com/yaniswav/TopicForge`.
21
+ - `mcpb/`: an MCP Bundle (`uv` server type, no vendored dependencies) for one-click
22
+ Claude Desktop install. The release workflow packs it and attaches it to the GitHub
23
+ release.
24
+ - `publish.yml` publishes `server.json` to the official MCP Registry after the PyPI
25
+ publish succeeds (pinned, checksum-verified `mcp-publisher`, GitHub OIDC), so the
26
+ registry no longer lags behind PyPI.
27
+ - `tests/test_version_consistency.py`: fails when the package, `server.json`, plugin,
28
+ Gemini extension, MCP Bundle and the pins in `docs/CLIENTS.md` disagree.
29
+
30
+ ## [0.6.1] - 2026-10-06
31
+
32
+ ### Added
33
+
34
+ - A Claude plugin bundle in `plugin/`: the TopicForge MCP server (started with
35
+ `uvx`, version pinned) and two skills, `diagnose-dds-bus` and
36
+ `inspect-ros2-robot`. Install with `claude plugin marketplace add
37
+ yaniswav/TopicForge`.
38
+ - [docs/VALIDATION.md](docs/VALIDATION.md): the OmniSim team's external validation
39
+ against a simulated robot's ground truth, published as approved by them.
40
+ - `MessageSample.recorded_ns`: the bag record time of a `peek_bag_samples` sample.
41
+ - `MessageSample.stamp_source` value `recorded`: `timestamp_ns` is the bag record
42
+ time because the message has no top-level header.
43
+ - `HealthReport.sim_clock_published` (live ROS 2 only, else null): whether `/clock`
44
+ has a publisher, a hint that nodes may run on simulated time.
45
+
46
+ ### Changed
47
+
48
+ - `peek_bag_samples` `timestamp_ns` is now `header.stamp` when the message has a
49
+ top-level header (`stamp_source` `header`), else the bag record time (`stamp_source`
50
+ `recorded`). It used to be the bag record time for every message, with
51
+ `stamp_source` null. The tool description says it returns the first `count`
52
+ messages, not the last.
53
+ - CI: `actions/checkout` v7, `actions/setup-python` v7, `actions/cache` v6. Jobs
54
+ that run tests or the demos use `ubuntu-24.04` instead of `ubuntu-latest`.
55
+
56
+ ### Fixed
57
+
58
+ - `peek_bag_samples` payloads no longer carry `__msgtype__` in every nested object
59
+ nor a top-level `_msgtype`; the message type stays at the sample level.
60
+
8
61
  ## [0.6.0] - 2026-10-05
9
62
 
10
63
  `sample_messages` (live) rewritten after the OmniSim team's report (count
@@ -1263,7 +1316,9 @@ Initial MVP release of TopicForge: ROS Topic Inspector & Bag Analyzer MCP server
1263
1316
  - The write path (publishing, commanding robots) is intentionally out of scope for the MVP.
1264
1317
  - `analyze_bag` in live mode parses `ros2 bag info` text output; deeper anomaly detection remains mock-only for now.
1265
1318
 
1266
- [Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.6.0...HEAD
1319
+ [Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.6.2...HEAD
1320
+ [0.6.2]: https://github.com/yaniswav/TopicForge/compare/v0.6.1...v0.6.2
1321
+ [0.6.1]: https://github.com/yaniswav/TopicForge/compare/v0.6.0...v0.6.1
1267
1322
  [0.6.0]: https://github.com/yaniswav/TopicForge/compare/v0.5.6...v0.6.0
1268
1323
  [0.5.6]: https://github.com/yaniswav/TopicForge/compare/v0.5.5...v0.5.6
1269
1324
  [0.5.5]: https://github.com/yaniswav/TopicForge/compare/v0.5.4...v0.5.5
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: topicforge
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: ROS Topic Inspector & Bag Analyzer MCP server for AI agents
5
5
  Project-URL: Homepage, https://github.com/yaniswav/TopicForge
6
6
  Project-URL: Repository, https://github.com/yaniswav/TopicForge
@@ -98,7 +98,11 @@ The server speaks MCP over stdio and waits for a client, so wire it into one. Fo
98
98
  }
99
99
  ```
100
100
 
101
- Then ask it to list the topics or to analyze `/tmp/demo.mcap`. For Claude Code: `claude mcp add topicforge -- topicforge`. Setup for a real ROS2 environment (WSL2, Linux, Docker, native Windows) is in [`docs/TESTING.md`](docs/TESTING.md); recurring monitoring prompts and the privacy contract are in [`docs/TUTORIEL.md`](docs/TUTORIEL.md).
101
+ Then ask it to list the topics or to analyze `/tmp/demo.mcap`. For Claude Code: `claude mcp add topicforge -- topicforge`.
102
+
103
+ **Other MCP clients.** Ready-to-paste configs for Claude Desktop, Cursor, VS Code, Windsurf, Cline, Roo Code, Continue, Zed, JetBrains, Codex, Gemini CLI, Goose and more are in [`docs/CLIENTS.md`](docs/CLIENTS.md). One-click: [Add to Cursor](https://cursor.com/install-mcp?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlW2Rkc109PTAuNi4yIiwidG9waWNmb3JnZSJdLCJlbnYiOnsiVE9QSUNGT1JHRV9NT0RFIjoiYXV0byIsIlRPUElDRk9SR0VfRERTX0JBQ0tFTkQiOiJjeWNsb25lIiwiVE9QSUNGT1JHRV9ERFNfRE9NQUlOX0lEIjoiMCJ9fQ%3D%3D) | [Install in VS Code](https://vscode.dev/redirect/mcp/install?name=topicforge&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22topicforge%5Bdds%5D%3D%3D0.6.2%22%2C%22topicforge%22%5D%2C%22env%22%3A%7B%22TOPICFORGE_MODE%22%3A%22auto%22%2C%22TOPICFORGE_DDS_BACKEND%22%3A%22cyclone%22%2C%22TOPICFORGE_DDS_DOMAIN_ID%22%3A%220%22%7D%7D).
104
+
105
+ Setup for a real ROS2 environment (WSL2, Linux, Docker, native Windows) is in [`docs/TESTING.md`](docs/TESTING.md); recurring monitoring prompts and the privacy contract are in [`docs/TUTORIEL.md`](docs/TUTORIEL.md).
102
106
 
103
107
  ## Tools
104
108
 
@@ -158,6 +162,8 @@ Samples with comments are in [`.env.example`](.env.example). Any invalid value s
158
162
 
159
163
  ## Limitations
160
164
 
165
+ External validation against a simulated robot's ground truth: [docs/VALIDATION.md](docs/VALIDATION.md).
166
+
161
167
  - DDS validation is partial. The Cyclone adapter has run against a real bus, with Cyclone and Dust DDS participants, on Windows and in CI on Ubuntu and Windows (`.github/workflows/demo.yml`). The Fast DDS adapter has never run against a bus, and no RTI, OpenDDS, CoreDX or OpenSplice participant has been observed by this project. The multi-vendor claim rests on the RTPS protocol guarantee, not on a recorded cross-vendor run.
162
168
  - User-topic payloads are not decoded. `peek_dds_samples` on a user topic returns count 0 and a note that the topic is announced on the bus; no traffic is read. `topic_metrics` therefore has data only for the builtin discovery topics and says so in its `status`. It is a discovery-layer probe, not a publish-rate monitor.
163
169
  - Liveliness at runtime is not observed. A writer that is alive but silent (a hung process whose lease is still renewed) looks healthy, because TopicForge reads discovery, not data. An opt-in data probe is planned. A crash and a clean leave cannot be told apart, and `lost_ns` is an upper bound of the death.
@@ -166,7 +172,7 @@ Samples with comments are in [`.env.example`](.env.example). Any invalid value s
166
172
  - DDS Security is not handled. A participant without credentials sees an empty secure bus. `detect_qos_mismatches` checks Partition, type name, Reliability, Durability, Deadline, Liveliness, LatencyBudget, Ownership (kind), DestinationOrder and DataRepresentation (History as a risk); Presentation, XTypes assignability and runtime behavior are not checked, and the result lists them in `policies_unchecked`. It returns a `MismatchScan` envelope: read `reports` for the mismatches.
167
173
  - Fast DDS serves no `list_endpoints`.
168
174
  - `sample_messages` (live) streams `ros2 topic echo` until `count` messages arrive or `timeout_s` (1..45, default 10, for the whole call) runs out, and returns what arrived with a `note` (`N of M messages within T s`, saying whether a publisher exists). QoS is matched to the publishers, so latched topics work. The payload has nested named fields (`payload.header.stamp.sec`); `timestamp_ns` is `header.stamp` (the publisher's clock, sim time on a simulation) or 0 for headerless types, with `stamp_source` and `received_ns` (wall clock when the CLI printed it). Arrays are cut at 128 elements by default; `max_array_length` (1..65536, or null for no cut) and `arrays_summary_only` change that, and a cut is listed under `_truncated_fields`. `nan` and `inf` come back as strings. `count` above 50 is capped with a note.
169
- - `analyze_bag` (live) parses `ros2 bag info` text for the totals and counts; anomaly detection is mock-only. Per-topic times, rates (`(n - 1) / span`) and `latched` are added when the bag can be read locally (`.db3` with the standard library, `.mcap` with `rosbags`), else rates fall back to count / bag duration (`frequency_basis`). `peek_bag_samples` reads the file itself, through `rosbags`, and is served only by the ROS2 CLI adapter or the mock; bags that embed no message definitions (Humble `.db3`) are decoded with the Humble definitions, or the distro the bag records, and `note` says so. Without `ros2`, bag tools return fixtures: check `health_check` for `mode: "mock"` before trusting bag output.
175
+ - `analyze_bag` (live) parses `ros2 bag info` text for the totals and counts; anomaly detection is mock-only. Per-topic times, rates (`(n - 1) / span`) and `latched` are added when the bag can be read locally (`.db3` with the standard library, `.mcap` with `rosbags`), else rates fall back to count / bag duration (`frequency_basis`). `peek_bag_samples` reads the file itself, through `rosbags`, returns the first `count` messages in recording order (not the last), and is served only by the ROS2 CLI adapter or the mock; its `timestamp_ns` is the message's `header.stamp` (`stamp_source` `header`) or, without a top-level header, the bag record time (`stamp_source` `recorded`), and `recorded_ns` is always the bag record time; bags that embed no message definitions (Humble `.db3`) are decoded with the Humble definitions, or the distro the bag records, and `note` says so. Without `ros2`, bag tools return fixtures: check `health_check` for `mode: "mock"` before trusting bag output. `health_check` also reports `sim_clock_published` (live only): true when `/clock` has a publisher, a hint that header stamps may be simulation time.
170
176
  - Synchronous handlers: the tools run on the MCP event loop; on Windows a hung `ros2` launcher can block the server.
171
177
  - No streaming or push subscriptions: tools are strictly request/response.
172
178
 
@@ -189,7 +195,7 @@ When on, each tool call emits one event with exactly six fields:
189
195
  | `tool_name` | `"list_topics"` | One of the twelve tools, never argument values |
190
196
  | `latency_ms` | `12.34` | Handler wall-clock duration, 2 decimals |
191
197
  | `mode` | `"mock"` | Mode of the adapter actually serving: `mock` or `live` |
192
- | `version` | `"0.6.0"` | TopicForge server version |
198
+ | `version` | `"0.6.2"` | TopicForge server version |
193
199
  | `session_id` | `"a1b2c3..."` | Random UUID per process, never persisted |
194
200
  | `success` | `true` | Whether the handler returned or raised |
195
201
 
@@ -38,7 +38,11 @@ The server speaks MCP over stdio and waits for a client, so wire it into one. Fo
38
38
  }
39
39
  ```
40
40
 
41
- Then ask it to list the topics or to analyze `/tmp/demo.mcap`. For Claude Code: `claude mcp add topicforge -- topicforge`. Setup for a real ROS2 environment (WSL2, Linux, Docker, native Windows) is in [`docs/TESTING.md`](docs/TESTING.md); recurring monitoring prompts and the privacy contract are in [`docs/TUTORIEL.md`](docs/TUTORIEL.md).
41
+ Then ask it to list the topics or to analyze `/tmp/demo.mcap`. For Claude Code: `claude mcp add topicforge -- topicforge`.
42
+
43
+ **Other MCP clients.** Ready-to-paste configs for Claude Desktop, Cursor, VS Code, Windsurf, Cline, Roo Code, Continue, Zed, JetBrains, Codex, Gemini CLI, Goose and more are in [`docs/CLIENTS.md`](docs/CLIENTS.md). One-click: [Add to Cursor](https://cursor.com/install-mcp?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlW2Rkc109PTAuNi4yIiwidG9waWNmb3JnZSJdLCJlbnYiOnsiVE9QSUNGT1JHRV9NT0RFIjoiYXV0byIsIlRPUElDRk9SR0VfRERTX0JBQ0tFTkQiOiJjeWNsb25lIiwiVE9QSUNGT1JHRV9ERFNfRE9NQUlOX0lEIjoiMCJ9fQ%3D%3D) | [Install in VS Code](https://vscode.dev/redirect/mcp/install?name=topicforge&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22topicforge%5Bdds%5D%3D%3D0.6.2%22%2C%22topicforge%22%5D%2C%22env%22%3A%7B%22TOPICFORGE_MODE%22%3A%22auto%22%2C%22TOPICFORGE_DDS_BACKEND%22%3A%22cyclone%22%2C%22TOPICFORGE_DDS_DOMAIN_ID%22%3A%220%22%7D%7D).
44
+
45
+ Setup for a real ROS2 environment (WSL2, Linux, Docker, native Windows) is in [`docs/TESTING.md`](docs/TESTING.md); recurring monitoring prompts and the privacy contract are in [`docs/TUTORIEL.md`](docs/TUTORIEL.md).
42
46
 
43
47
  ## Tools
44
48
 
@@ -98,6 +102,8 @@ Samples with comments are in [`.env.example`](.env.example). Any invalid value s
98
102
 
99
103
  ## Limitations
100
104
 
105
+ External validation against a simulated robot's ground truth: [docs/VALIDATION.md](docs/VALIDATION.md).
106
+
101
107
  - DDS validation is partial. The Cyclone adapter has run against a real bus, with Cyclone and Dust DDS participants, on Windows and in CI on Ubuntu and Windows (`.github/workflows/demo.yml`). The Fast DDS adapter has never run against a bus, and no RTI, OpenDDS, CoreDX or OpenSplice participant has been observed by this project. The multi-vendor claim rests on the RTPS protocol guarantee, not on a recorded cross-vendor run.
102
108
  - User-topic payloads are not decoded. `peek_dds_samples` on a user topic returns count 0 and a note that the topic is announced on the bus; no traffic is read. `topic_metrics` therefore has data only for the builtin discovery topics and says so in its `status`. It is a discovery-layer probe, not a publish-rate monitor.
103
109
  - Liveliness at runtime is not observed. A writer that is alive but silent (a hung process whose lease is still renewed) looks healthy, because TopicForge reads discovery, not data. An opt-in data probe is planned. A crash and a clean leave cannot be told apart, and `lost_ns` is an upper bound of the death.
@@ -106,7 +112,7 @@ Samples with comments are in [`.env.example`](.env.example). Any invalid value s
106
112
  - DDS Security is not handled. A participant without credentials sees an empty secure bus. `detect_qos_mismatches` checks Partition, type name, Reliability, Durability, Deadline, Liveliness, LatencyBudget, Ownership (kind), DestinationOrder and DataRepresentation (History as a risk); Presentation, XTypes assignability and runtime behavior are not checked, and the result lists them in `policies_unchecked`. It returns a `MismatchScan` envelope: read `reports` for the mismatches.
107
113
  - Fast DDS serves no `list_endpoints`.
108
114
  - `sample_messages` (live) streams `ros2 topic echo` until `count` messages arrive or `timeout_s` (1..45, default 10, for the whole call) runs out, and returns what arrived with a `note` (`N of M messages within T s`, saying whether a publisher exists). QoS is matched to the publishers, so latched topics work. The payload has nested named fields (`payload.header.stamp.sec`); `timestamp_ns` is `header.stamp` (the publisher's clock, sim time on a simulation) or 0 for headerless types, with `stamp_source` and `received_ns` (wall clock when the CLI printed it). Arrays are cut at 128 elements by default; `max_array_length` (1..65536, or null for no cut) and `arrays_summary_only` change that, and a cut is listed under `_truncated_fields`. `nan` and `inf` come back as strings. `count` above 50 is capped with a note.
109
- - `analyze_bag` (live) parses `ros2 bag info` text for the totals and counts; anomaly detection is mock-only. Per-topic times, rates (`(n - 1) / span`) and `latched` are added when the bag can be read locally (`.db3` with the standard library, `.mcap` with `rosbags`), else rates fall back to count / bag duration (`frequency_basis`). `peek_bag_samples` reads the file itself, through `rosbags`, and is served only by the ROS2 CLI adapter or the mock; bags that embed no message definitions (Humble `.db3`) are decoded with the Humble definitions, or the distro the bag records, and `note` says so. Without `ros2`, bag tools return fixtures: check `health_check` for `mode: "mock"` before trusting bag output.
115
+ - `analyze_bag` (live) parses `ros2 bag info` text for the totals and counts; anomaly detection is mock-only. Per-topic times, rates (`(n - 1) / span`) and `latched` are added when the bag can be read locally (`.db3` with the standard library, `.mcap` with `rosbags`), else rates fall back to count / bag duration (`frequency_basis`). `peek_bag_samples` reads the file itself, through `rosbags`, returns the first `count` messages in recording order (not the last), and is served only by the ROS2 CLI adapter or the mock; its `timestamp_ns` is the message's `header.stamp` (`stamp_source` `header`) or, without a top-level header, the bag record time (`stamp_source` `recorded`), and `recorded_ns` is always the bag record time; bags that embed no message definitions (Humble `.db3`) are decoded with the Humble definitions, or the distro the bag records, and `note` says so. Without `ros2`, bag tools return fixtures: check `health_check` for `mode: "mock"` before trusting bag output. `health_check` also reports `sim_clock_published` (live only): true when `/clock` has a publisher, a hint that header stamps may be simulation time.
110
116
  - Synchronous handlers: the tools run on the MCP event loop; on Windows a hung `ros2` launcher can block the server.
111
117
  - No streaming or push subscriptions: tools are strictly request/response.
112
118
 
@@ -129,7 +135,7 @@ When on, each tool call emits one event with exactly six fields:
129
135
  | `tool_name` | `"list_topics"` | One of the twelve tools, never argument values |
130
136
  | `latency_ms` | `12.34` | Handler wall-clock duration, 2 decimals |
131
137
  | `mode` | `"mock"` | Mode of the adapter actually serving: `mock` or `live` |
132
- | `version` | `"0.6.0"` | TopicForge server version |
138
+ | `version` | `"0.6.2"` | TopicForge server version |
133
139
  | `session_id` | `"a1b2c3..."` | Random UUID per process, never persisted |
134
140
  | `success` | `true` | Whether the handler returned or raised |
135
141
 
@@ -0,0 +1,280 @@
1
+ # Using TopicForge from your MCP client
2
+
3
+ Last checked 2026-10-06.
4
+
5
+ TopicForge (ROS 2 / DDS) is a local, read-only MCP server that speaks stdio.
6
+ Every client below launches it as a child process with the same command:
7
+
8
+ ```
9
+ uvx --from "topicforge[dds]==0.6.2" topicforge
10
+ ```
11
+
12
+ and the same three environment variables:
13
+
14
+ | Variable | Value | Meaning |
15
+ | --- | --- | --- |
16
+ | `TOPICFORGE_MODE` | `auto` | Use ROS 2 if it is on PATH, else fixtures. |
17
+ | `TOPICFORGE_DDS_BACKEND` | `cyclone` | Join the DDS bus as a read-only participant (the `[dds]` extra provides the binding). Use `mock` for a demo with no robot. |
18
+ | `TOPICFORGE_DDS_DOMAIN_ID` | `0` | The one DDS domain to observe (0-232). Fixed at startup: restart the server to change it. |
19
+
20
+ The registry name is `io.github.yaniswav/topicforge`. Where a client asks for a
21
+ display name, use **TopicForge (ROS 2 / DDS)**: an unrelated SEO product is also
22
+ called TopicForge in the MCP registries.
23
+
24
+ The version pin (`==0.6.2`) is moved on every release together with the other
25
+ version strings; a test keeps them in sync.
26
+
27
+ ## Before you start
28
+
29
+ - **uv.** `uvx` ships with [uv](https://docs.astral.sh/uv/getting-started/installation/).
30
+ Install it once, then check `uvx --version` in a new terminal.
31
+ - **Desktop apps do not inherit your shell PATH.** On Windows especially, an app
32
+ started from the Start menu may not see `uvx`. If a client reports "command not
33
+ found", put the absolute path in `command` (find it with `where uvx` on Windows
34
+ or `which uvx` elsewhere, for example `C:/Users/you/.local/bin/uvx.exe`;
35
+ forward slashes are fine in JSON).
36
+ - **First start is slow.** `uvx` downloads the package and the Cyclone DDS binding
37
+ the first time. Clients with a short startup timeout may need a retry or a
38
+ larger timeout (noted per client below).
39
+ - **If resolution fails on a very new Python** (the Cyclone binding ships wheels for Python 3.10 to 3.13 only), add `"--python", "3.12"` before
40
+ `"--from"` in the args.
41
+ - **pip alternative.** `pip install "topicforge[dds]==0.6.2"`, then use
42
+ `"command": "topicforge"` (or `"command": "python", "args": ["-m", "topicforge"]`)
43
+ with no `args` for uvx. Use the absolute path of the binary if the client does
44
+ not see your venv.
45
+ - **No robot at hand?** Set `TOPICFORGE_DDS_BACKEND` to `mock` to try the tools
46
+ against deterministic fixtures.
47
+
48
+ ## What cannot work: ChatGPT and hosted endpoints
49
+
50
+ ChatGPT on the web and in the desktop app cannot run local stdio MCP servers; it
51
+ only talks to remote MCP endpoints. Use Codex (below), which runs local stdio
52
+ servers, or any other client on this page.
53
+
54
+ A hosted TopicForge endpoint is out of scope on purpose. TopicForge has to sit on
55
+ the same network as the DDS participants it observes, and exposing a robot bus to
56
+ the internet would contradict the read-only safety promise the product is built
57
+ on.
58
+
59
+ ## The common JSON shape
60
+
61
+ Most clients read an `mcpServers` object like this one. The per-client sections
62
+ say where the file lives and what differs.
63
+
64
+ ```json
65
+ {
66
+ "mcpServers": {
67
+ "topicforge": {
68
+ "command": "uvx",
69
+ "args": ["--from", "topicforge[dds]==0.6.2", "topicforge"],
70
+ "env": {
71
+ "TOPICFORGE_MODE": "auto",
72
+ "TOPICFORGE_DDS_BACKEND": "cyclone",
73
+ "TOPICFORGE_DDS_DOMAIN_ID": "0"
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ ## Claude Code
81
+
82
+ Plugin (server plus two skills, asks for backend and domain at install):
83
+
84
+ ```
85
+ claude plugin marketplace add yaniswav/TopicForge
86
+ claude plugin install topicforge@topicforge
87
+ ```
88
+
89
+ Or just the server:
90
+
91
+ ```
92
+ claude mcp add topicforge --env TOPICFORGE_MODE=auto --env TOPICFORGE_DDS_BACKEND=cyclone --env TOPICFORGE_DDS_DOMAIN_ID=0 -- uvx --from "topicforge[dds]==0.6.2" topicforge
93
+ ```
94
+
95
+ ## Claude Desktop
96
+
97
+ From 0.6.2: download `topicforge-<version>.mcpb` from the latest GitHub release and double-click it. Claude Desktop asks for the DDS backend and domain id and manages Python and dependencies itself through uv.
98
+
99
+ Or edit the config file by hand: `claude_desktop_config.json` (Settings > Developer > Edit Config):
100
+ `%APPDATA%\Claude\claude_desktop_config.json` on Windows,
101
+ `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS. Paste
102
+ the common JSON shape above and restart the app completely. On Windows, if
103
+ `uvx` is not found, use its absolute path as `command`.
104
+
105
+ ## Cursor
106
+
107
+ The one-click links in this page and in the README are https redirects, because GitHub and PyPI strip `cursor://` and `vscode:` links. Only the raw schemes are documented by Cursor and VS Code; the https forms (`cursor.com/install-mcp`, `vscode.dev/redirect/mcp/install`) were observed to redirect correctly on 2026-10-06 but are not in the vendors' docs (unverified, last checked 2026-10-06). The raw links are given as a fallback.
108
+
109
+ One click: [Add TopicForge to Cursor](https://cursor.com/install-mcp?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlW2Rkc109PTAuNi4yIiwidG9waWNmb3JnZSJdLCJlbnYiOnsiVE9QSUNGT1JHRV9NT0RFIjoiYXV0byIsIlRPUElDRk9SR0VfRERTX0JBQ0tFTkQiOiJjeWNsb25lIiwiVE9QSUNGT1JHRV9ERFNfRE9NQUlOX0lEIjoiMCJ9fQ%3D%3D)
110
+
111
+ If that page does not open Cursor, use the raw deeplink: `cursor://anysphere.cursor-deeplink/mcp/install?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlW2Rkc109PTAuNi4yIiwidG9waWNmb3JnZSJdLCJlbnYiOnsiVE9QSUNGT1JHRV9NT0RFIjoiYXV0byIsIlRPUElDRk9SR0VfRERTX0JBQ0tFTkQiOiJjeWNsb25lIiwiVE9QSUNGT1JHRV9ERFNfRE9NQUlOX0lEIjoiMCJ9fQ==`
112
+
113
+ Or paste the common JSON shape into `~/.cursor/mcp.json` (all projects) or
114
+ `.cursor/mcp.json` (one project).
115
+
116
+ ## VS Code / GitHub Copilot
117
+
118
+ One click: [Install in VS Code](https://vscode.dev/redirect/mcp/install?name=topicforge&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22topicforge%5Bdds%5D%3D%3D0.6.2%22%2C%22topicforge%22%5D%2C%22env%22%3A%7B%22TOPICFORGE_MODE%22%3A%22auto%22%2C%22TOPICFORGE_DDS_BACKEND%22%3A%22cyclone%22%2C%22TOPICFORGE_DDS_DOMAIN_ID%22%3A%220%22%7D%7D)
119
+
120
+ Fallback, the raw URL handler: `vscode:mcp/install?%7B%22name%22%3A%22topicforge%22%2C%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22--from%22%2C%22topicforge%5Bdds%5D%3D%3D0.6.2%22%2C%22topicforge%22%5D%2C%22env%22%3A%7B%22TOPICFORGE_MODE%22%3A%22auto%22%2C%22TOPICFORGE_DDS_BACKEND%22%3A%22cyclone%22%2C%22TOPICFORGE_DDS_DOMAIN_ID%22%3A%220%22%7D%7D`
121
+
122
+ Or put this in `.vscode/mcp.json` (the key is `servers`, not `mcpServers`), or run
123
+ "MCP: Add Server" from the Command Palette:
124
+
125
+ ```json
126
+ {
127
+ "servers": {
128
+ "topicforge": {
129
+ "type": "stdio",
130
+ "command": "uvx",
131
+ "args": ["--from", "topicforge[dds]==0.6.2", "topicforge"],
132
+ "env": {
133
+ "TOPICFORGE_MODE": "auto",
134
+ "TOPICFORGE_DDS_BACKEND": "cyclone",
135
+ "TOPICFORGE_DDS_DOMAIN_ID": "0"
136
+ }
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ ## Windsurf / Devin (Cascade)
143
+
144
+ Windsurf's docs now redirect to Devin's (unverified, last checked 2026-10-06). Edit `%APPDATA%\devin\mcp_config.json`
145
+ (Windows) or `~/.config/devin/mcp_config.json` (macOS, Linux) and paste the common
146
+ JSON shape. Older installs may still use `~/.codeium/windsurf/mcp_config.json` (unverified, last checked 2026-10-06).
147
+
148
+ ## Cline
149
+
150
+ Open the MCP Servers icon in the Cline panel, then Configure, then "Configure MCP
151
+ Servers", and paste the common JSON shape (the file is
152
+ `~/.cline/data/settings/cline_mcp_settings.json` per docs.cline.bot on 2026-10-06; older VS Code installs kept it elsewhere, unverified). Optional fields such as
153
+ `"disabled": false` and `"autoApprove": []` are accepted.
154
+
155
+ ## Roo Code
156
+
157
+ Project file `.roo/mcp.json` (takes precedence) or the global `mcp_settings.json`
158
+ opened from the MCP view. Paste the common JSON shape. Roo's docs say Windows
159
+ needs a `cmd /c` wrapper for `npx`; for `uvx`, try it directly first and fall back
160
+ to `"command": "cmd", "args": ["/c", "uvx", "--from", "topicforge[dds]==0.6.2",
161
+ "topicforge"]` if it fails to start.
162
+
163
+ ## Continue
164
+
165
+ Create `.continue/mcpServers/topicforge.yaml` (MCP works in agent mode only):
166
+
167
+ ```yaml
168
+ name: TopicForge (ROS 2 / DDS)
169
+ version: 0.0.1
170
+ schema: v1
171
+ mcpServers:
172
+ - name: topicforge
173
+ type: stdio
174
+ command: uvx
175
+ args:
176
+ - "--from"
177
+ - "topicforge[dds]==0.6.2"
178
+ - "topicforge"
179
+ env:
180
+ TOPICFORGE_MODE: auto
181
+ TOPICFORGE_DDS_BACKEND: cyclone
182
+ TOPICFORGE_DDS_DOMAIN_ID: "0"
183
+ ```
184
+
185
+ ## Zed
186
+
187
+ In `settings.json`, under `context_servers`:
188
+
189
+ ```json
190
+ {
191
+ "context_servers": {
192
+ "topicforge": {
193
+ "command": "uvx",
194
+ "args": ["--from", "topicforge[dds]==0.6.2", "topicforge"],
195
+ "env": {
196
+ "TOPICFORGE_MODE": "auto",
197
+ "TOPICFORGE_DDS_BACKEND": "cyclone",
198
+ "TOPICFORGE_DDS_DOMAIN_ID": "0"
199
+ }
200
+ }
201
+ }
202
+ }
203
+ ```
204
+
205
+ ## JetBrains AI Assistant
206
+
207
+ Settings > Tools > AI Assistant > Model Context Protocol (MCP) > Add, then paste the
208
+ common JSON shape. You can also use "Import from Claude" if you already set it up
209
+ there.
210
+
211
+ ## OpenAI Codex CLI
212
+
213
+ Codex runs local stdio servers (CLI, IDE extension and desktop app share
214
+ `~/.codex/config.toml`). The default startup timeout is 10 s, which a first `uvx`
215
+ run with the dds extra can exceed, so set it to 60 s:
216
+
217
+ ```toml
218
+ [mcp_servers.topicforge]
219
+ command = "uvx"
220
+ args = ["--from", "topicforge[dds]==0.6.2", "topicforge"]
221
+ startup_timeout_sec = 60
222
+
223
+ [mcp_servers.topicforge.env]
224
+ TOPICFORGE_MODE = "auto"
225
+ TOPICFORGE_DDS_BACKEND = "cyclone"
226
+ TOPICFORGE_DDS_DOMAIN_ID = "0"
227
+ ```
228
+
229
+ ## Gemini CLI
230
+
231
+ As an extension (reads `gemini-extension.json` from this repository):
232
+
233
+ ```
234
+ gemini extensions install https://github.com/yaniswav/TopicForge
235
+ ```
236
+
237
+ Or paste the common JSON shape into `~/.gemini/settings.json` (or
238
+ `.gemini/settings.json` in a project). Add `"timeout": 60000` (milliseconds) for
239
+ the slow first start, and do not set `"trust": true`.
240
+
241
+ ## Goose
242
+
243
+ In `~/.config/goose/config.yaml` (macOS, Linux; the Windows location is unverified, last checked 2026-10-06), or via the extensions screen:
244
+
245
+ ```yaml
246
+ extensions:
247
+ topicforge:
248
+ name: TopicForge (ROS 2 / DDS)
249
+ type: stdio
250
+ cmd: uvx
251
+ args: [--from, "topicforge[dds]==0.6.2", topicforge]
252
+ enabled: true
253
+ envs: { "TOPICFORGE_MODE": "auto", "TOPICFORGE_DDS_BACKEND": "cyclone", "TOPICFORGE_DDS_DOMAIN_ID": "0" }
254
+ timeout: 300
255
+ ```
256
+
257
+ ## LM Studio
258
+
259
+ Program tab > Install > Edit `mcp.json` (same notation as Cursor), then paste the
260
+ common JSON shape. LM Studio's docs only show remote examples; local command
261
+ servers follow the Cursor notation but are not documented explicitly. Small
262
+ local models may struggle with twelve tools.
263
+
264
+ ## Amazon Q Developer / Kiro
265
+
266
+ Kiro: `~/.kiro/settings/mcp.json` (user) or `.kiro/settings/mcp.json` (workspace,
267
+ takes precedence); paste the common JSON shape. Amazon Q Developer CLI uses the
268
+ same shape in `~/.aws/amazonq/mcp.json` (the Amazon Q path and the move to Kiro CLI are unverified, last checked 2026-10-06).
269
+
270
+ ## Warp
271
+
272
+ Settings > MCP > "+ Add", then paste the common JSON shape (unverified, last checked 2026-10-06: the Warp docs page could not be fetched).
273
+
274
+ ## Troubleshooting
275
+
276
+ - Run the command by hand first: `uvx --from "topicforge[dds]==0.6.2" topicforge --version`.
277
+ - Server starts but sees no DDS participants: check the domain id, and that the
278
+ client machine is on the same network as the robot (DDS discovery uses
279
+ multicast UDP). Ask the assistant to call `health_check`.
280
+ - More in [TROUBLESHOOTING.md](TROUBLESHOOTING.md) and [TESTING.md](TESTING.md).
@@ -15,7 +15,7 @@ How to get a working ROS2 environment to point TopicForge at, and how to wire it
15
15
  ```bash
16
16
  python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
17
17
  pip install topicforge
18
- python -m topicforge --version # -> topicforge 0.6.0
18
+ python -m topicforge --version # -> topicforge 0.6.2
19
19
  TOPICFORGE_MODE=mock python -m topicforge # blocks on stdio; MCP clients spawn it
20
20
  ```
21
21
 
@@ -41,7 +41,7 @@ python3 -m venv ~/topicforge-venv && source ~/topicforge-venv/bin/activate
41
41
  pip install topicforge # Ubuntu 22.04 ships Python 3.10, which is enough
42
42
  ```
43
43
 
44
- Then run live mode with three terminals. In the first, `ros2 run demo_nodes_cpp talker` publishes `/chatter` at about 1 Hz. In the second, `ros2 topic list` should show `/chatter`. In the third, source ROS2, activate the venv and run `TOPICFORGE_MODE=live python -m topicforge`. The startup line reads `topicforge 0.6.0 ready (mode=live, requested_mode=live, adapter=ros2_cli, telemetry=off)`; `mode=mock` means `ros2` was not found and the server fell back to fixtures.
44
+ Then run live mode with three terminals. In the first, `ros2 run demo_nodes_cpp talker` publishes `/chatter` at about 1 Hz. In the second, `ros2 topic list` should show `/chatter`. In the third, source ROS2, activate the venv and run `TOPICFORGE_MODE=live python -m topicforge`. The startup line reads `topicforge 0.6.2 ready (mode=live, requested_mode=live, adapter=ros2_cli, telemetry=off)`; `mode=mock` means `ros2` was not found and the server fell back to fixtures.
45
45
 
46
46
  ## Linux native
47
47
 
@@ -102,4 +102,4 @@ TopicForge speaks MCP over stdio; any compliant client can spawn it with a comma
102
102
  }
103
103
  ```
104
104
 
105
- That is the Claude Desktop shape (`claude_desktop_config.json`); restart the app and the twelve tools appear under the hammer icon. For Claude Code run `claude mcp add topicforge -- topicforge`. Cursor, Continue and Cline accept the same stdio config. If the `topicforge` script is not on PATH, use `"command": "python", "args": ["-m", "topicforge"]`, or the absolute path of the binary inside your venv: desktop clients do not inherit your shell's PATH or venv activation.
105
+ That is the Claude Desktop shape (`claude_desktop_config.json`); restart the app and the twelve tools appear under the hammer icon. For Claude Code run `claude mcp add topicforge -- topicforge`. Cursor, Continue and Cline accept the same stdio config; ready-to-paste configs for every major client are in [`CLIENTS.md`](CLIENTS.md). If the `topicforge` script is not on PATH, use `"command": "python", "args": ["-m", "topicforge"]`, or the absolute path of the binary inside your venv: desktop clients do not inherit your shell's PATH or venv activation.
@@ -0,0 +1,15 @@
1
+ # External validation
2
+
3
+ ## OmniSim (October 2026)
4
+
5
+ The OmniSim team ran TopicForge against a simulated robot and compared its answers with the simulator's ground truth.
6
+
7
+ Setup: [OmniSim v9.1.3](https://github.com/omnilink-tech/omnisim/releases/tag/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.
8
+
9
+ What matched:
10
+ - Every lidar scan TopicForge sampled was bit-identical to the simulator's raw data on the beams it delivered.
11
+ - `list_topics` and `get_topic_info` returned the same names, types and publisher/subscriber counts as the `ros2` CLI.
12
+ - `analyze_bag` returned the same duration and message counts as `ros2 bag info` and an independent read of the bag.
13
+ - 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.
14
+
15
+ 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 (`tests/fixtures/bags/omnisim_humble/`).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yanis ETHVIGNOT
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,60 @@
1
+ # TopicForge plugin for Claude
2
+
3
+ TopicForge is a read-only MCP server for ROS 2 and DDS. This plugin bundles the server with two short skills so Claude can inspect a robot stack and diagnose a DDS bus. It cannot publish, command a robot, or change QoS: the server has no write path.
4
+
5
+ ## What is included
6
+
7
+ - MCP server `topicforge`, started with `uvx` from the `topicforge` package on PyPI (version pinned to 0.6.2). It exposes twelve read-only tools: `health_check`, `list_topics`, `get_topic_info`, `sample_messages`, `analyze_bag`, `peek_bag_samples`, `list_participants`, `list_endpoints`, `detect_qos_mismatches`, `participant_events`, `peek_dds_samples`, `topic_metrics`.
8
+ - Skill `diagnose-dds-bus`: what to call, and in which order, when nodes do not talk, a topic gets no data, or a node crashed or restarts.
9
+ - Skill `inspect-ros2-robot`: listing topics, sampling messages and reading bags, including large arrays and simulation time.
10
+
11
+ ## Requirements
12
+
13
+ - [`uv`](https://docs.astral.sh/uv/) on PATH, which provides `uvx`. The first start downloads the package and the Cyclone DDS binding, so it can take a little while.
14
+ - A local MCP server: it runs on your machine, in Claude Code and in Cowork sessions that run on your computer. It does not run in claude.ai chat.
15
+ - ROS 2 on PATH is optional. Without it the ROS 2 tools fall back to fixtures (a fictional demo robot) and `health_check` reports `mode: mock`. The DDS tools work without ROS 2.
16
+
17
+ ## Install
18
+
19
+ From a marketplace, once the plugin is listed:
20
+
21
+ ```
22
+ /plugin install topicforge
23
+ ```
24
+
25
+ Meanwhile, from this repository (Claude Code):
26
+
27
+ ```
28
+ claude plugin marketplace add yaniswav/TopicForge
29
+ claude plugin install topicforge@topicforge
30
+ ```
31
+
32
+ Or load the folder for one session: `claude --plugin-dir ./plugin`.
33
+
34
+ ## Configuration
35
+
36
+ Claude Code asks for two options when the plugin is enabled:
37
+
38
+ | Option | Default | Meaning |
39
+ | --- | --- | --- |
40
+ | `dds_backend` | `cyclone` | `cyclone` joins the DDS bus as a read-only participant. `mock` serves fixtures. `auto` picks the best available. |
41
+ | `dds_domain_id` | `0` | The one DDS domain observed (0 to 232). Fixed at startup. |
42
+
43
+ The server runs with `TOPICFORGE_MODE=auto`: live ROS 2 when `ros2` is on PATH, fixtures otherwise. Cowork does not prompt for options and uses the defaults. To use other settings there, edit `.mcp.json`.
44
+
45
+ Without DDS, set `dds_backend` to `mock`, or use the standalone install `uvx topicforge`.
46
+
47
+ ## Privacy
48
+
49
+ The server reads the local ROS 2 graph and the DDS discovery traffic on the machine and network it runs on, and returns the result to the Claude session. It makes no outbound network calls. Anonymous telemetry exists but is off by default and is not enabled by this plugin. See the main [README](https://github.com/yaniswav/TopicForge#telemetry) for the telemetry contract and [SECURITY.md](https://github.com/yaniswav/TopicForge/blob/main/SECURITY.md).
50
+
51
+ ## Limits
52
+
53
+ TopicForge sees what DDS discovery announces. It cannot see whether data flows, a writer that is alive but silent, other DDS domains, or secured (DDS Security) endpoints. The skills tell Claude to say so.
54
+
55
+ ## Links
56
+
57
+ - Project: https://github.com/yaniswav/TopicForge
58
+ - Site: https://topicforge.horizonvista.xyz
59
+ - Issues: https://github.com/yaniswav/TopicForge/issues
60
+ - License: MIT
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "topicforge"
7
- version = "0.6.0"
7
+ version = "0.6.2"
8
8
  description = "ROS Topic Inspector & Bag Analyzer MCP server for AI agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -81,7 +81,7 @@ packages = ["src/topicforge"]
81
81
 
82
82
  [tool.hatch.build.targets.sdist]
83
83
  include = ["src/topicforge", "tests", "docs", "README.md", "CHANGELOG.md", "LICENSE", "pyproject.toml"]
84
- exclude = [".claude", "docs/projet-file/**", "docs/projet-file", "tests/fixtures/bags/**", "CLAUDE.md", "*.pdf", ".pytest_cache", "pytest-cache-files-*", "pro"]
84
+ exclude = [".claude", "docs/projet-file/**", "docs/projet-file", "tests/fixtures/bags/**", "CLAUDE.md", "*.pdf", ".pytest_cache", "pytest-cache-files-*", "pro", "mcpb"]
85
85
 
86
86
  [tool.pytest.ini_options]
87
87
  testpaths = ["tests"]
@@ -1,5 +1,5 @@
1
1
  """TopicForge: ROS Topic Inspector & Bag Analyzer MCP server."""
2
2
 
3
- __version__ = "0.6.0"
3
+ __version__ = "0.6.2"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -59,8 +59,18 @@ def iter_field_names(sample: Any) -> list[str]:
59
59
  """List field names on a dynamic-type sample.
60
60
 
61
61
  Tries `__dataclass_fields__`, `__fields__`, `__slots__`, then public
62
- `__dict__` keys. Returns an empty list when none apply.
62
+ `__dict__` keys. Returns an empty list when none apply. Dunder names
63
+ such as the `__msgtype__` marker that `rosbags` messages carry are
64
+ not data and are left out.
63
65
  """
66
+ return [name for name in _raw_field_names(sample) if not _is_dunder(name)]
67
+
68
+
69
+ def _is_dunder(name: str) -> bool:
70
+ return name.startswith("__") and name.endswith("__")
71
+
72
+
73
+ def _raw_field_names(sample: Any) -> list[str]:
64
74
  fields = getattr(sample, "__dataclass_fields__", None)
65
75
  if fields:
66
76
  return list(fields)
@@ -81,6 +81,11 @@ class CompositeAdapter:
81
81
  def analyze_bag(self, path: str) -> BagAnalysis:
82
82
  return self._ros.analyze_bag(path)
83
83
 
84
+ def sim_clock_published(self) -> bool | None:
85
+ """The ROS 2 half's `/clock` probe, `None` when it has none."""
86
+ probe = getattr(self._ros, "sim_clock_published", None)
87
+ return probe() if callable(probe) else None
88
+
84
89
  # ----- DDS surface -> DDS adapter -----
85
90
 
86
91
  def observer_status(self) -> dict[str, Any] | None: