topicforge 0.6.1__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.1 → topicforge-0.6.2}/CHANGELOG.md +24 -1
  2. {topicforge-0.6.1 → topicforge-0.6.2}/PKG-INFO +7 -3
  3. {topicforge-0.6.1 → topicforge-0.6.2}/README.md +6 -2
  4. topicforge-0.6.2/docs/CLIENTS.md +280 -0
  5. {topicforge-0.6.1 → topicforge-0.6.2}/docs/TESTING.md +3 -3
  6. {topicforge-0.6.1 → topicforge-0.6.2}/plugin/README.md +1 -1
  7. {topicforge-0.6.1 → topicforge-0.6.2}/pyproject.toml +2 -2
  8. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/__init__.py +1 -1
  9. topicforge-0.6.2/tests/test_version_consistency.py +150 -0
  10. {topicforge-0.6.1 → topicforge-0.6.2}/.gitignore +0 -0
  11. {topicforge-0.6.1 → topicforge-0.6.2}/LICENSE +0 -0
  12. {topicforge-0.6.1 → topicforge-0.6.2}/docs/DDS_QUICKSTART.md +0 -0
  13. {topicforge-0.6.1 → topicforge-0.6.2}/docs/TROUBLESHOOTING.md +0 -0
  14. {topicforge-0.6.1 → topicforge-0.6.2}/docs/TUTORIEL.md +0 -0
  15. {topicforge-0.6.1 → topicforge-0.6.2}/docs/VALIDATION.md +0 -0
  16. {topicforge-0.6.1 → topicforge-0.6.2}/docs/dds-interop-matrix.md +0 -0
  17. {topicforge-0.6.1 → topicforge-0.6.2}/examples/README.md +0 -0
  18. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/00_hello_pub_sub/README.md +0 -0
  19. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/01_who_is_on_the_bus/README.md +0 -0
  20. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/02_why_cant_they_talk/README.md +0 -0
  21. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/03_a_node_crashed/README.md +0 -0
  22. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/04_late_joiner_misses_data/README.md +0 -0
  23. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/05_reliability_in_code/README.md +0 -0
  24. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/06_durability_late_joiner_in_code/README.md +0 -0
  25. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/07_deadline_in_code/README.md +0 -0
  26. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/08_crash_seen_from_inside/README.md +0 -0
  27. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/10_lidar_silent_after_driver_swap/README.md +0 -0
  28. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/11_who_talks_to_whom/README.md +0 -0
  29. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/12_safety_monitor_dropout/README.md +0 -0
  30. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/13_deadline_not_offered/README.md +0 -0
  31. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/14_restart_loop/README.md +0 -0
  32. {topicforge-0.6.1 → topicforge-0.6.2}/examples/dds/README.md +0 -0
  33. {topicforge-0.6.1 → topicforge-0.6.2}/plugin/LICENSE +0 -0
  34. {topicforge-0.6.1 → topicforge-0.6.2}/scripts/agent_eval/README.md +0 -0
  35. {topicforge-0.6.1 → topicforge-0.6.2}/scripts/integration/README.md +0 -0
  36. {topicforge-0.6.1 → topicforge-0.6.2}/scripts/integration/publishers/cyclone_c/README.md +0 -0
  37. {topicforge-0.6.1 → topicforge-0.6.2}/scripts/integration/publishers/cyclone_cpp/README.md +0 -0
  38. {topicforge-0.6.1 → topicforge-0.6.2}/scripts/integration/publishers/cyclone_rust/README.md +0 -0
  39. {topicforge-0.6.1 → topicforge-0.6.2}/scripts/integration/publishers/dust_py/README.md +0 -0
  40. {topicforge-0.6.1 → topicforge-0.6.2}/scripts/integration/publishers/fast_publisher_cpp/README.md +0 -0
  41. {topicforge-0.6.1 → topicforge-0.6.2}/scripts/integration/publishers/fast_py/README.md +0 -0
  42. {topicforge-0.6.1 → topicforge-0.6.2}/scripts/integration/publishers/opensplice_publisher/README.md +0 -0
  43. {topicforge-0.6.1 → topicforge-0.6.2}/scripts/integration/publishers/rti_c/README.md +0 -0
  44. {topicforge-0.6.1 → topicforge-0.6.2}/scripts/integration/publishers/rti_cpp/README.md +0 -0
  45. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/__main__.py +0 -0
  46. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/__init__.py +0 -0
  47. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/base.py +0 -0
  48. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/__init__.py +0 -0
  49. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/cdr_decoder.py +0 -0
  50. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/dds_helpers.py +0 -0
  51. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/dds_introspection.py +0 -0
  52. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/discovery_tracker.py +0 -0
  53. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/endpoints.py +0 -0
  54. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/lifecycle.py +0 -0
  55. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/metrics_buffer.py +0 -0
  56. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/qos_analyzer.py +0 -0
  57. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/qos_endpoints.py +0 -0
  58. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/qos_normalize.py +0 -0
  59. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/qos_scan.py +0 -0
  60. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/topic_filter.py +0 -0
  61. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/common/xtypes.py +0 -0
  62. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/composite.py +0 -0
  63. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/dds_cyclone/__init__.py +0 -0
  64. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/dds_cyclone/adapter.py +0 -0
  65. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/dds_dust/__init__.py +0 -0
  66. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/dds_dust/adapter.py +0 -0
  67. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/dds_fast/__init__.py +0 -0
  68. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/dds_fast/adapter.py +0 -0
  69. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/dds_opendds/__init__.py +0 -0
  70. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/dds_opendds/adapter.py +0 -0
  71. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
  72. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/adapter.py +0 -0
  73. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/echo_parser.py +0 -0
  74. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/echo_stream.py +0 -0
  75. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/parsers.py +0 -0
  76. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/ros2_live/process_tree.py +0 -0
  77. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
  78. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/ros2_mock/adapter.py +0 -0
  79. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/adapters/ros2_mock/fixtures.py +0 -0
  80. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/config/__init__.py +0 -0
  81. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/config/settings.py +0 -0
  82. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/constants.py +0 -0
  83. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/models/__init__.py +0 -0
  84. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/models/schemas.py +0 -0
  85. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/server/__init__.py +0 -0
  86. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/server/app.py +0 -0
  87. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/services/__init__.py +0 -0
  88. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/services/bag_service.py +0 -0
  89. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/services/bag_stats.py +0 -0
  90. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/services/factory.py +0 -0
  91. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/services/health.py +0 -0
  92. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/services/inspector.py +0 -0
  93. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/services/sample_budget.py +0 -0
  94. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/telemetry/__init__.py +0 -0
  95. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/telemetry/client.py +0 -0
  96. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/tools/__init__.py +0 -0
  97. {topicforge-0.6.1 → topicforge-0.6.2}/src/topicforge/tools/handlers.py +0 -0
  98. {topicforge-0.6.1 → topicforge-0.6.2}/tests/__init__.py +0 -0
  99. {topicforge-0.6.1 → topicforge-0.6.2}/tests/conftest.py +0 -0
  100. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_echo/image_noarr.yaml +0 -0
  101. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_echo/image_trunc4.yaml +0 -0
  102. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_echo/scan_edge.yaml +0 -0
  103. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_echo/scan_noarr.yaml +0 -0
  104. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_echo/scan_trunc128.yaml +0 -0
  105. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_echo/scan_trunc3.yaml +0 -0
  106. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_echo/string_latched.yaml +0 -0
  107. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_echo/twist.yaml +0 -0
  108. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_topic_info_verbose_cmd_vel.txt +0 -0
  109. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_topic_info_verbose_parameter_events.txt +0 -0
  110. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_topic_info_verbose_scan.txt +0 -0
  111. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_topic_info_verbose_tf.txt +0 -0
  112. {topicforge-0.6.1 → topicforge-0.6.2}/tests/fixtures/ros2_topic_info_verbose_tf_static.txt +0 -0
  113. {topicforge-0.6.1 → topicforge-0.6.2}/tests/integration/__init__.py +0 -0
  114. {topicforge-0.6.1 → topicforge-0.6.2}/tests/integration/ros2/Dockerfile +0 -0
  115. {topicforge-0.6.1 → topicforge-0.6.2}/tests/integration/ros2/__init__.py +0 -0
  116. {topicforge-0.6.1 → topicforge-0.6.2}/tests/integration/ros2/entrypoint.sh +0 -0
  117. {topicforge-0.6.1 → topicforge-0.6.2}/tests/integration/ros2/publisher.py +0 -0
  118. {topicforge-0.6.1 → topicforge-0.6.2}/tests/integration/ros2/run_bench.py +0 -0
  119. {topicforge-0.6.1 → topicforge-0.6.2}/tests/integration/ros2/test_live_adapter.py +0 -0
  120. {topicforge-0.6.1 → topicforge-0.6.2}/tests/integration/test_real_bus.py +0 -0
  121. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_analyze_bag_multi_format.py +0 -0
  122. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_bag_omnisim_humble.py +0 -0
  123. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_bag_service.py +0 -0
  124. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_bag_stats.py +0 -0
  125. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_cdr_decoder.py +0 -0
  126. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_composite_adapter.py +0 -0
  127. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_config.py +0 -0
  128. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_cyclone_adapter.py +0 -0
  129. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_dds_cross_vendor.py +0 -0
  130. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_dds_helpers.py +0 -0
  131. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_dds_inactive_reason.py +0 -0
  132. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_dds_introspection.py +0 -0
  133. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_dds_qos_normalization.py +0 -0
  134. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_dds_schemas.py +0 -0
  135. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_discovery_tracker.py +0 -0
  136. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_dust_adapter.py +0 -0
  137. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_echo_parser.py +0 -0
  138. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_echo_stream.py +0 -0
  139. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_endpoints.py +0 -0
  140. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_example_node_spec.py +0 -0
  141. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_factory.py +0 -0
  142. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_fast_adapter.py +0 -0
  143. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_health.py +0 -0
  144. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_history_and_hints.py +0 -0
  145. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_honest_outputs.py +0 -0
  146. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_inspector.py +0 -0
  147. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_lifecycle_buffer.py +0 -0
  148. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_live_adapter_graph.py +0 -0
  149. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_live_adapter_parse.py +0 -0
  150. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_live_adapter_subprocess.py +0 -0
  151. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_live_sample_notes.py +0 -0
  152. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_metrics_buffer.py +0 -0
  153. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_mock_adapter.py +0 -0
  154. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_opendds_adapter.py +0 -0
  155. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_peek_bag_samples.py +0 -0
  156. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_process_tree.py +0 -0
  157. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_qos_analyzer.py +0 -0
  158. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_qos_endpoints.py +0 -0
  159. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_qos_scan.py +0 -0
  160. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_sample_options.py +0 -0
  161. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_telemetry.py +0 -0
  162. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_tools_integration.py +0 -0
  163. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_topic_metrics.py +0 -0
  164. {topicforge-0.6.1 → topicforge-0.6.2}/tests/test_xtypes.py +0 -0
