topicforge 0.6.2__tar.gz → 0.6.4__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 (170) hide show
  1. {topicforge-0.6.2 → topicforge-0.6.4}/CHANGELOG.md +75 -1
  2. {topicforge-0.6.2 → topicforge-0.6.4}/PKG-INFO +12 -9
  3. {topicforge-0.6.2 → topicforge-0.6.4}/README.md +9 -6
  4. {topicforge-0.6.2 → topicforge-0.6.4}/docs/CLIENTS.md +31 -21
  5. {topicforge-0.6.2 → topicforge-0.6.4}/docs/DDS_QUICKSTART.md +1 -1
  6. {topicforge-0.6.2 → topicforge-0.6.4}/docs/TESTING.md +4 -4
  7. {topicforge-0.6.2 → topicforge-0.6.4}/docs/TROUBLESHOOTING.md +1 -1
  8. {topicforge-0.6.2 → topicforge-0.6.4}/docs/TUTORIEL.md +1 -1
  9. {topicforge-0.6.2 → topicforge-0.6.4}/plugin/README.md +7 -1
  10. topicforge-0.6.4/plugin/pyproject.toml +16 -0
  11. {topicforge-0.6.2 → topicforge-0.6.4}/pyproject.toml +23 -13
  12. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/__init__.py +1 -1
  13. topicforge-0.6.4/src/topicforge/adapters/common/stamps.py +58 -0
  14. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/ros2_live/echo_parser.py +8 -16
  15. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/ros2_mock/adapter.py +3 -0
  16. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/ros2_mock/fixtures.py +7 -4
  17. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/models/schemas.py +9 -5
  18. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/server/app.py +15 -0
  19. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/services/bag_service.py +7 -17
  20. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/services/factory.py +51 -8
  21. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/services/health.py +1 -1
  22. topicforge-0.6.4/src/topicforge/tools/annotations.py +32 -0
  23. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/tools/handlers.py +32 -16
  24. {topicforge-0.6.2 → topicforge-0.6.4}/tests/conftest.py +29 -0
  25. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_bag_omnisim_humble.py +26 -1
  26. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_config.py +39 -9
  27. topicforge-0.6.4/tests/test_dds_inactive_reason.py +188 -0
  28. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_discovery_tracker.py +6 -2
  29. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_factory.py +28 -1
  30. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_mock_adapter.py +4 -3
  31. topicforge-0.6.4/tests/test_stamps.py +107 -0
  32. topicforge-0.6.4/tests/test_tool_annotations.py +44 -0
  33. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_version_consistency.py +50 -2
  34. topicforge-0.6.2/tests/test_dds_inactive_reason.py +0 -94
  35. {topicforge-0.6.2 → topicforge-0.6.4}/.gitignore +0 -0
  36. {topicforge-0.6.2 → topicforge-0.6.4}/LICENSE +0 -0
  37. {topicforge-0.6.2 → topicforge-0.6.4}/docs/VALIDATION.md +0 -0
  38. {topicforge-0.6.2 → topicforge-0.6.4}/docs/dds-interop-matrix.md +0 -0
  39. {topicforge-0.6.2 → topicforge-0.6.4}/examples/README.md +0 -0
  40. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/00_hello_pub_sub/README.md +0 -0
  41. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/01_who_is_on_the_bus/README.md +0 -0
  42. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/02_why_cant_they_talk/README.md +0 -0
  43. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/03_a_node_crashed/README.md +0 -0
  44. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/04_late_joiner_misses_data/README.md +0 -0
  45. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/05_reliability_in_code/README.md +0 -0
  46. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/06_durability_late_joiner_in_code/README.md +0 -0
  47. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/07_deadline_in_code/README.md +0 -0
  48. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/08_crash_seen_from_inside/README.md +0 -0
  49. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/10_lidar_silent_after_driver_swap/README.md +0 -0
  50. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/11_who_talks_to_whom/README.md +0 -0
  51. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/12_safety_monitor_dropout/README.md +0 -0
  52. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/13_deadline_not_offered/README.md +0 -0
  53. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/14_restart_loop/README.md +0 -0
  54. {topicforge-0.6.2 → topicforge-0.6.4}/examples/dds/README.md +0 -0
  55. {topicforge-0.6.2 → topicforge-0.6.4}/plugin/LICENSE +0 -0
  56. {topicforge-0.6.2 → topicforge-0.6.4}/scripts/agent_eval/README.md +0 -0
  57. {topicforge-0.6.2 → topicforge-0.6.4}/scripts/integration/README.md +0 -0
  58. {topicforge-0.6.2 → topicforge-0.6.4}/scripts/integration/publishers/cyclone_c/README.md +0 -0
  59. {topicforge-0.6.2 → topicforge-0.6.4}/scripts/integration/publishers/cyclone_cpp/README.md +0 -0
  60. {topicforge-0.6.2 → topicforge-0.6.4}/scripts/integration/publishers/cyclone_rust/README.md +0 -0
  61. {topicforge-0.6.2 → topicforge-0.6.4}/scripts/integration/publishers/dust_py/README.md +0 -0
  62. {topicforge-0.6.2 → topicforge-0.6.4}/scripts/integration/publishers/fast_publisher_cpp/README.md +0 -0
  63. {topicforge-0.6.2 → topicforge-0.6.4}/scripts/integration/publishers/fast_py/README.md +0 -0
  64. {topicforge-0.6.2 → topicforge-0.6.4}/scripts/integration/publishers/opensplice_publisher/README.md +0 -0
  65. {topicforge-0.6.2 → topicforge-0.6.4}/scripts/integration/publishers/rti_c/README.md +0 -0
  66. {topicforge-0.6.2 → topicforge-0.6.4}/scripts/integration/publishers/rti_cpp/README.md +0 -0
  67. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/__main__.py +0 -0
  68. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/__init__.py +0 -0
  69. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/base.py +0 -0
  70. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/__init__.py +0 -0
  71. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/cdr_decoder.py +0 -0
  72. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/dds_helpers.py +0 -0
  73. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/dds_introspection.py +0 -0
  74. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/discovery_tracker.py +0 -0
  75. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/endpoints.py +0 -0
  76. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/lifecycle.py +0 -0
  77. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/metrics_buffer.py +0 -0
  78. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/qos_analyzer.py +0 -0
  79. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/qos_endpoints.py +0 -0
  80. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/qos_normalize.py +0 -0
  81. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/qos_scan.py +0 -0
  82. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/topic_filter.py +0 -0
  83. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/common/xtypes.py +0 -0
  84. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/composite.py +0 -0
  85. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/dds_cyclone/__init__.py +0 -0
  86. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/dds_cyclone/adapter.py +0 -0
  87. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/dds_dust/__init__.py +0 -0
  88. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/dds_dust/adapter.py +0 -0
  89. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/dds_fast/__init__.py +0 -0
  90. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/dds_fast/adapter.py +0 -0
  91. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/dds_opendds/__init__.py +0 -0
  92. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/dds_opendds/adapter.py +0 -0
  93. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
  94. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/ros2_live/adapter.py +0 -0
  95. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/ros2_live/echo_stream.py +0 -0
  96. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/ros2_live/parsers.py +0 -0
  97. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/ros2_live/process_tree.py +0 -0
  98. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
  99. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/config/__init__.py +0 -0
  100. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/config/settings.py +0 -0
  101. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/constants.py +0 -0
  102. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/models/__init__.py +0 -0
  103. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/server/__init__.py +0 -0
  104. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/services/__init__.py +0 -0
  105. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/services/bag_stats.py +0 -0
  106. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/services/inspector.py +0 -0
  107. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/services/sample_budget.py +0 -0
  108. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/telemetry/__init__.py +0 -0
  109. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/telemetry/client.py +0 -0
  110. {topicforge-0.6.2 → topicforge-0.6.4}/src/topicforge/tools/__init__.py +0 -0
  111. {topicforge-0.6.2 → topicforge-0.6.4}/tests/__init__.py +0 -0
  112. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_echo/image_noarr.yaml +0 -0
  113. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_echo/image_trunc4.yaml +0 -0
  114. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_echo/scan_edge.yaml +0 -0
  115. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_echo/scan_noarr.yaml +0 -0
  116. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_echo/scan_trunc128.yaml +0 -0
  117. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_echo/scan_trunc3.yaml +0 -0
  118. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_echo/string_latched.yaml +0 -0
  119. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_echo/twist.yaml +0 -0
  120. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_topic_info_verbose_cmd_vel.txt +0 -0
  121. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_topic_info_verbose_parameter_events.txt +0 -0
  122. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_topic_info_verbose_scan.txt +0 -0
  123. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_topic_info_verbose_tf.txt +0 -0
  124. {topicforge-0.6.2 → topicforge-0.6.4}/tests/fixtures/ros2_topic_info_verbose_tf_static.txt +0 -0
  125. {topicforge-0.6.2 → topicforge-0.6.4}/tests/integration/__init__.py +0 -0
  126. {topicforge-0.6.2 → topicforge-0.6.4}/tests/integration/ros2/Dockerfile +0 -0
  127. {topicforge-0.6.2 → topicforge-0.6.4}/tests/integration/ros2/__init__.py +0 -0
  128. {topicforge-0.6.2 → topicforge-0.6.4}/tests/integration/ros2/entrypoint.sh +0 -0
  129. {topicforge-0.6.2 → topicforge-0.6.4}/tests/integration/ros2/publisher.py +0 -0
  130. {topicforge-0.6.2 → topicforge-0.6.4}/tests/integration/ros2/run_bench.py +0 -0
  131. {topicforge-0.6.2 → topicforge-0.6.4}/tests/integration/ros2/test_live_adapter.py +0 -0
  132. {topicforge-0.6.2 → topicforge-0.6.4}/tests/integration/test_real_bus.py +0 -0
  133. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_analyze_bag_multi_format.py +0 -0
  134. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_bag_service.py +0 -0
  135. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_bag_stats.py +0 -0
  136. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_cdr_decoder.py +0 -0
  137. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_composite_adapter.py +0 -0
  138. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_cyclone_adapter.py +0 -0
  139. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_dds_cross_vendor.py +0 -0
  140. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_dds_helpers.py +0 -0
  141. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_dds_introspection.py +0 -0
  142. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_dds_qos_normalization.py +0 -0
  143. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_dds_schemas.py +0 -0
  144. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_dust_adapter.py +0 -0
  145. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_echo_parser.py +0 -0
  146. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_echo_stream.py +0 -0
  147. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_endpoints.py +0 -0
  148. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_example_node_spec.py +0 -0
  149. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_fast_adapter.py +0 -0
  150. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_health.py +0 -0
  151. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_history_and_hints.py +0 -0
  152. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_honest_outputs.py +0 -0
  153. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_inspector.py +0 -0
  154. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_lifecycle_buffer.py +0 -0
  155. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_live_adapter_graph.py +0 -0
  156. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_live_adapter_parse.py +0 -0
  157. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_live_adapter_subprocess.py +0 -0
  158. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_live_sample_notes.py +0 -0
  159. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_metrics_buffer.py +0 -0
  160. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_opendds_adapter.py +0 -0
  161. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_peek_bag_samples.py +0 -0
  162. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_process_tree.py +0 -0
  163. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_qos_analyzer.py +0 -0
  164. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_qos_endpoints.py +0 -0
  165. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_qos_scan.py +0 -0
  166. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_sample_options.py +0 -0
  167. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_telemetry.py +0 -0
  168. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_tools_integration.py +0 -0
  169. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_topic_metrics.py +0 -0
  170. {topicforge-0.6.2 → topicforge-0.6.4}/tests/test_xtypes.py +0 -0
