topicforge 0.6.2__tar.gz → 0.6.3__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.
- {topicforge-0.6.2 → topicforge-0.6.3}/CHANGELOG.md +40 -1
- {topicforge-0.6.2 → topicforge-0.6.3}/PKG-INFO +8 -4
- {topicforge-0.6.2 → topicforge-0.6.3}/README.md +6 -3
- {topicforge-0.6.2 → topicforge-0.6.3}/docs/CLIENTS.md +27 -21
- {topicforge-0.6.2 → topicforge-0.6.3}/docs/DDS_QUICKSTART.md +1 -1
- {topicforge-0.6.2 → topicforge-0.6.3}/docs/TESTING.md +2 -2
- {topicforge-0.6.2 → topicforge-0.6.3}/plugin/README.md +1 -1
- topicforge-0.6.3/plugin/pyproject.toml +16 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/pyproject.toml +14 -1
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/__init__.py +1 -1
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/ros2_mock/adapter.py +3 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/services/factory.py +51 -8
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/services/health.py +1 -1
- topicforge-0.6.3/src/topicforge/tools/annotations.py +32 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/tools/handlers.py +25 -12
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/conftest.py +29 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_config.py +39 -9
- topicforge-0.6.3/tests/test_dds_inactive_reason.py +188 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_discovery_tracker.py +6 -2
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_factory.py +28 -1
- topicforge-0.6.3/tests/test_tool_annotations.py +44 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_version_consistency.py +27 -2
- topicforge-0.6.2/tests/test_dds_inactive_reason.py +0 -94
- {topicforge-0.6.2 → topicforge-0.6.3}/.gitignore +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/LICENSE +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/docs/TROUBLESHOOTING.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/docs/TUTORIEL.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/docs/VALIDATION.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/docs/dds-interop-matrix.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/00_hello_pub_sub/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/01_who_is_on_the_bus/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/02_why_cant_they_talk/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/03_a_node_crashed/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/04_late_joiner_misses_data/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/05_reliability_in_code/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/06_durability_late_joiner_in_code/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/07_deadline_in_code/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/08_crash_seen_from_inside/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/10_lidar_silent_after_driver_swap/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/11_who_talks_to_whom/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/12_safety_monitor_dropout/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/13_deadline_not_offered/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/14_restart_loop/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/examples/dds/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/plugin/LICENSE +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/scripts/agent_eval/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/scripts/integration/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/scripts/integration/publishers/cyclone_c/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/scripts/integration/publishers/cyclone_cpp/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/scripts/integration/publishers/cyclone_rust/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/scripts/integration/publishers/dust_py/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/scripts/integration/publishers/fast_publisher_cpp/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/scripts/integration/publishers/fast_py/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/scripts/integration/publishers/opensplice_publisher/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/scripts/integration/publishers/rti_c/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/scripts/integration/publishers/rti_cpp/README.md +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/__main__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/base.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/cdr_decoder.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/dds_helpers.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/dds_introspection.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/discovery_tracker.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/endpoints.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/lifecycle.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/metrics_buffer.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/qos_analyzer.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/qos_endpoints.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/qos_normalize.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/qos_scan.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/topic_filter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/common/xtypes.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/composite.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/dds_cyclone/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/dds_cyclone/adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/dds_dust/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/dds_dust/adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/dds_fast/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/dds_fast/adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/dds_opendds/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/dds_opendds/adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/ros2_live/adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/ros2_live/echo_parser.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/ros2_live/echo_stream.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/ros2_live/parsers.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/ros2_live/process_tree.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/adapters/ros2_mock/fixtures.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/config/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/config/settings.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/constants.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/models/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/models/schemas.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/server/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/server/app.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/services/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/services/bag_service.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/services/bag_stats.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/services/inspector.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/services/sample_budget.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/telemetry/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/telemetry/client.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/src/topicforge/tools/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_echo/image_noarr.yaml +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_echo/image_trunc4.yaml +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_echo/scan_edge.yaml +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_echo/scan_noarr.yaml +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_echo/scan_trunc128.yaml +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_echo/scan_trunc3.yaml +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_echo/string_latched.yaml +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_echo/twist.yaml +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_topic_info_verbose_cmd_vel.txt +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_topic_info_verbose_parameter_events.txt +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_topic_info_verbose_scan.txt +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_topic_info_verbose_tf.txt +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/fixtures/ros2_topic_info_verbose_tf_static.txt +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/integration/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/integration/ros2/Dockerfile +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/integration/ros2/__init__.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/integration/ros2/entrypoint.sh +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/integration/ros2/publisher.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/integration/ros2/run_bench.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/integration/ros2/test_live_adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/integration/test_real_bus.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_analyze_bag_multi_format.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_bag_omnisim_humble.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_bag_service.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_bag_stats.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_cdr_decoder.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_composite_adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_cyclone_adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_dds_cross_vendor.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_dds_helpers.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_dds_introspection.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_dds_qos_normalization.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_dds_schemas.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_dust_adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_echo_parser.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_echo_stream.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_endpoints.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_example_node_spec.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_fast_adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_health.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_history_and_hints.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_honest_outputs.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_inspector.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_lifecycle_buffer.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_live_adapter_graph.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_live_adapter_parse.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_live_adapter_subprocess.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_live_sample_notes.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_metrics_buffer.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_mock_adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_opendds_adapter.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_peek_bag_samples.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_process_tree.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_qos_analyzer.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_qos_endpoints.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_qos_scan.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_sample_options.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_telemetry.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_tools_integration.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_topic_metrics.py +0 -0
- {topicforge-0.6.2 → topicforge-0.6.3}/tests/test_xtypes.py +0 -0
|
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning 2.0.0](https://semver.org/spec/
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.3] - 2026-10-07
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Platform-aware DDS message: when the Cyclone backend is requested but the binding is
|
|
15
|
+
missing, `dds_inactive_reason` (and the server log) now says why in plain words, for
|
|
16
|
+
example "`cyclonedds` has no prebuilt wheel for linux-aarch64 / Python 3.14", and what
|
|
17
|
+
to do (build it against the Cyclone C library, or use mock). A server that falls back
|
|
18
|
+
to the mock fixtures because of this reports the reason in `health_check` too. Field
|
|
19
|
+
names are unchanged.
|
|
20
|
+
- CI: a `ubuntu-24.04-arm` job that installs the package without a Cyclone wheel and
|
|
21
|
+
checks that the server starts and `health_check` explains, and Python 3.14 in the test
|
|
22
|
+
matrix (the suite runs there with the binding absent).
|
|
23
|
+
|
|
24
|
+
- MCP `ToolAnnotations` on all twelve tools (`title`, `readOnlyHint=true`,
|
|
25
|
+
`destructiveHint=false`, `idempotentHint=true`, per-tool `openWorldHint`),
|
|
26
|
+
built by one shared helper in `tools/annotations.py`, and
|
|
27
|
+
`tests/test_tool_annotations.py`, which fails if any tool lacks them.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- `cyclonedds` is now a core dependency, restricted by an environment marker to the
|
|
32
|
+
platforms where it ships prebuilt wheels (Python 3.10-3.13 on Linux x86_64, Windows
|
|
33
|
+
AMD64 and macOS), so `uvx topicforge` and `pip install topicforge` bring the DDS
|
|
34
|
+
binding with no extra. Other platforms (Linux aarch64, Python 3.14, Windows ARM64)
|
|
35
|
+
install cleanly without it. `topicforge[dds]` and `topicforge[dds-cyclone]` stay as
|
|
36
|
+
aliases. The default `TOPICFORGE_DDS_BACKEND` is still `mock`.
|
|
37
|
+
- The Claude plugin launches `uvx --python 3.12 --from topicforge==0.6.3 topicforge`
|
|
38
|
+
without an extra; its dependencies are declared in the new `plugin/pyproject.toml`
|
|
39
|
+
(a lock, `plugin/uv.lock`, is generated once the release is on PyPI). The version
|
|
40
|
+
consistency test covers both files.
|
|
41
|
+
- Documented launch commands, the Cursor and VS Code install links and the MCP Bundle
|
|
42
|
+
now use `topicforge==X` instead of `topicforge[dds]==X`.
|
|
43
|
+
- `SECURITY.md`: new section "Read-only guarantee: architecture, declaration,
|
|
44
|
+
proof" (including the limits of passivity on a DDS domain and the ros2 CLI),
|
|
45
|
+
Popen streaming with process-tree kill documented, any MCP client named, and
|
|
46
|
+
a remote-access paragraph.
|
|
47
|
+
|
|
10
48
|
## [0.6.2] - 2026-10-06
|
|
11
49
|
|
|
12
50
|
### Added
|
|
@@ -1316,7 +1354,8 @@ Initial MVP release of TopicForge: ROS Topic Inspector & Bag Analyzer MCP server
|
|
|
1316
1354
|
- The write path (publishing, commanding robots) is intentionally out of scope for the MVP.
|
|
1317
1355
|
- `analyze_bag` in live mode parses `ros2 bag info` text output; deeper anomaly detection remains mock-only for now.
|
|
1318
1356
|
|
|
1319
|
-
[Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.6.
|
|
1357
|
+
[Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.6.3...HEAD
|
|
1358
|
+
[0.6.3]: https://github.com/yaniswav/TopicForge/compare/v0.6.2...v0.6.3
|
|
1320
1359
|
[0.6.2]: https://github.com/yaniswav/TopicForge/compare/v0.6.1...v0.6.2
|
|
1321
1360
|
[0.6.1]: https://github.com/yaniswav/TopicForge/compare/v0.6.0...v0.6.1
|
|
1322
1361
|
[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.
|
|
3
|
+
Version: 0.6.3
|
|
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,6 +40,7 @@ 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
|
|
@@ -70,6 +71,8 @@ Description-Content-Type: text/markdown
|
|
|
70
71
|
|
|
71
72
|
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
73
|
|
|
74
|
+
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.
|
|
75
|
+
|
|
73
76
|
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
77
|
|
|
75
78
|
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 +103,7 @@ The server speaks MCP over stdio and waits for a client, so wire it into one. Fo
|
|
|
100
103
|
|
|
101
104
|
Then ask it to list the topics or to analyze `/tmp/demo.mcap`. For Claude Code: `claude mcp add topicforge -- topicforge`.
|
|
102
105
|
|
|
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=
|
|
106
|
+
**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=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjYuMyIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%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.3%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
107
|
|
|
105
108
|
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
109
|
|
|
@@ -138,7 +141,8 @@ Walkthroughs against the mock, each with the exact tool calls and payloads, are
|
|
|
138
141
|
## DDS backends
|
|
139
142
|
|
|
140
143
|
```bash
|
|
141
|
-
pip install topicforge
|
|
144
|
+
pip install topicforge # includes Eclipse CycloneDDS where wheels exist (Python 3.10-3.13; Linux x86_64, Windows x64, macOS)
|
|
145
|
+
pip install "topicforge[dds]" # alias, kept so older commands work; forces a source build elsewhere (needs the Cyclone C library)
|
|
142
146
|
TOPICFORGE_DDS_BACKEND=cyclone python -m topicforge
|
|
143
147
|
```
|
|
144
148
|
|
|
@@ -195,7 +199,7 @@ When on, each tool call emits one event with exactly six fields:
|
|
|
195
199
|
| `tool_name` | `"list_topics"` | One of the twelve tools, never argument values |
|
|
196
200
|
| `latency_ms` | `12.34` | Handler wall-clock duration, 2 decimals |
|
|
197
201
|
| `mode` | `"mock"` | Mode of the adapter actually serving: `mock` or `live` |
|
|
198
|
-
| `version` | `"0.6.
|
|
202
|
+
| `version` | `"0.6.3"` | TopicForge server version |
|
|
199
203
|
| `session_id` | `"a1b2c3..."` | Random UUID per process, never persisted |
|
|
200
204
|
| `success` | `true` | Whether the handler returned or raised |
|
|
201
205
|
|
|
@@ -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=
|
|
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=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjYuMyIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%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.3%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
|
|
|
@@ -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
|
|
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
|
|
|
@@ -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.
|
|
141
|
+
| `version` | `"0.6.3"` | 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-
|
|
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
|
|
9
|
+
uvx --from "topicforge==0.6.3" 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 `
|
|
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.
|
|
24
|
+
The version pin (`==0.6.3`) 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
|
-
- **
|
|
40
|
-
`
|
|
41
|
-
|
|
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.3"`, 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
|
|
75
|
+
"args": ["--from", "topicforge==0.6.3", "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
|
|
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.3" topicforge
|
|
93
99
|
```
|
|
94
100
|
|
|
95
101
|
## Claude Desktop
|
|
96
102
|
|
|
97
|
-
From 0.6.
|
|
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,18 @@ 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=
|
|
115
|
+
One click: [Add TopicForge to Cursor](https://cursor.com/install-mcp?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjYuMyIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0%3D)
|
|
110
116
|
|
|
111
|
-
If that page does not open Cursor, use the raw deeplink: `cursor://anysphere.cursor-deeplink/mcp/install?name=topicforge&config=
|
|
117
|
+
If that page does not open Cursor, use the raw deeplink: `cursor://anysphere.cursor-deeplink/mcp/install?name=topicforge&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyItLWZyb20iLCJ0b3BpY2ZvcmdlPT0wLjYuMyIsInRvcGljZm9yZ2UiXSwiZW52Ijp7IlRPUElDRk9SR0VfTU9ERSI6ImF1dG8iLCJUT1BJQ0ZPUkdFX0REU19CQUNLRU5EIjoiY3ljbG9uZSIsIlRPUElDRk9SR0VfRERTX0RPTUFJTl9JRCI6IjAifX0=`
|
|
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
|
|
|
116
122
|
## VS Code / GitHub Copilot
|
|
117
123
|
|
|
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%
|
|
124
|
+
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.3%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
125
|
|
|
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%
|
|
126
|
+
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.3%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
127
|
|
|
122
128
|
Or put this in `.vscode/mcp.json` (the key is `servers`, not `mcpServers`), or run
|
|
123
129
|
"MCP: Add Server" from the Command Palette:
|
|
@@ -128,7 +134,7 @@ Or put this in `.vscode/mcp.json` (the key is `servers`, not `mcpServers`), or r
|
|
|
128
134
|
"topicforge": {
|
|
129
135
|
"type": "stdio",
|
|
130
136
|
"command": "uvx",
|
|
131
|
-
"args": ["--from", "topicforge
|
|
137
|
+
"args": ["--from", "topicforge==0.6.3", "topicforge"],
|
|
132
138
|
"env": {
|
|
133
139
|
"TOPICFORGE_MODE": "auto",
|
|
134
140
|
"TOPICFORGE_DDS_BACKEND": "cyclone",
|
|
@@ -157,7 +163,7 @@ Servers", and paste the common JSON shape (the file is
|
|
|
157
163
|
Project file `.roo/mcp.json` (takes precedence) or the global `mcp_settings.json`
|
|
158
164
|
opened from the MCP view. Paste the common JSON shape. Roo's docs say Windows
|
|
159
165
|
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
|
|
166
|
+
to `"command": "cmd", "args": ["/c", "uvx", "--from", "topicforge==0.6.3",
|
|
161
167
|
"topicforge"]` if it fails to start.
|
|
162
168
|
|
|
163
169
|
## Continue
|
|
@@ -174,7 +180,7 @@ mcpServers:
|
|
|
174
180
|
command: uvx
|
|
175
181
|
args:
|
|
176
182
|
- "--from"
|
|
177
|
-
- "topicforge
|
|
183
|
+
- "topicforge==0.6.3"
|
|
178
184
|
- "topicforge"
|
|
179
185
|
env:
|
|
180
186
|
TOPICFORGE_MODE: auto
|
|
@@ -191,7 +197,7 @@ In `settings.json`, under `context_servers`:
|
|
|
191
197
|
"context_servers": {
|
|
192
198
|
"topicforge": {
|
|
193
199
|
"command": "uvx",
|
|
194
|
-
"args": ["--from", "topicforge
|
|
200
|
+
"args": ["--from", "topicforge==0.6.3", "topicforge"],
|
|
195
201
|
"env": {
|
|
196
202
|
"TOPICFORGE_MODE": "auto",
|
|
197
203
|
"TOPICFORGE_DDS_BACKEND": "cyclone",
|
|
@@ -217,7 +223,7 @@ run with the dds extra can exceed, so set it to 60 s:
|
|
|
217
223
|
```toml
|
|
218
224
|
[mcp_servers.topicforge]
|
|
219
225
|
command = "uvx"
|
|
220
|
-
args = ["--from", "topicforge
|
|
226
|
+
args = ["--from", "topicforge==0.6.3", "topicforge"]
|
|
221
227
|
startup_timeout_sec = 60
|
|
222
228
|
|
|
223
229
|
[mcp_servers.topicforge.env]
|
|
@@ -248,7 +254,7 @@ extensions:
|
|
|
248
254
|
name: TopicForge (ROS 2 / DDS)
|
|
249
255
|
type: stdio
|
|
250
256
|
cmd: uvx
|
|
251
|
-
args: [--from, "topicforge
|
|
257
|
+
args: [--from, "topicforge==0.6.3", topicforge]
|
|
252
258
|
enabled: true
|
|
253
259
|
envs: { "TOPICFORGE_MODE": "auto", "TOPICFORGE_DDS_BACKEND": "cyclone", "TOPICFORGE_DDS_DOMAIN_ID": "0" }
|
|
254
260
|
timeout: 300
|
|
@@ -273,7 +279,7 @@ Settings > MCP > "+ Add", then paste the common JSON shape (unverified, last che
|
|
|
273
279
|
|
|
274
280
|
## Troubleshooting
|
|
275
281
|
|
|
276
|
-
- Run the command by hand first: `uvx --from "topicforge
|
|
282
|
+
- Run the command by hand first: `uvx --from "topicforge==0.6.3" topicforge --version`.
|
|
277
283
|
- Server starts but sees no DDS participants: check the domain id, and that the
|
|
278
284
|
client machine is on the same network as the robot (DDS discovery uses
|
|
279
285
|
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.
|
|
18
|
+
python -m topicforge --version # -> topicforge 0.6.3
|
|
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.
|
|
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.3 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
|
|
|
@@ -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.
|
|
7
|
+
- MCP server `topicforge`, started with `uvx` from the `topicforge` package on PyPI (version pinned to 0.6.3). 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
|
|
|
@@ -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.3"
|
|
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.3",
|
|
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.
|
|
7
|
+
version = "0.6.3"
|
|
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,13 @@ dependencies = [
|
|
|
26
26
|
"mcp>=1.0.0,<2",
|
|
27
27
|
"pydantic>=2.6",
|
|
28
28
|
"pyyaml>=6",
|
|
29
|
+
# Cyclone DDS binding, core since 0.6.3 so that `uvx topicforge` (no extra) can join a
|
|
30
|
+
# DDS bus. The marker lists exactly the platforms where cyclonedds 11.0.1 ships wheels
|
|
31
|
+
# (checked on PyPI 2026-10-07): CPython 3.10-3.13 on Linux x86_64, Windows AMD64 and
|
|
32
|
+
# macOS (x86_64 and arm64). Anywhere else (Linux aarch64, Python 3.14, Windows ARM64)
|
|
33
|
+
# pip would try to build the sdist, which needs the Cyclone C library and fails, so the
|
|
34
|
+
# dependency is skipped there and the server reports why in `dds_inactive_reason`.
|
|
35
|
+
'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
36
|
]
|
|
30
37
|
|
|
31
38
|
[project.optional-dependencies]
|
|
@@ -50,6 +57,12 @@ dev = [
|
|
|
50
57
|
# Fast DDS still works if its Python binding is built from eProsima's sources;
|
|
51
58
|
# see docs/DDS_QUICKSTART.md. Do not add a name here without checking it
|
|
52
59
|
# resolves on PyPI and belongs to the upstream project.
|
|
60
|
+
#
|
|
61
|
+
# `[dds]` and `[dds-cyclone]` are kept as aliases since 0.6.3 (cyclonedds became a core
|
|
62
|
+
# dependency on supported platforms) so existing `topicforge[dds]` commands keep working.
|
|
63
|
+
# They deliberately list cyclonedds WITHOUT the platform marker: a user on an unsupported
|
|
64
|
+
# platform (Linux aarch64, Python 3.14) who has the Cyclone C library installed can still opt
|
|
65
|
+
# in with `pip install "topicforge[dds]"` and build the binding from source.
|
|
53
66
|
dds-cyclone = [
|
|
54
67
|
"cyclonedds>=0.10",
|
|
55
68
|
]
|
|
@@ -29,6 +29,9 @@ _BAG_EXTENSIONS: frozenset[str] = frozenset({".mcap", ".db3", ".bag"})
|
|
|
29
29
|
|
|
30
30
|
class MockAdapter:
|
|
31
31
|
name: AdapterName = "mock"
|
|
32
|
+
# Set by the factory when a DDS backend was requested but the server fell back to
|
|
33
|
+
# these fixtures, so `health_check` can say why. `None` for a deliberate mock.
|
|
34
|
+
dds_inactive_reason: str | None = None
|
|
32
35
|
|
|
33
36
|
@property
|
|
34
37
|
def effective_mode(self) -> EffectiveMode:
|
|
@@ -10,6 +10,8 @@ and the DDS adapter are each built best-effort. Both up gives a
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import logging
|
|
13
|
+
import platform
|
|
14
|
+
import sys
|
|
13
15
|
from collections.abc import Callable
|
|
14
16
|
|
|
15
17
|
from topicforge.adapters.base import AdapterError, MiddlewareAdapter
|
|
@@ -59,7 +61,10 @@ def build_adapter(settings: Settings) -> MiddlewareAdapter:
|
|
|
59
61
|
settings.ros2_executable,
|
|
60
62
|
settings.effective_dds_backend,
|
|
61
63
|
)
|
|
62
|
-
|
|
64
|
+
fallback = MockAdapter()
|
|
65
|
+
if settings.dds_backend != "mock":
|
|
66
|
+
fallback.dds_inactive_reason = _dds_inactive_reason(settings)
|
|
67
|
+
return fallback
|
|
63
68
|
|
|
64
69
|
|
|
65
70
|
def _try_build_ros2_cli(
|
|
@@ -80,11 +85,50 @@ def _try_build_ros2_cli(
|
|
|
80
85
|
return cli_adapter
|
|
81
86
|
|
|
82
87
|
|
|
83
|
-
|
|
88
|
+
_CYCLONE_INSTALL_HINT = (
|
|
84
89
|
'Install it with `pip install "topicforge[dds-cyclone]"` and set '
|
|
85
90
|
"`TOPICFORGE_DDS_BACKEND=cyclone`."
|
|
86
91
|
)
|
|
87
92
|
|
|
93
|
+
# Platforms where the `cyclonedds` project publishes prebuilt wheels (CPython 3.10-3.13).
|
|
94
|
+
# Keep in sync with the environment marker on the `cyclonedds` dependency in pyproject.toml.
|
|
95
|
+
_CYCLONE_WHEEL_SYSTEMS: dict[str, frozenset[str] | None] = {
|
|
96
|
+
"linux": frozenset({"x86_64"}),
|
|
97
|
+
"windows": frozenset({"amd64"}),
|
|
98
|
+
"darwin": None, # any machine: x86_64 and arm64 both ship wheels
|
|
99
|
+
}
|
|
100
|
+
_CYCLONE_MAX_PYTHON = (3, 13)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def cyclone_wheel_gap() -> str | None:
|
|
104
|
+
"""Describe why `cyclonedds` has no prebuilt wheel here, or `None` when it has one.
|
|
105
|
+
|
|
106
|
+
The answer is a short platform label such as `linux-aarch64` or
|
|
107
|
+
`linux-aarch64 / Python 3.14`, naming only the parts that are unsupported.
|
|
108
|
+
"""
|
|
109
|
+
system = platform.system().lower()
|
|
110
|
+
machine = platform.machine().lower()
|
|
111
|
+
machines = _CYCLONE_WHEEL_SYSTEMS.get(system, frozenset())
|
|
112
|
+
parts = []
|
|
113
|
+
if machines is not None and machine not in machines:
|
|
114
|
+
parts.append(f"{system or 'unknown'}-{machine or 'unknown'}")
|
|
115
|
+
if sys.version_info[:2] > _CYCLONE_MAX_PYTHON:
|
|
116
|
+
parts.append(f"Python {sys.version_info[0]}.{sys.version_info[1]}")
|
|
117
|
+
return " / ".join(parts) or None
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _cyclone_hint() -> str:
|
|
121
|
+
"""What to do to get the Cyclone binding, adapted to the platform."""
|
|
122
|
+
gap = cyclone_wheel_gap()
|
|
123
|
+
if gap is None:
|
|
124
|
+
return _CYCLONE_INSTALL_HINT
|
|
125
|
+
return (
|
|
126
|
+
f"`cyclonedds` has no prebuilt wheel for {gap}, so it was not installed with "
|
|
127
|
+
"TopicForge. Install the Cyclone C library, then build the binding with "
|
|
128
|
+
'`pip install "topicforge[dds-cyclone]"` and set `TOPICFORGE_DDS_BACKEND=cyclone`; '
|
|
129
|
+
"or use `TOPICFORGE_DDS_BACKEND=mock`."
|
|
130
|
+
)
|
|
131
|
+
|
|
88
132
|
|
|
89
133
|
def _dds_inactive_reason(settings: Settings) -> str:
|
|
90
134
|
"""Why no DDS backend is serving, for the three distinct causes.
|
|
@@ -98,7 +142,7 @@ def _dds_inactive_reason(settings: Settings) -> str:
|
|
|
98
142
|
if settings.dds_backend == "auto":
|
|
99
143
|
return (
|
|
100
144
|
"`TOPICFORGE_DDS_BACKEND=auto` found no DDS binding (`fastdds` or "
|
|
101
|
-
f"`cyclonedds`) installed. {
|
|
145
|
+
f"`cyclonedds`) installed. {_cyclone_hint()}"
|
|
102
146
|
)
|
|
103
147
|
if module_is_importable(DDS_BACKEND_MODULES["cyclone"]):
|
|
104
148
|
return (
|
|
@@ -106,14 +150,14 @@ def _dds_inactive_reason(settings: Settings) -> str:
|
|
|
106
150
|
"The `cyclonedds` binding is installed; set `TOPICFORGE_DDS_BACKEND=cyclone` "
|
|
107
151
|
"and restart to enable the DDS tools."
|
|
108
152
|
)
|
|
109
|
-
return f"no DDS backend is selected and no DDS binding is installed. {
|
|
153
|
+
return f"no DDS backend is selected and no DDS binding is installed. {_cyclone_hint()}"
|
|
110
154
|
module = DDS_BACKEND_MODULES.get(backend)
|
|
111
155
|
if backend in ("opendds", "dust"):
|
|
112
156
|
return f"`TOPICFORGE_DDS_BACKEND={backend}` is a stub that never serves; use `cyclone`."
|
|
113
157
|
if module is not None and not module_is_importable(module):
|
|
114
158
|
return (
|
|
115
159
|
f"`TOPICFORGE_DDS_BACKEND={backend}` is set but its `{module}` Python binding is "
|
|
116
|
-
f"not installed. {
|
|
160
|
+
f"not installed. {_cyclone_hint() if backend == 'cyclone' else ''}".rstrip()
|
|
117
161
|
)
|
|
118
162
|
return (
|
|
119
163
|
f"`TOPICFORGE_DDS_BACKEND={backend}` is set and its binding is installed, but the "
|
|
@@ -150,9 +194,8 @@ def _try_build_cyclone(settings: Settings) -> MiddlewareAdapter | None:
|
|
|
150
194
|
except ImportError:
|
|
151
195
|
log.warning(
|
|
152
196
|
"TOPICFORGE_DDS_BACKEND=cyclone but the `cyclonedds` Python "
|
|
153
|
-
"bindings are not installed.
|
|
154
|
-
|
|
155
|
-
"unavailable."
|
|
197
|
+
"bindings are not installed. %s The DDS module is unavailable.",
|
|
198
|
+
_cyclone_hint(),
|
|
156
199
|
)
|
|
157
200
|
return None
|
|
158
201
|
except Exception as exc: # a native library can fail to load (OSError)
|
|
@@ -53,7 +53,7 @@ class HealthService:
|
|
|
53
53
|
dds_backend=dds_backend,
|
|
54
54
|
dds_inactive_reason=(
|
|
55
55
|
getattr(self._adapter, "dds_inactive_reason", None)
|
|
56
|
-
if dds_backend
|
|
56
|
+
if dds_backend in ("none", "mock")
|
|
57
57
|
else None
|
|
58
58
|
),
|
|
59
59
|
dds_domain_id=self._settings.dds_domain_id,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""MCP `ToolAnnotations` shared by every TopicForge tool.
|
|
2
|
+
|
|
3
|
+
TopicForge has no write path. The protocol-level declaration of that fact is
|
|
4
|
+
built here, in one place, so a tool cannot be registered with a weaker
|
|
5
|
+
read-only base by accident. `tests/test_tool_annotations.py` fails closed when
|
|
6
|
+
a tool lacks these annotations.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from mcp.types import ToolAnnotations
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def read_only_annotations(title: str, *, open_world: bool) -> ToolAnnotations:
|
|
15
|
+
"""Build the annotations for one read-only tool.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
title: Short human-readable tool name.
|
|
19
|
+
open_world: True when the tool observes a live ROS 2 graph or DDS bus
|
|
20
|
+
(state outside this process); False when it only reads the local
|
|
21
|
+
environment or local files.
|
|
22
|
+
"""
|
|
23
|
+
return ToolAnnotations(
|
|
24
|
+
title=title,
|
|
25
|
+
readOnlyHint=True,
|
|
26
|
+
destructiveHint=False,
|
|
27
|
+
idempotentHint=True,
|
|
28
|
+
openWorldHint=open_world,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
__all__ = ["read_only_annotations"]
|