@@ -5,6 +5,28 @@ 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
+
8
30
  ## [0.6.1] - 2026-10-06
9
31
 
10
32
  ### Added
@@ -1294,7 +1316,8 @@ Initial MVP release of TopicForge: ROS Topic Inspector & Bag Analyzer MCP server
1294
1316
  - The write path (publishing, commanding robots) is intentionally out of scope for the MVP.
1295
1317
  - `analyze_bag` in live mode parses `ros2 bag info` text output; deeper anomaly detection remains mock-only for now.
1296
1318
 
1297
- [Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.6.1...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
1298
1321
  [0.6.1]: https://github.com/yaniswav/TopicForge/compare/v0.6.0...v0.6.1
1299
1322
  [0.6.0]: https://github.com/yaniswav/TopicForge/compare/v0.5.6...v0.6.0
1300
1323
  [0.5.6]: https://github.com/yaniswav/TopicForge/compare/v0.5.5...v0.5.6
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: topicforge
3
- Version: 0.6.1
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
 
@@ -191,7 +195,7 @@ When on, each tool call emits one event with exactly six fields:
191
195
  | `tool_name` | `"list_topics"` | One of the twelve tools, never argument values |
192
196
  | `latency_ms` | `12.34` | Handler wall-clock duration, 2 decimals |
193
197
  | `mode` | `"mock"` | Mode of the adapter actually serving: `mock` or `live` |
194
- | `version` | `"0.6.1"` | TopicForge server version |
198
+ | `version` | `"0.6.2"` | TopicForge server version |
195
199
  | `session_id` | `"a1b2c3..."` | Random UUID per process, never persisted |
196
200
  | `success` | `true` | Whether the handler returned or raised |
197
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
 
@@ -131,7 +135,7 @@ When on, each tool call emits one event with exactly six fields:
131
135
  | `tool_name` | `"list_topics"` | One of the twelve tools, never argument values |
132
136
  | `latency_ms` | `12.34` | Handler wall-clock duration, 2 decimals |
133
137
  | `mode` | `"mock"` | Mode of the adapter actually serving: `mock` or `live` |
134
- | `version` | `"0.6.1"` | TopicForge server version |
138
+ | `version` | `"0.6.2"` | TopicForge server version |
135
139
  | `session_id` | `"a1b2c3..."` | Random UUID per process, never persisted |
136
140
  | `success` | `true` | Whether the handler returned or raised |
137
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.1
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.1 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.
@@ -4,7 +4,7 @@ TopicForge is a read-only MCP server for ROS 2 and DDS. This plugin bundles the
4
4
 
5
5
  ## What is included
6
6
 
7
- - MCP server `topicforge`, started with `uvx` from the `topicforge` package on PyPI (version pinned to 0.6.1). 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`.
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
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
9
  - Skill `inspect-ros2-robot`: listing topics, sampling messages and reading bags, including large arrays and simulation time.
10
10
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "topicforge"
7
- version = "0.6.1"
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.1"
3
+ __version__ = "0.6.2"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -0,0 +1,150 @@
1
+ """Fail when the version strings scattered across the repo drift apart.
2
+
3
+ One release touches the package, the registry manifest, the Claude plugin, the
4
+ Gemini extension, the MCP Bundle and the pins copied into the docs. Forgetting
5
+ one of them ships a client config that installs the wrong version.
6
+
7
+ Repo-only: the sdist does not carry server.json, the plugin or the bundle, so the
8
+ whole module is skipped there. Nothing reads a file at import time.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import base64
14
+ import json
15
+ import re
16
+ import urllib.parse
17
+ from collections.abc import Callable
18
+ from pathlib import Path
19
+
20
+ import pytest
21
+
22
+ import topicforge
23
+
24
+ ROOT = Path(__file__).resolve().parent.parent
25
+
26
+ pytestmark = pytest.mark.skipif(
27
+ not (ROOT / "server.json").exists(), reason="repo-only test (not in the sdist)"
28
+ )
29
+
30
+ PIN_RE = re.compile(r"topicforge\[dds\]==([0-9][^\s\"'\\)&%]*)")
31
+ BARE_PIN_RE = re.compile(r"==(\d+\.\d+\.\d+)")
32
+ VERSION_LINE_RE = re.compile(r'^version\s*=\s*"([^"]+)"', re.MULTILINE)
33
+ LINK_RES = {
34
+ "cursor https": re.compile(r"\((https://cursor\.com/install-mcp\?[^)\s]+)\)"),
35
+ "vscode https": re.compile(r"\((https://vscode\.dev/redirect/mcp/install\?[^)\s]+)\)"),
36
+ }
37
+
38
+
39
+ def _text(rel: str) -> str:
40
+ return (ROOT / rel).read_text(encoding="utf-8")
41
+
42
+
43
+ def _json(rel: str) -> dict:
44
+ return json.loads(_text(rel))
45
+
46
+
47
+ def _toml_version(rel: str) -> str:
48
+ match = VERSION_LINE_RE.search(_text(rel))
49
+ assert match, f"no version line in {rel}"
50
+ return match.group(1)
51
+
52
+
53
+ def _pin_in_args(args: list[str]) -> str:
54
+ match = PIN_RE.fullmatch(args[args.index("--from") + 1])
55
+ assert match, f"no topicforge[dds]==X pin in {args}"
56
+ return match.group(1)
57
+
58
+
59
+ def _mcpb_pin() -> str:
60
+ match = PIN_RE.search(_text("mcpb/pyproject.toml"))
61
+ assert match, "no topicforge[dds]==X pin in mcpb/pyproject.toml"
62
+ return match.group(1)
63
+
64
+
65
+ def _server_args(rel: str) -> list[str]:
66
+ return _json(rel)["mcpServers"]["topicforge"]["args"]
67
+
68
+
69
+ SOURCES: dict[str, Callable[[], str]] = {
70
+ "pyproject.toml": lambda: _toml_version("pyproject.toml"),
71
+ "server.json": lambda: _json("server.json")["version"],
72
+ "server.json packages[0]": lambda: _json("server.json")["packages"][0]["version"],
73
+ "plugin.json": lambda: _json("plugin/.claude-plugin/plugin.json")["version"],
74
+ "plugin .mcp.json pin": lambda: _pin_in_args(_server_args("plugin/.mcp.json")),
75
+ "gemini-extension.json": lambda: _json("gemini-extension.json")["version"],
76
+ "gemini-extension.json pin": lambda: _pin_in_args(_server_args("gemini-extension.json")),
77
+ "mcpb manifest": lambda: _json("mcpb/manifest.json")["version"],
78
+ "mcpb pyproject version": lambda: _toml_version("mcpb/pyproject.toml"),
79
+ "mcpb pyproject pin": _mcpb_pin,
80
+ }
81
+
82
+
83
+ @pytest.mark.parametrize("name", sorted(SOURCES))
84
+ def test_version_matches_package(name: str) -> None:
85
+ assert SOURCES[name]() == topicforge.__version__, f"{name} drifted from __version__"
86
+
87
+
88
+ def test_pyproject_matches_package_and_bundle() -> None:
89
+ assert _toml_version("pyproject.toml") == topicforge.__version__
90
+ assert _toml_version("pyproject.toml") == _toml_version("mcpb/pyproject.toml")
91
+
92
+
93
+ @pytest.mark.parametrize("rel", ["docs/CLIENTS.md", "README.md", "docs/TESTING.md"])
94
+ def test_doc_pins_match_package(rel: str) -> None:
95
+ text = _text(rel)
96
+ pins = set(PIN_RE.findall(text)) | set(BARE_PIN_RE.findall(text))
97
+ assert pins <= {topicforge.__version__}, f"{rel} has stale pins: {sorted(pins)}"
98
+
99
+
100
+ def _links(text: str) -> dict[str, str]:
101
+ found = {}
102
+ for name, regex in LINK_RES.items():
103
+ match = regex.search(text)
104
+ assert match, f"{name} install link missing"
105
+ found[name] = match.group(1)
106
+ return found
107
+
108
+
109
+ def _decode_cursor(url: str) -> dict:
110
+ query = urllib.parse.parse_qs(urllib.parse.urlsplit(url).query)
111
+ assert query["name"] == ["topicforge"]
112
+ return json.loads(base64.b64decode(query["config"][0]))
113
+
114
+
115
+ def _decode_vscode_redirect(url: str) -> dict:
116
+ query = urllib.parse.parse_qs(urllib.parse.urlsplit(url).query)
117
+ assert query["name"] == ["topicforge"]
118
+ return json.loads(query["config"][0])
119
+
120
+
121
+ def test_install_links_decode_to_pinned_config() -> None:
122
+ clients = _text("docs/CLIENTS.md")
123
+ links = _links(clients)
124
+ cursor_cfg = _decode_cursor(links["cursor https"])
125
+ vscode_cfg = _decode_vscode_redirect(links["vscode https"])
126
+ assert _pin_in_args(cursor_cfg["args"]) == topicforge.__version__
127
+ assert vscode_cfg == cursor_cfg
128
+
129
+ # Raw scheme fallbacks kept in CLIENTS.md decode to the same config.
130
+ raw_cursor = re.search(r"`(cursor://[^`\s]+)`", clients)
131
+ assert raw_cursor, "raw cursor:// fallback missing"
132
+ assert _decode_cursor(raw_cursor.group(1)) == cursor_cfg
133
+ raw_vscode = re.search(r"`vscode:mcp/install\?([^`\s]+)`", clients)
134
+ assert raw_vscode, "raw vscode: fallback missing"
135
+ assert json.loads(urllib.parse.unquote(raw_vscode.group(1))) == {
136
+ "name": "topicforge",
137
+ **cursor_cfg,
138
+ }
139
+
140
+
141
+ @pytest.mark.parametrize("rel", ["README.md", "docs/TESTING.md"])
142
+ def test_other_docs_links_identical_to_clients_doc(rel: str) -> None:
143
+ text = _text(rel)
144
+ expected = _links(_text("docs/CLIENTS.md"))
145
+ for name, regex in LINK_RES.items():
146
+ found = {m for m in regex.findall(text)}
147
+ if rel == "README.md":
148
+ assert found == {expected[name]}, f"{rel}: {name} link differs from CLIENTS.md"
149
+ else:
150
+ assert found <= {expected[name]}, f"{rel}: {name} link differs from CLIENTS.md"
File without changes
File without changes
File without changes
File without changes