@@ -7,6 +7,78 @@ and this project adheres to [Semantic Versioning 2.0.0](https://semver.org/spec/
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.4] - 2026-10-08
11
+
12
+ ### Added
13
+
14
+ - `plugin/` is now also an Agent Plugins 1.0.0 package (open standard used by Cursor and
15
+ other clients): `plugin/plugin.json` and `plugin/mcp.json` next to the Claude files,
16
+ with the same pinned `uvx` launch and fixed env defaults (no per-user configuration in
17
+ that format). A repo-root `.cursor-plugin/marketplace.json` points Cursor at `plugin/`.
18
+ The version-consistency test covers the new files.
19
+
20
+ ### Fixed
21
+
22
+ - `peek_bag_samples` no longer fails with "requires the `rosbags` library" for a plain
23
+ `pip install topicforge`, `uvx topicforge` or the Claude / Agent plugin: `rosbags` was
24
+ only in the `[bags]` extra, which none of those select.
25
+ - `sample_messages` on `/clock` (`rosgraph_msgs/Clock`), `/tf` and `/tf_static`
26
+ (`tf2_msgs/TFMessage`) and `/rosout` (`rcl_interfaces/Log`) returned `timestamp_ns` 0 and
27
+ `stamp_source` `none` although the time is in the body. It now reads `clock`,
28
+ `transforms[0].header.stamp` (only when the list is not empty) and `stamp`. Messages
29
+ with a top-level `header.stamp` are unchanged. `peek_bag_samples` applies the same rule,
30
+ so these topics no longer fall back to the bag record time. The mock `/tf` samples follow it.
31
+
32
+ - The MCP `initialize` handshake reported the `mcp` library version (for example 1.30.0) as
33
+ `serverInfo.version`; it now reports TopicForge's own version.
34
+
35
+ ### Changed
36
+
37
+ - `rosbags` (pure Python, wheels for linux-aarch64 and CPython 3.14 for all of its
38
+ dependencies) is now a core dependency, `>=0.11.3` as before. `topicforge[bags]` stays
39
+ as an empty alias so existing install commands keep working.
40
+ - `stamp_source` accepts one additive value, `payload`: the time comes from the message
41
+ body (`Clock`, `TFMessage`, `Log`) rather than a `header`. Existing values are unchanged.
42
+ - The error raised when `rosbags` cannot be imported now gives the reinstall command.
43
+
44
+ ## [0.6.3] - 2026-10-07
45
+
46
+ ### Added
47
+
48
+ - Platform-aware DDS message: when the Cyclone backend is requested but the binding is
49
+ missing, `dds_inactive_reason` (and the server log) now says why in plain words, for
50
+ example "`cyclonedds` has no prebuilt wheel for linux-aarch64 / Python 3.14", and what
51
+ to do (build it against the Cyclone C library, or use mock). A server that falls back
52
+ to the mock fixtures because of this reports the reason in `health_check` too. Field
53
+ names are unchanged.
54
+ - CI: a `ubuntu-24.04-arm` job that installs the package without a Cyclone wheel and
55
+ checks that the server starts and `health_check` explains, and Python 3.14 in the test
56
+ matrix (the suite runs there with the binding absent).
57
+
58
+ - MCP `ToolAnnotations` on all twelve tools (`title`, `readOnlyHint=true`,
59
+ `destructiveHint=false`, `idempotentHint=true`, per-tool `openWorldHint`),
60
+ built by one shared helper in `tools/annotations.py`, and
61
+ `tests/test_tool_annotations.py`, which fails if any tool lacks them.
62
+
63
+ ### Changed
64
+
65
+ - `cyclonedds` is now a core dependency, restricted by an environment marker to the
66
+ platforms where it ships prebuilt wheels (Python 3.10-3.13 on Linux x86_64, Windows
67
+ AMD64 and macOS), so `uvx topicforge` and `pip install topicforge` bring the DDS
68
+ binding with no extra. Other platforms (Linux aarch64, Python 3.14, Windows ARM64)
69
+ install cleanly without it. `topicforge[dds]` and `topicforge[dds-cyclone]` stay as
70
+ aliases. The default `TOPICFORGE_DDS_BACKEND` is still `mock`.
71
+ - The Claude plugin launches `uvx --python 3.12 --from topicforge==0.6.3 topicforge`
72
+ without an extra; its dependencies are declared in the new `plugin/pyproject.toml`
73
+ (a lock, `plugin/uv.lock`, is generated once the release is on PyPI). The version
74
+ consistency test covers both files.
75
+ - Documented launch commands, the Cursor and VS Code install links and the MCP Bundle
76
+ now use `topicforge==X` instead of `topicforge[dds]==X`.
77
+ - `SECURITY.md`: new section "Read-only guarantee: architecture, declaration,
78
+ proof" (including the limits of passivity on a DDS domain and the ros2 CLI),
79
+ Popen streaming with process-tree kill documented, any MCP client named, and
80
+ a remote-access paragraph.
81
+
10
82
  ## [0.6.2] - 2026-10-06
11
83
 
12
84
  ### Added
@@ -1316,7 +1388,9 @@ Initial MVP release of TopicForge: ROS Topic Inspector & Bag Analyzer MCP server
1316
1388
  - The write path (publishing, commanding robots) is intentionally out of scope for the MVP.
1317
1389
  - `analyze_bag` in live mode parses `ros2 bag info` text output; deeper anomaly detection remains mock-only for now.
1318
1390
 
1319
- [Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.6.2...HEAD
1391
+ [Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.6.4...HEAD
1392
+ [0.6.4]: https://github.com/yaniswav/TopicForge/compare/v0.6.3...v0.6.4
1393
+ [0.6.3]: https://github.com/yaniswav/TopicForge/compare/v0.6.2...v0.6.3
1320
1394
  [0.6.2]: https://github.com/yaniswav/TopicForge/compare/v0.6.1...v0.6.2
1321
1395
  [0.6.1]: https://github.com/yaniswav/TopicForge/compare/v0.6.0...v0.6.1
1322
1396
  [0.6.0]: https://github.com/yaniswav/TopicForge/compare/v0.5.6...v0.6.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: topicforge
3
- Version: 0.6.2
3
+ Version: 0.6.4
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
@@ -40,13 +40,14 @@ Classifier: Programming Language :: Python :: 3.11
40
40
  Classifier: Programming Language :: Python :: 3.12
41
41
  Classifier: Programming Language :: Python :: 3.13
42
42
  Requires-Python: >=3.10
43
+ Requires-Dist: cyclonedds>=0.10; python_version < '3.14' and ((platform_system == 'Linux' and platform_machine == 'x86_64') or (platform_system == 'Windows' and platform_machine == 'AMD64') or platform_system == 'Darwin')
43
44
  Requires-Dist: mcp<2,>=1.0.0
44
45
  Requires-Dist: pydantic>=2.6
45
46
  Requires-Dist: pyyaml>=6
47
+ Requires-Dist: rosbags>=0.11.3
46
48
  Provides-Extra: all
47
49
  Requires-Dist: cyclonedds>=0.10; extra == 'all'
48
50
  Provides-Extra: bags
49
- Requires-Dist: rosbags>=0.11.3; extra == 'bags'
50
51
  Provides-Extra: dds
51
52
  Requires-Dist: cyclonedds>=0.10; extra == 'dds'
52
53
  Provides-Extra: dds-cyclone
@@ -54,7 +55,6 @@ Requires-Dist: cyclonedds>=0.10; extra == 'dds-cyclone'
54
55
  Provides-Extra: dev
55
56
  Requires-Dist: pytest-cov>=5.0; extra == 'dev'
56
57
  Requires-Dist: pytest>=8.0; extra == 'dev'
57
- Requires-Dist: rosbags>=0.11.3; extra == 'dev'
58
58
  Requires-Dist: ruff>=0.4; extra == 'dev'
59
59
  Description-Content-Type: text/markdown
60
60
 
@@ -70,6 +70,8 @@ Description-Content-Type: text/markdown
70
70
 
71
71
  A read-only MCP (Model Context Protocol) server that lets an AI agent inspect a ROS2 graph, recorded bag files and the DDS layer underneath ROS. The code has no write path: it cannot publish to the bus or command a robot, and there is no permission system to configure.
72
72
 
73
+ Every tool also declares itself read-only in the MCP protocol (`readOnlyHint`, `destructiveHint=false`, `idempotentHint`, with an honest `openWorldHint`), and a test fails if any tool lacks it. See [`SECURITY.md`](SECURITY.md#read-only-guarantee-architecture-declaration-proof) for the guarantee and its limits.
74
+
73
75
  It gives the agent twelve typed tools that return frozen Pydantic schemas, identical whether the server talks to a real robot or to its built-in mock fixtures. Ask why `nav_planner` gets no scan, and the agent reads the bus, finds the BEST_EFFORT writer facing a RELIABLE reader and names the incompatible policy (see [`examples/02-debug-qos-mismatch.md`](examples/02-debug-qos-mismatch.md)). It is meant for ROS2 developers, robotics ML/CV engineers and teams that cannot accept a write path into a production stack.
74
76
 
75
77
  For DDS, TopicForge joins a domain as a read-only participant through one open-source binding (Eclipse CycloneDDS from PyPI) and reads the builtin discovery topics that the OMG DDS-RTPS protocol standardizes. So far the author has observed Cyclone DDS and Dust DDS participants on a live bus. RTI Connext, OpenDDS, CoreDX and Fast DDS announce themselves through the same standard discovery, but none of them has been observed yet. This covers discovery only: participants, readers, writers and their QoS. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md).
@@ -100,7 +102,7 @@ The server speaks MCP over stdio and waits for a client, so wire it into one. Fo
100
102
 
101
103
  Then ask it to list the topics or to analyze `/tmp/demo.mcap`. For Claude Code: `claude mcp add topicforge -- topicforge`.
102
104
 
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).
105
+ **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=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjYuNCIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%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%3D%3D0.6.4%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
106
 
105
107
  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).
106
108
 
@@ -120,7 +122,7 @@ Every response except `health_check` carries `mode_effective` (`"live"` or `"moc
120
122
  | `peek_dds_samples` | Raw DDS samples; structured on the three builtin discovery topics, presence-only on user topics |
121
123
  | `participant_events` | Timeline of participant `discovered` / `lost` events |
122
124
  | `topic_metrics` | Frequency, sequence-gap and latency schema; data only for builtin discovery topics |
123
- | `peek_bag_samples` | Decoded samples from a recorded bag, including ROS 1 `.bag` (needs `pip install topicforge[bags]`) |
125
+ | `peek_bag_samples` | Decoded samples from a recorded bag, including ROS 1 `.bag` (`rosbags` ships with the package) |
124
126
  | `list_endpoints` | DDS writers and readers with structured QoS, per-topic roll-up that flags orphans (writer with no reader, reader with no writer) |
125
127
 
126
128
  Walkthroughs against the mock, each with the exact tool calls and payloads, are in [`examples/`](examples/README.md). To run the DDS tools against a real bus with several programs and vendors, see [`examples/dds/README.md`](examples/dds/README.md) (`python examples/dds/run_all.py`).
@@ -138,7 +140,8 @@ Walkthroughs against the mock, each with the exact tool calls and payloads, are
138
140
  ## DDS backends
139
141
 
140
142
  ```bash
141
- pip install topicforge[dds] # Eclipse CycloneDDS ([dds-cyclone] is the same thing)
143
+ pip install topicforge # includes Eclipse CycloneDDS where wheels exist (Python 3.10-3.13; Linux x86_64, Windows x64, macOS)
144
+ pip install "topicforge[dds]" # alias, kept so older commands work; forces a source build elsewhere (needs the Cyclone C library)
142
145
  TOPICFORGE_DDS_BACKEND=cyclone python -m topicforge
143
146
  ```
144
147
 
@@ -171,8 +174,8 @@ External validation against a simulated robot's ground truth: [docs/VALIDATION.m
171
174
  - Single domain: the server observes the domain it joined at startup; changing it needs a restart.
172
175
  - 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.
173
176
  - Fast DDS serves no `list_endpoints`.
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.
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.
177
+ - `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), the time in the body of `/clock`, `/tf` and `/rosout` messages (`stamp_source` `payload`), or 0 for other 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.
178
+ - `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`), the body time of `Clock`, `TFMessage` and `Log` messages (`payload`) or, without either, 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.
176
179
  - Synchronous handlers: the tools run on the MCP event loop; on Windows a hung `ros2` launcher can block the server.
177
180
  - No streaming or push subscriptions: tools are strictly request/response.
178
181
 
@@ -195,7 +198,7 @@ When on, each tool call emits one event with exactly six fields:
195
198
  | `tool_name` | `"list_topics"` | One of the twelve tools, never argument values |
196
199
  | `latency_ms` | `12.34` | Handler wall-clock duration, 2 decimals |
197
200
  | `mode` | `"mock"` | Mode of the adapter actually serving: `mock` or `live` |
198
- | `version` | `"0.6.2"` | TopicForge server version |
201
+ | `version` | `"0.6.4"` | TopicForge server version |
199
202
  | `session_id` | `"a1b2c3..."` | Random UUID per process, never persisted |
200
203
  | `success` | `true` | Whether the handler returned or raised |
201
204
 
@@ -10,6 +10,8 @@
10
10
 
11
11
  A read-only MCP (Model Context Protocol) server that lets an AI agent inspect a ROS2 graph, recorded bag files and the DDS layer underneath ROS. The code has no write path: it cannot publish to the bus or command a robot, and there is no permission system to configure.
12
12
 
13
+ Every tool also declares itself read-only in the MCP protocol (`readOnlyHint`, `destructiveHint=false`, `idempotentHint`, with an honest `openWorldHint`), and a test fails if any tool lacks it. See [`SECURITY.md`](SECURITY.md#read-only-guarantee-architecture-declaration-proof) for the guarantee and its limits.
14
+
13
15
  It gives the agent twelve typed tools that return frozen Pydantic schemas, identical whether the server talks to a real robot or to its built-in mock fixtures. Ask why `nav_planner` gets no scan, and the agent reads the bus, finds the BEST_EFFORT writer facing a RELIABLE reader and names the incompatible policy (see [`examples/02-debug-qos-mismatch.md`](examples/02-debug-qos-mismatch.md)). It is meant for ROS2 developers, robotics ML/CV engineers and teams that cannot accept a write path into a production stack.
14
16
 
15
17
  For DDS, TopicForge joins a domain as a read-only participant through one open-source binding (Eclipse CycloneDDS from PyPI) and reads the builtin discovery topics that the OMG DDS-RTPS protocol standardizes. So far the author has observed Cyclone DDS and Dust DDS participants on a live bus. RTI Connext, OpenDDS, CoreDX and Fast DDS announce themselves through the same standard discovery, but none of them has been observed yet. This covers discovery only: participants, readers, writers and their QoS. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md).
@@ -40,7 +42,7 @@ The server speaks MCP over stdio and waits for a client, so wire it into one. Fo
40
42
 
41
43
  Then ask it to list the topics or to analyze `/tmp/demo.mcap`. For Claude Code: `claude mcp add topicforge -- topicforge`.
42
44
 
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).
45
+ **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=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjYuNCIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%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%3D%3D0.6.4%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
46
 
45
47
  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).
46
48
 
@@ -60,7 +62,7 @@ Every response except `health_check` carries `mode_effective` (`"live"` or `"moc
60
62
  | `peek_dds_samples` | Raw DDS samples; structured on the three builtin discovery topics, presence-only on user topics |
61
63
  | `participant_events` | Timeline of participant `discovered` / `lost` events |
62
64
  | `topic_metrics` | Frequency, sequence-gap and latency schema; data only for builtin discovery topics |
63
- | `peek_bag_samples` | Decoded samples from a recorded bag, including ROS 1 `.bag` (needs `pip install topicforge[bags]`) |
65
+ | `peek_bag_samples` | Decoded samples from a recorded bag, including ROS 1 `.bag` (`rosbags` ships with the package) |
64
66
  | `list_endpoints` | DDS writers and readers with structured QoS, per-topic roll-up that flags orphans (writer with no reader, reader with no writer) |
65
67
 
66
68
  Walkthroughs against the mock, each with the exact tool calls and payloads, are in [`examples/`](examples/README.md). To run the DDS tools against a real bus with several programs and vendors, see [`examples/dds/README.md`](examples/dds/README.md) (`python examples/dds/run_all.py`).
@@ -78,7 +80,8 @@ Walkthroughs against the mock, each with the exact tool calls and payloads, are
78
80
  ## DDS backends
79
81
 
80
82
  ```bash
81
- pip install topicforge[dds] # Eclipse CycloneDDS ([dds-cyclone] is the same thing)
83
+ pip install topicforge # includes Eclipse CycloneDDS where wheels exist (Python 3.10-3.13; Linux x86_64, Windows x64, macOS)
84
+ pip install "topicforge[dds]" # alias, kept so older commands work; forces a source build elsewhere (needs the Cyclone C library)
82
85
  TOPICFORGE_DDS_BACKEND=cyclone python -m topicforge
83
86
  ```
84
87
 
@@ -111,8 +114,8 @@ External validation against a simulated robot's ground truth: [docs/VALIDATION.m
111
114
  - Single domain: the server observes the domain it joined at startup; changing it needs a restart.
112
115
  - 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.
113
116
  - Fast DDS serves no `list_endpoints`.
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.
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.
117
+ - `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), the time in the body of `/clock`, `/tf` and `/rosout` messages (`stamp_source` `payload`), or 0 for other 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.
118
+ - `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`), the body time of `Clock`, `TFMessage` and `Log` messages (`payload`) or, without either, 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.
116
119
  - Synchronous handlers: the tools run on the MCP event loop; on Windows a hung `ros2` launcher can block the server.
117
120
  - No streaming or push subscriptions: tools are strictly request/response.
118
121
 
@@ -135,7 +138,7 @@ When on, each tool call emits one event with exactly six fields:
135
138
  | `tool_name` | `"list_topics"` | One of the twelve tools, never argument values |
136
139
  | `latency_ms` | `12.34` | Handler wall-clock duration, 2 decimals |
137
140
  | `mode` | `"mock"` | Mode of the adapter actually serving: `mock` or `live` |
138
- | `version` | `"0.6.2"` | TopicForge server version |
141
+ | `version` | `"0.6.4"` | TopicForge server version |
139
142
  | `session_id` | `"a1b2c3..."` | Random UUID per process, never persisted |
140
143
  | `success` | `true` | Whether the handler returned or raised |
141
144
 
@@ -1,12 +1,12 @@
1
1
  # Using TopicForge from your MCP client
2
2
 
3
- Last checked 2026-10-06.
3
+ Last checked 2026-10-07.
4
4
 
5
5
  TopicForge (ROS 2 / DDS) is a local, read-only MCP server that speaks stdio.
6
6
  Every client below launches it as a child process with the same command:
7
7
 
8
8
  ```
9
- uvx --from "topicforge[dds]==0.6.2" topicforge
9
+ uvx --from "topicforge==0.6.4" topicforge
10
10
  ```
11
11
 
12
12
  and the same three environment variables:
@@ -14,14 +14,14 @@ and the same three environment variables:
14
14
  | Variable | Value | Meaning |
15
15
  | --- | --- | --- |
16
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. |
17
+ | `TOPICFORGE_DDS_BACKEND` | `cyclone` | Join the DDS bus as a read-only participant (the `cyclonedds` binding is installed with TopicForge on supported platforms). Use `mock` for a demo with no robot. |
18
18
  | `TOPICFORGE_DDS_DOMAIN_ID` | `0` | The one DDS domain to observe (0-232). Fixed at startup: restart the server to change it. |
19
19
 
20
20
  The registry name is `io.github.yaniswav/topicforge`. Where a client asks for a
21
21
  display name, use **TopicForge (ROS 2 / DDS)**: an unrelated SEO product is also
22
22
  called TopicForge in the MCP registries.
23
23
 
24
- The version pin (`==0.6.2`) is moved on every release together with the other
24
+ The version pin (`==0.6.4`) is moved on every release together with the other
25
25
  version strings; a test keeps them in sync.
26
26
 
27
27
  ## Before you start
@@ -36,9 +36,15 @@ version strings; a test keeps them in sync.
36
36
  - **First start is slow.** `uvx` downloads the package and the Cyclone DDS binding
37
37
  the first time. Clients with a short startup timeout may need a retry or a
38
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
39
+ - **Platforms without a Cyclone wheel.** Since 0.6.3 `cyclonedds` is a normal dependency
40
+ of `topicforge`, installed only where it ships prebuilt wheels: Python 3.10 to 3.13 on
41
+ Linux x86_64, Windows x64 and macOS. On Linux ARM (Jetson, Raspberry Pi), Windows ARM64
42
+ or Python 3.14 the server still starts and `health_check` says why the DDS backend is
43
+ off (`dds_inactive_reason`); with a pinned Python, add `"--python", "3.12"` before
44
+ `"--from"` in the args. To build the binding yourself, install the Cyclone C library and
45
+ run `pip install "topicforge[dds]"`. The `[dds]` and `[dds-cyclone]` extras still work
46
+ as aliases, so older commands keep installing.
47
+ - **pip alternative.** `pip install "topicforge==0.6.4"`, then use
42
48
  `"command": "topicforge"` (or `"command": "python", "args": ["-m", "topicforge"]`)
43
49
  with no `args` for uvx. Use the absolute path of the binary if the client does
44
50
  not see your venv.
@@ -66,7 +72,7 @@ say where the file lives and what differs.
66
72
  "mcpServers": {
67
73
  "topicforge": {
68
74
  "command": "uvx",
69
- "args": ["--from", "topicforge[dds]==0.6.2", "topicforge"],
75
+ "args": ["--from", "topicforge==0.6.4", "topicforge"],
70
76
  "env": {
71
77
  "TOPICFORGE_MODE": "auto",
72
78
  "TOPICFORGE_DDS_BACKEND": "cyclone",
@@ -89,12 +95,12 @@ claude plugin install topicforge@topicforge
89
95
  Or just the server:
90
96
 
91
97
  ```
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
98
+ claude mcp add topicforge --env TOPICFORGE_MODE=auto --env TOPICFORGE_DDS_BACKEND=cyclone --env TOPICFORGE_DDS_DOMAIN_ID=0 -- uvx --from "topicforge==0.6.4" topicforge
93
99
  ```
94
100
 
95
101
  ## Claude Desktop
96
102
 
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.
103
+ From 0.6.3: 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
104
 
99
105
  Or edit the config file by hand: `claude_desktop_config.json` (Settings > Developer > Edit Config):
100
106
  `%APPDATA%\Claude\claude_desktop_config.json` on Windows,
@@ -106,18 +112,22 @@ the common JSON shape above and restart the app completely. On Windows, if
106
112
 
107
113
  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
114
 
109
- One click: [Add TopicForge to Cursor](https://cursor.com/install-mcp?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlW2Rkc109PTAuNi4yIiwidG9waWNmb3JnZSJdLCJlbnYiOnsiVE9QSUNGT1JHRV9NT0RFIjoiYXV0byIsIlRPUElDRk9SR0VfRERTX0JBQ0tFTkQiOiJjeWNsb25lIiwiVE9QSUNGT1JHRV9ERFNfRE9NQUlOX0lEIjoiMCJ9fQ%3D%3D)
115
+ One click: [Add TopicForge to Cursor](https://cursor.com/install-mcp?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjYuNCIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%3D)
110
116
 
111
- If that page does not open Cursor, use the raw deeplink: `cursor://anysphere.cursor-deeplink/mcp/install?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlW2Rkc109PTAuNi4yIiwidG9waWNmb3JnZSJdLCJlbnYiOnsiVE9QSUNGT1JHRV9NT0RFIjoiYXV0byIsIlRPUElDRk9SR0VfRERTX0JBQ0tFTkQiOiJjeWNsb25lIiwiVE9QSUNGT1JHRV9ERFNfRE9NQUlOX0lEIjoiMCJ9fQ==`
117
+ If that page does not open Cursor, use the raw deeplink: `cursor://anysphere.cursor-deeplink/mcp/install?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjYuNCIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0=`
112
118
 
113
119
  Or paste the common JSON shape into `~/.cursor/mcp.json` (all projects) or
114
120
  `.cursor/mcp.json` (one project).
115
121
 
122
+ ## Agent Plugins (Cursor and other compatible clients)
123
+
124
+ The `plugin/` folder of this repository is also an [Agent Plugins](https://agent-plugins.org) 1.0.0 package: `plugin.json` (manifest), `mcp.json` (the same pinned `uvx` launch) and `skills/` (two skills). Clients that load that format, such as Cursor, can install it as a plugin and get the server and the skills in one step; the plain JSON config above remains the simplest route for everything else. The format has no user-configuration prompt, so the backend is fixed to `cyclone` and the domain to `0`; edit the `env` block of `mcp.json` to change them (see `plugin/README.md`). Cursor Directory (cursor.directory/plugins/new) and the Cursor marketplace take a repository URL; a repo-root `.cursor-plugin/marketplace.json` points Cursor at the `plugin/` subfolder. Whether each directory scanner accepts a subfolder is unverified.
125
+
116
126
  ## VS Code / GitHub Copilot
117
127
 
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)
128
+ 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%3D%3D0.6.4%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
129
 
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`
130
+ 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%3D%3D0.6.4%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
131
 
122
132
  Or put this in `.vscode/mcp.json` (the key is `servers`, not `mcpServers`), or run
123
133
  "MCP: Add Server" from the Command Palette:
@@ -128,7 +138,7 @@ Or put this in `.vscode/mcp.json` (the key is `servers`, not `mcpServers`), or r
128
138
  "topicforge": {
129
139
  "type": "stdio",
130
140
  "command": "uvx",
131
- "args": ["--from", "topicforge[dds]==0.6.2", "topicforge"],
141
+ "args": ["--from", "topicforge==0.6.4", "topicforge"],
132
142
  "env": {
133
143
  "TOPICFORGE_MODE": "auto",
134
144
  "TOPICFORGE_DDS_BACKEND": "cyclone",
@@ -157,7 +167,7 @@ Servers", and paste the common JSON shape (the file is
157
167
  Project file `.roo/mcp.json` (takes precedence) or the global `mcp_settings.json`
158
168
  opened from the MCP view. Paste the common JSON shape. Roo's docs say Windows
159
169
  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",
170
+ to `"command": "cmd", "args": ["/c", "uvx", "--from", "topicforge==0.6.4",
161
171
  "topicforge"]` if it fails to start.
162
172
 
163
173
  ## Continue
@@ -174,7 +184,7 @@ mcpServers:
174
184
  command: uvx
175
185
  args:
176
186
  - "--from"
177
- - "topicforge[dds]==0.6.2"
187
+ - "topicforge==0.6.4"
178
188
  - "topicforge"
179
189
  env:
180
190
  TOPICFORGE_MODE: auto
@@ -191,7 +201,7 @@ In `settings.json`, under `context_servers`:
191
201
  "context_servers": {
192
202
  "topicforge": {
193
203
  "command": "uvx",
194
- "args": ["--from", "topicforge[dds]==0.6.2", "topicforge"],
204
+ "args": ["--from", "topicforge==0.6.4", "topicforge"],
195
205
  "env": {
196
206
  "TOPICFORGE_MODE": "auto",
197
207
  "TOPICFORGE_DDS_BACKEND": "cyclone",
@@ -217,7 +227,7 @@ run with the dds extra can exceed, so set it to 60 s:
217
227
  ```toml
218
228
  [mcp_servers.topicforge]
219
229
  command = "uvx"
220
- args = ["--from", "topicforge[dds]==0.6.2", "topicforge"]
230
+ args = ["--from", "topicforge==0.6.4", "topicforge"]
221
231
  startup_timeout_sec = 60
222
232
 
223
233
  [mcp_servers.topicforge.env]
@@ -248,7 +258,7 @@ extensions:
248
258
  name: TopicForge (ROS 2 / DDS)
249
259
  type: stdio
250
260
  cmd: uvx
251
- args: [--from, "topicforge[dds]==0.6.2", topicforge]
261
+ args: [--from, "topicforge==0.6.4", topicforge]
252
262
  enabled: true
253
263
  envs: { "TOPICFORGE_MODE": "auto", "TOPICFORGE_DDS_BACKEND": "cyclone", "TOPICFORGE_DDS_DOMAIN_ID": "0" }
254
264
  timeout: 300
@@ -273,7 +283,7 @@ Settings > MCP > "+ Add", then paste the common JSON shape (unverified, last che
273
283
 
274
284
  ## Troubleshooting
275
285
 
276
- - Run the command by hand first: `uvx --from "topicforge[dds]==0.6.2" topicforge --version`.
286
+ - Run the command by hand first: `uvx --from "topicforge==0.6.4" topicforge --version`.
277
287
  - Server starts but sees no DDS participants: check the domain id, and that the
278
288
  client machine is on the same network as the robot (DDS discovery uses
279
289
  multicast UDP). Ask the assistant to call `health_check`.
@@ -30,7 +30,7 @@ pip install topicforge[dds] # same as [dds-cyclone]
30
30
  TOPICFORGE_DDS_BACKEND=cyclone python -m topicforge
31
31
  ```
32
32
 
33
- `cyclonedds` publishes wheels for CPython 3.10 to 3.13 on Windows, Linux and macOS (as of 11.0.1). A background thread reads the builtin DCPS topics through `BuiltinDataReader`. On a real bus `list_participants` reports each participant's `name` (EntityName QoS) and `hostname` (the `__Hostname` discovery property) when the remote participant sets them, and the vendor from the first two bytes of the GUID prefix; implementations that do not follow that convention (Dust DDS, RTI by default) show vendor `unknown`.
33
+ `cyclonedds` publishes wheels for CPython 3.10 to 3.13 on Windows, Linux and macOS (as of 11.0.1), and `topicforge` depends on it only there. Elsewhere (Linux aarch64, Python 3.14, Windows ARM64) `health_check` reports why in `dds_inactive_reason`. A background thread reads the builtin DCPS topics through `BuiltinDataReader`. On a real bus `list_participants` reports each participant's `name` (EntityName QoS) and `hostname` (the `__Hostname` discovery property) when the remote participant sets them, and the vendor from the first two bytes of the GUID prefix; implementations that do not follow that convention (Dust DDS, RTI by default) show vendor `unknown`.
34
34
 
35
35
  Fast DDS has no PyPI install path. The `fastdds` binding is not published, so TopicForge declares no extra for it. Build eProsima's [Fast-DDS-python](https://github.com/eProsima/Fast-DDS-python) from source (it needs the Fast DDS C++ libraries and SWIG), install it into the same environment, then `TOPICFORGE_DDS_BACKEND=fast`. The adapter was written against the 2.6.x API and has never run against a bus.
36
36
 
@@ -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.2
18
+ python -m topicforge --version # -> topicforge 0.6.4
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.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.
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.4 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
 
@@ -77,7 +77,7 @@ TopicForge resolves the `ros2` launcher with `shutil.which` (normally `ros2.exe`
77
77
 
78
78
  Discover the graph. Ask "What topics are currently being published, and what message types do they carry?" TopicForge calls `list_topics`. Live mode shows `/chatter` plus `/rosout` and `/parameter_events`; mock mode shows `/cmd_vel`, `/odom`, `/scan`, `/tf` and `/camera/image_raw`.
79
79
 
80
- Inspect and sample. Ask "Show me the latest message on /chatter." TopicForge calls `get_topic_info` then `sample_messages`. The payload has the message fields as nested named values. `timestamp_ns` is the `header.stamp` and `0` for headerless types such as `std_msgs/String` (`stamp_source` says which).
80
+ Inspect and sample. Ask "Show me the latest message on /chatter." TopicForge calls `get_topic_info` then `sample_messages`. The payload has the message fields as nested named values. `timestamp_ns` is the `header.stamp` and the body time of `/clock`, `/tf` and `/rosout`, and `0` for other headerless types such as `std_msgs/String` (`stamp_source` says which).
81
81
 
82
82
  Record and analyze a bag.
83
83
 
@@ -85,7 +85,7 @@ Record and analyze a bag.
85
85
  ros2 bag record /chatter --output ~/demo_run --max-bag-duration 30
86
86
  ```
87
87
 
88
- Then ask "Analyze the bag at /home/<you>/demo_run." `analyze_bag` returns duration, message count and per-topic stats; anomalies are mock-only. To read actual messages, install `topicforge[bags]` and ask for samples, which calls `peek_bag_samples`. A fourth scenario, DDS discovery and QoS diagnosis against a real bus, is in [`examples/dds/README.md`](../examples/dds/README.md).
88
+ Then ask "Analyze the bag at /home/<you>/demo_run." `analyze_bag` returns duration, message count and per-topic stats; anomalies are mock-only. To read actual messages, ask for samples, which calls `peek_bag_samples`. A fourth scenario, DDS discovery and QoS diagnosis against a real bus, is in [`examples/dds/README.md`](../examples/dds/README.md).
89
89
 
90
90
  ## Connect an MCP client
91
91
 
@@ -35,7 +35,7 @@ If you want a DDS-only deployment, the message is expected: you use the seven DD
35
35
 
36
36
  ## "rosbags"
37
37
 
38
- `peek_bag_samples` needs the optional `rosbags` library: `pip install topicforge[bags]`, then retry, no env change needed. `analyze_bag` does not raise this error, since in live mode it parses `ros2 bag info` and never touches `rosbags`; the error text mentions an `analyze_bag` fallback that does not exist. `peek_bag_samples` is served only by the ROS2 CLI adapter and the mock. Without `ros2` and without a DDS backend you are on the mock and get fixture samples, not the content of your file: check `health_check` first.
38
+ `peek_bag_samples` needs the `rosbags` library, a core dependency since 0.6.4 (older versions: `pip install topicforge[bags]`, which still works as an alias); if the import fails, `pip install --force-reinstall rosbags`, then retry. `analyze_bag` does not raise this error, since in live mode it parses `ros2 bag info` and never touches `rosbags`; the error text mentions an `analyze_bag` fallback that does not exist. `peek_bag_samples` is served only by the ROS2 CLI adapter and the mock. Without `ros2` and without a DDS backend you are on the mock and get fixture samples, not the content of your file: check `health_check` first.
39
39
 
40
40
  ## "CycloneDDS participant discovery failed"
41
41
 
@@ -126,7 +126,7 @@ Regardless of the telemetry setting, TopicForge never transmits topic names, mes
126
126
 
127
127
  ### Bags are read locally
128
128
 
129
- `analyze_bag` and `peek_bag_samples` read bag files on the filesystem where TopicForge runs. `analyze_bag` runs `ros2 bag info` on the path in live mode; `peek_bag_samples` parses the file in-process with the `rosbags` library (`pip install topicforge[bags]`). Nothing about a bag's content or path is uploaded anywhere.
129
+ `analyze_bag` and `peek_bag_samples` read bag files on the filesystem where TopicForge runs. `analyze_bag` runs `ros2 bag info` on the path in live mode; `peek_bag_samples` parses the file in-process with the `rosbags` library (installed with topicforge). Nothing about a bag's content or path is uploaded anywhere.
130
130
 
131
131
  ---
132
132
 
@@ -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.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`.
7
+ - MCP server `topicforge`, started with `uvx` from the `topicforge` package on PyPI (version pinned to 0.6.4). 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
 
@@ -44,6 +44,12 @@ The server runs with `TOPICFORGE_MODE=auto`: live ROS 2 when `ros2` is on PATH,
44
44
 
45
45
  Without DDS, set `dds_backend` to `mock`, or use the standalone install `uvx topicforge`.
46
46
 
47
+ ## Other clients (Agent Plugins)
48
+
49
+ This folder is also an [Agent Plugins](https://agent-plugins.org) 1.0.0 package, so Cursor and other compatible clients can load it: `plugin.json` is the manifest, `mcp.json` declares the server and `skills/` holds the two skills (shared with the Claude format above). The Claude files (`.claude-plugin/plugin.json`, `.mcp.json`) are left alone.
50
+
51
+ The Agent Plugins format has no user-configuration prompt, so `mcp.json` uses fixed defaults: `TOPICFORGE_MODE=auto`, `TOPICFORGE_DDS_BACKEND=cyclone`, `TOPICFORGE_DDS_DOMAIN_ID=0`. To change them, edit the `env` block of `mcp.json` in the installed copy (backend `mock` serves fixtures, `auto` picks the best available; domain 0 to 232, fixed at startup) and restart the server.
52
+
47
53
  ## Privacy
48
54
 
49
55
  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).
@@ -0,0 +1,16 @@
1
+ # Dependency manifest for the Claude plugin directory. It is not a package: it only declares
2
+ # what `plugin/.mcp.json` launches, so that `uv.lock` next to it pins every dependency with
3
+ # hashes. Keep the topicforge pin equal to the version in `.mcp.json`; a test checks it.
4
+ # After a release reaches PyPI, regenerate the lock: `cd plugin && uv lock`.
5
+ [project]
6
+ name = "topicforge-claude-plugin"
7
+ version = "0.6.4"
8
+ description = "Locked launch manifest for the TopicForge Claude plugin."
9
+ # cyclonedds ships wheels for CPython 3.10 to 3.13 only (checked on PyPI 2026-10-07).
10
+ requires-python = ">=3.10,<3.14"
11
+ dependencies = [
12
+ "topicforge==0.6.4",
13
+ ]
14
+
15
+ [tool.uv]
16
+ package = false
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "topicforge"
7
- version = "0.6.2"
7
+ version = "0.6.4"
8
8
  description = "ROS Topic Inspector & Bag Analyzer MCP server for AI agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -26,6 +26,19 @@ dependencies = [
26
26
  "mcp>=1.0.0,<2",
27
27
  "pydantic>=2.6",
28
28
  "pyyaml>=6",
29
+ # Bag reader behind `peek_bag_samples`, core since 0.6.4 (it used to be the `[bags]`
30
+ # extra, which `uvx topicforge` and the plugin never selected). Pure Python
31
+ # (py3-none-any wheel); its dependencies apsw, lz4, numpy, zstandard and ruamel.yaml
32
+ # all ship wheels for linux-aarch64 and CPython 3.14 (checked on PyPI 2026-10-08), so
33
+ # no platform marker is needed.
34
+ "rosbags>=0.11.3",
35
+ # Cyclone DDS binding, core since 0.6.3 so that `uvx topicforge` (no extra) can join a
36
+ # DDS bus. The marker lists exactly the platforms where cyclonedds 11.0.1 ships wheels
37
+ # (checked on PyPI 2026-10-07): CPython 3.10-3.13 on Linux x86_64, Windows AMD64 and
38
+ # macOS (x86_64 and arm64). Anywhere else (Linux aarch64, Python 3.14, Windows ARM64)
39
+ # pip would try to build the sdist, which needs the Cyclone C library and fails, so the
40
+ # dependency is skipped there and the server reports why in `dds_inactive_reason`.
41
+ 'cyclonedds>=0.10; python_version < "3.14" and ((platform_system == "Linux" and platform_machine == "x86_64") or (platform_system == "Windows" and platform_machine == "AMD64") or platform_system == "Darwin")',
29
42
  ]
30
43
 
31
44
  [project.optional-dependencies]
@@ -33,11 +46,6 @@ dev = [
33
46
  "pytest>=8.0",
34
47
  "pytest-cov>=5.0",
35
48
  "ruff>=0.4",
36
- # rosbags (Apache-2.0, pure-Python) is a *dev* dependency so the
37
- # bag-analysis I/O tests actually run in CI instead of self-skipping on
38
- # `requires_rosbags`. It is NOT a runtime dependency: end users opt in
39
- # via `topicforge[bags]`.
40
- "rosbags>=0.11.3",
41
49
  ]
42
50
  # DDS extras. Only bindings that are actually published on PyPI and used by
43
51
  # a working adapter are declared here.
@@ -50,19 +58,21 @@ dev = [
50
58
  # Fast DDS still works if its Python binding is built from eProsima's sources;
51
59
  # see docs/DDS_QUICKSTART.md. Do not add a name here without checking it
52
60
  # resolves on PyPI and belongs to the upstream project.
61
+ #
62
+ # `[dds]` and `[dds-cyclone]` are kept as aliases since 0.6.3 (cyclonedds became a core
63
+ # dependency on supported platforms) so existing `topicforge[dds]` commands keep working.
64
+ # They deliberately list cyclonedds WITHOUT the platform marker: a user on an unsupported
65
+ # platform (Linux aarch64, Python 3.14) who has the Cyclone C library installed can still opt
66
+ # in with `pip install "topicforge[dds]"` and build the binding from source.
53
67
  dds-cyclone = [
54
68
  "cyclonedds>=0.10",
55
69
  ]
56
70
  dds = [
57
71
  "topicforge[dds-cyclone]",
58
72
  ]
59
- # v0.4.0 Phase 3: bag analysis via `rosbags` (Apache 2.0, pure-Python,
60
- # covers MCAP + ROS2 .db3 + ROS1 .bag legacy through a single API).
61
- # Optional because rosbags pulls in SQLite / msgpack / zstd-side deps
62
- # users without bag analysis don't need.
63
- bags = [
64
- "rosbags>=0.11.3",
65
- ]
73
+ # `[bags]` is kept as an alias since 0.6.4 (rosbags became a core dependency, so a plain
74
+ # install can run `peek_bag_samples`) so existing `topicforge[bags]` commands keep working.
75
+ bags = []
66
76
  all = [
67
77
  "topicforge[dds]",
68
78
  ]
@@ -1,5 +1,5 @@
1
1
  """TopicForge: ROS Topic Inspector & Bag Analyzer MCP server."""
2
2
 
3
- __version__ = "0.6.2"
3
+ __version__ = "0.6.4"
4
4
 
5
5
  __all__ = ["__version__"]