topicforge 0.1.2__tar.gz → 0.3.0__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.1.2 → topicforge-0.3.0}/.gitignore +20 -0
- topicforge-0.3.0/CHANGELOG.md +154 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/PKG-INFO +76 -11
- {topicforge-0.1.2 → topicforge-0.3.0}/README.md +65 -10
- topicforge-0.3.0/docs/DDS_QUICKSTART.md +156 -0
- topicforge-0.3.0/docs/MIGRATION_v0.1_to_v0.2.md +140 -0
- topicforge-0.3.0/docs/MIGRATION_v0.2_to_v0.3.md +153 -0
- topicforge-0.3.0/docs/dds-interop-matrix.md +50 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/docs/product-plan.md +33 -17
- {topicforge-0.1.2 → topicforge-0.3.0}/pyproject.toml +26 -1
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/__init__.py +1 -1
- topicforge-0.3.0/src/topicforge/adapters/__init__.py +17 -0
- topicforge-0.3.0/src/topicforge/adapters/base.py +107 -0
- topicforge-0.3.0/src/topicforge/adapters/common/__init__.py +17 -0
- topicforge-0.3.0/src/topicforge/adapters/common/dds_helpers.py +111 -0
- topicforge-0.3.0/src/topicforge/adapters/common/qos_analyzer.py +86 -0
- topicforge-0.3.0/src/topicforge/adapters/dds_cyclone/__init__.py +11 -0
- topicforge-0.3.0/src/topicforge/adapters/dds_cyclone/adapter.py +384 -0
- topicforge-0.3.0/src/topicforge/adapters/dds_fast/__init__.py +11 -0
- topicforge-0.3.0/src/topicforge/adapters/dds_fast/adapter.py +493 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/adapters/ros2_live/adapter.py +52 -8
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/adapters/ros2_mock/adapter.py +38 -3
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/adapters/ros2_mock/fixtures.py +121 -1
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/config/settings.py +69 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/models/__init__.py +6 -0
- topicforge-0.3.0/src/topicforge/models/schemas.py +369 -0
- topicforge-0.3.0/src/topicforge/services/constants.py +21 -0
- topicforge-0.3.0/src/topicforge/services/factory.py +129 -0
- topicforge-0.3.0/src/topicforge/services/health.py +65 -0
- topicforge-0.3.0/src/topicforge/services/inspector.py +174 -0
- topicforge-0.3.0/src/topicforge/tools/handlers.py +291 -0
- topicforge-0.3.0/tests/test_config.py +170 -0
- topicforge-0.3.0/tests/test_cyclone_adapter.py +117 -0
- topicforge-0.3.0/tests/test_dds_cross_vendor.py +136 -0
- topicforge-0.3.0/tests/test_dds_helpers.py +117 -0
- topicforge-0.3.0/tests/test_dds_schemas.py +168 -0
- topicforge-0.3.0/tests/test_fast_adapter.py +146 -0
- topicforge-0.3.0/tests/test_health.py +126 -0
- topicforge-0.3.0/tests/test_qos_analyzer.py +262 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/tests/test_tools_integration.py +7 -0
- topicforge-0.1.2/CHANGELOG.md +0 -73
- topicforge-0.1.2/docs/v0.1.2-action-plan.md +0 -356
- topicforge-0.1.2/src/topicforge/adapters/__init__.py +0 -5
- topicforge-0.1.2/src/topicforge/adapters/base.py +0 -56
- topicforge-0.1.2/src/topicforge/models/schemas.py +0 -178
- topicforge-0.1.2/src/topicforge/services/factory.py +0 -39
- topicforge-0.1.2/src/topicforge/services/health.py +0 -32
- topicforge-0.1.2/src/topicforge/services/inspector.py +0 -98
- topicforge-0.1.2/src/topicforge/tools/handlers.py +0 -167
- topicforge-0.1.2/tests/test_config.py +0 -58
- topicforge-0.1.2/tests/test_health.py +0 -56
- {topicforge-0.1.2 → topicforge-0.3.0}/LICENSE +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/docs/TESTING.md +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/docs/pro.md +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/__main__.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/config/__init__.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/server/__init__.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/server/app.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/services/__init__.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/telemetry/__init__.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/telemetry/client.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/src/topicforge/tools/__init__.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/tests/__init__.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/tests/conftest.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/tests/fixtures/csv_echo_imu.txt +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/tests/fixtures/csv_echo_pose_multi.txt +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/tests/test_inspector.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/tests/test_live_adapter_parse.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/tests/test_live_adapter_subprocess.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/tests/test_mock_adapter.py +0 -0
- {topicforge-0.1.2 → topicforge-0.3.0}/tests/test_telemetry.py +0 -0
|
@@ -197,6 +197,26 @@ CLAUDE*.md
|
|
|
197
197
|
/docs/projet-file/**
|
|
198
198
|
!/docs/projet-file/README.md
|
|
199
199
|
!/docs/projet-file/*-spec.md
|
|
200
|
+
# Traction snapshots are versioned: weekly JSON + summary + folder README.
|
|
201
|
+
# This is the historical curve that informs decision gates G1/G2/G3
|
|
202
|
+
# (product-plan §12). Numeric history must survive across machines.
|
|
203
|
+
!/docs/projet-file/traction/
|
|
204
|
+
!/docs/projet-file/traction/**
|
|
205
|
+
# Launch-post drafts (Reddit, LinkedIn, X). Versioned so the maintainer
|
|
206
|
+
# can diff drafts across releases and keep marketing history auditable.
|
|
207
|
+
# Drafts, not production copy — never the public README.
|
|
208
|
+
!/docs/projet-file/launch-posts/
|
|
209
|
+
!/docs/projet-file/launch-posts/**
|
|
210
|
+
# Audit reports (security, architecture) + audit-followup triage docs.
|
|
211
|
+
# Versioned so audit trails survive across machines and the triage
|
|
212
|
+
# decisions are bisectable per release.
|
|
213
|
+
!/docs/projet-file/*-audit-*.md
|
|
214
|
+
!/docs/projet-file/audit-*.md
|
|
215
|
+
# External reference material (OMG interop reports, third-party specs
|
|
216
|
+
# snapshots) the maintainer wants pinned in git so strategy decisions
|
|
217
|
+
# remain reproducible. Tracked, not part of sdist.
|
|
218
|
+
!/docs/projet-file/references/
|
|
219
|
+
!/docs/projet-file/references/**
|
|
200
220
|
/docs/assets/screencast-raw/
|
|
201
221
|
|
|
202
222
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to TopicForge are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.3.0] - 2026-05-14
|
|
11
|
+
|
|
12
|
+
### Strategic
|
|
13
|
+
|
|
14
|
+
- **OMG DDS-RTPS multi-vendor positioning.** TopicForge is now framed as a read-only DDS-RTPS observer that joins the bus via one of two OSS Python participants — Eclipse CycloneDDS or eProsima Fast DDS — and observes every conformant vendor on the domain (RTI Connext, OpenDDS, CoreDX, Dust DDS in Rust, etc.) regardless of host language. See `docs/dds-interop-matrix.md` for the canonical statement and `docs/projet-file/references/omg-dds-interop-2025-05-08.xlsx` for the OMG May 2025 interop reference. The earlier v0.2.0/v0.3.0 phasing (Cyclone-only at v0.3.0, RTI at v0.3.0+) is collapsed: multi-vendor OSS lands together at v0.3.0 ; RTI Pro defers to v0.4.0+.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Real `CycloneDdsAdapter`** — replaces the v0.2.0 stub with actual CycloneDDS discovery via `cyclonedds.builtin.BuiltinDataReader` on the DCPS participant/subscription/publication builtin topics. QoS extracted via `Policy.*` class-name introspection. `take_iter(timeout=...)` for bounded discovery. Lazy-imported via `services.factory`.
|
|
19
|
+
- **`FastDdsAdapter`** (`adapters/dds_fast/`) — new parallel OSS adapter built on the `fastdds` Python bindings (BSD-licensed, eProsima). Duck-typed listener subclass aggregates discovery callbacks under an RLock. Bounded `discovery_wait_ms=1500` warm-up after participant creation. `close()` releases the participant via `factory.delete_participant`.
|
|
20
|
+
- **`adapters/common/dds_helpers.py`** — vendor-neutral helpers: `canonicalize_vendor_id` (OMG vendor_id → canonical tag), `format_guid` (16-byte GUID → `xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx` canonical text), `DDS_ONLY_ERROR_MSG` (shared remediation message). Pure functions, no DDS dependency.
|
|
21
|
+
- **Pyproject extras refactor**: `[dds-cyclone]` (Cyclone only), `[dds-fast]` (Fast only), `[dds]` (both — union of v0.2.0 `[dds]` behavior plus fastdds).
|
|
22
|
+
- **`TOPICFORGE_DDS_BACKEND=fast`** — new accepted value alongside `mock | cyclone | rti | auto`.
|
|
23
|
+
- **3rd mock participant** with `vendor="fast"` exercises the multi-vendor positioning in mock mode.
|
|
24
|
+
- **New pytest marker** `requires_fastdds` — auto-skips without the binding.
|
|
25
|
+
- **35+ new tests**: `tests/test_dds_helpers.py`, `tests/test_fast_adapter.py`, `tests/test_dds_cross_vendor.py` (parametrized on both adapters), 6 analyzer edge cases in `tests/test_qos_analyzer.py`, 4 new health tests for DDS field population.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- **`ParticipantInfo.vendor` Literal widened** to include `"fast"`. **Strict JSON-schema clients pinned to v0.2.0 will reject `vendor:fast` unless their schema is regenerated.** Standard MCP clients reading tool descriptions dynamically are unaffected.
|
|
30
|
+
- **`HealthReport.dds_backend` Literal widened** to include `"fast"`. Same soft-breaking caveat.
|
|
31
|
+
- **`AdapterName` Literal widened** to include `"fast"` (internal type — no wire impact).
|
|
32
|
+
- **`DdsBackend` / `ResolvedDdsBackend`** widened to include `"fast"`.
|
|
33
|
+
- **`Settings.effective_dds_backend` auto resolution** now prefers Fast DDS > Cyclone DDS > Mock (was Cyclone > Mock in v0.2.0). v0.2.0 users with only `cyclonedds` installed see no change — Fast is unimportable on their host. Users with both SDKs installed will see Fast selected. Reflects the OMG May 2025 interop matrix.
|
|
34
|
+
- **`HealthService.report()`** now populates `dds_backend`, `dds_domain_id`, `middleware_available` — previously returned schema defaults regardless of configuration (v0.2.0 latent bug). `middleware_available` is checked via `importlib.util.find_spec` on the active backend's Python module.
|
|
35
|
+
- **`Ros2CliAdapter._DDS_MODULE_INACTIVE_MSG`** updated to mention both `pip install topicforge[dds-cyclone]` and `pip install topicforge[dds-fast]` remediation paths.
|
|
36
|
+
- **`Inspector` DDS topic validator relaxed** — `detect_qos_mismatches` and `peek_dds_samples` now accept DDS-native topic names (no leading `/` required, `::` separators allowed) via a new `_validate_topic_name_dds`. The strict ROS2 validator stays in place for the 5 ROS2 graph methods. Resolves audit-2026-05-14 "Refactor opportunities" #5.
|
|
37
|
+
|
|
38
|
+
### Removed
|
|
39
|
+
|
|
40
|
+
- **`CycloneDdsAdapter` v0.2.0 stub** — `_NOT_IMPLEMENTED_MSG` and the corresponding `test_dds_surface_raises_stub_error_in_v020` test removed. The 3 DDS methods now serve real results when cyclonedds is installed.
|
|
41
|
+
|
|
42
|
+
### Notes
|
|
43
|
+
|
|
44
|
+
- **OMG-DDS-RTPS interoperability** is the protocol guarantee that makes multi-vendor observation work — see `docs/dds-interop-matrix.md` and `docs/projet-file/references/omg-dds-interop-2025-05-08.xlsx`.
|
|
45
|
+
- **v0.3.0 `peek_dds_samples` limitation** — full-fidelity on the 4 builtin DCPS topics (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`) ; arbitrary user topics raise an `AdapterError` with a v0.3.x roadmap pointer (XTypes/IDL discovery is the missing piece, both for Cyclone via `cyclonedds.dynamic.get_types_for_typeid` and for Fast DDS via XTypes remote type lookup).
|
|
46
|
+
- **`pip install topicforge[dds]` in v0.3.0** now pulls BOTH `cyclonedds` and `fastdds` (was Cyclone only in v0.2.0). Use `[dds-cyclone]` or `[dds-fast]` for single-vendor installs. See `docs/MIGRATION_v0.2_to_v0.3.md`.
|
|
47
|
+
- **Fast DDS pin**: `fastdds>=2.6.1,<3` — Fast DDS 3.x binding wheels for Python 3.11+ on Windows / Linux are not yet stable. Bump when upstream cuts stable 3.x wheels.
|
|
48
|
+
- **No code change to the 5 ROS2 tools** — `health_check`, `list_topics`, `get_topic_info`, `sample_messages`, `analyze_bag` behave identically to v0.2.0. The `mode_effective` wire contract is unchanged ; `health_check` now populates DDS fields correctly.
|
|
49
|
+
- **Full migration guide**: `docs/MIGRATION_v0.2_to_v0.3.md`.
|
|
50
|
+
|
|
51
|
+
## [0.2.0] - 2026-05-14
|
|
52
|
+
|
|
53
|
+
### Strategic
|
|
54
|
+
|
|
55
|
+
- **Mono-MCP pivot (2026-05-14).** The 3-to-5-MCP pack draft is collapsed into a 2-product strategy: TopicForge umbrella (this product — covers ROS2 today and grows a DDS observability module starting with v0.2.0), and **DatasetForge** (Vision Dataset Inspector, the standalone second product). The previously-planned standalone DDS-MCP product is cancelled — its spec is reframed as the TopicForge DDS module spec at `docs/projet-file/mcp-02-spec.md`. Motif: solo-maintenance cost of two parallel repos was the binding constraint, and ROS2 / DDS are the same problem shape (typed pub/sub graph introspection) under the same `MiddlewareAdapter` superset.
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- **DDS module — 3 new MCP tools.** `list_participants(domain_id)`, `detect_qos_mismatches(topic)`, `peek_dds_samples(topic, count)`. All read-only ; surface DDS-layer introspection distinct from the ROS2 graph tools. `peek_dds_samples` is deliberately separate from `sample_messages` — different layer, different semantics, distinct tool description so an LLM picks the right one in a mixed setup.
|
|
60
|
+
- **`MiddlewareAdapter` protocol** in `adapters/base.py` — superset of the historical `RosAdapter`. Covers both ROS2 graph methods and the new DDS methods under one contract. `RosAdapter` retained as a backward-compat alias (`RosAdapter = MiddlewareAdapter`).
|
|
61
|
+
- **`CycloneDdsAdapter`** (`adapters/dds_cyclone/`) — lazy-imported only when `TOPICFORGE_DDS_BACKEND=cyclone` and the optional `cyclonedds` extras are installed (`pip install topicforge[dds]`). **v0.2.0 ships a protocol-compliant stub**: the lazy import, `is_available()`, and routing all work ; the 3 DDS methods raise `AdapterError` with a v0.2.x roadmap pointer. The real CycloneDDS discovery (builtin topics, QoS pair extraction, typed reader for samples) lands in a v0.2.x patch. The mock backend (`TOPICFORGE_DDS_BACKEND=mock`, the default) exposes a working DDS surface against deterministic fixtures in the meantime.
|
|
62
|
+
- **3 new Pydantic schemas**: `QosProfile` (Reliability / Durability / History / Deadline at MVP), `ParticipantInfo` (GUID, vendor, hostname, domain_id), `MismatchReport` (incompatible_policies + severity). All frozen, `extra="forbid"`.
|
|
63
|
+
- **Pure analyzer** `adapters/common/qos_analyzer.detect_mismatches` — module-level pure function, testable against synthesized QoS pairs without any DDS middleware installed.
|
|
64
|
+
- **Environment variables**:
|
|
65
|
+
- `TOPICFORGE_DDS_BACKEND` — `mock | cyclone | rti | auto`, default `mock`. The DDS module is opt-in ; existing ROS2-only setups behave unchanged.
|
|
66
|
+
- `TOPICFORGE_DDS_DOMAIN_ID` — DDS domain id observed (0..232), default `0`.
|
|
67
|
+
- **Mock fixtures enriched**: 2 deterministic DDS participants, two-topic scenario (`/dds/well_matched` and `/dds/qos_mismatch`) exercising `detect_qos_mismatches` end-to-end.
|
|
68
|
+
- **`pyproject.toml` extras**: `[dds]` pulls `cyclonedds>=0.10` ; `[all]` aliases `[dds]`. `pip install topicforge` keeps the core + mock only (zero install impact on ROS2-only users).
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- **`TopicInfo` schema soft-breaking.** Three additive optional fields (`reader_count: int | None`, `writer_count: int | None`, `qos_profile: QosProfile | None`) — all default `None`. Producer side: code constructing `TopicInfo` directly is unaffected (defaults compile). **Strict MCP clients that validated v0.1.x responses against the `TopicInfo` schema with `additionalProperties: false` will reject v0.2.0 responses unless their schema is regenerated. Standard MCP clients that read tool descriptions dynamically are unaffected.**
|
|
73
|
+
- **`HealthReport` schema soft-breaking**, same shape. Three additive optional fields (`dds_backend`, `dds_domain_id`, `middleware_available`) with safe defaults (`"none"`, `None`, `False`).
|
|
74
|
+
- **`RosAdapter` renamed to `MiddlewareAdapter`** in `adapters/base.py`. The old name remains as an alias (`RosAdapter = MiddlewareAdapter`) ; existing imports `from topicforge.adapters import RosAdapter` still type-check. The `Ros2CliAdapter.name` value moves from `"live"` to `"ros2_cli"` — internal tag, separate from the MCP-wire `mode_effective` field which keeps its `Literal["mock", "live"]` contract.
|
|
75
|
+
- **`Settings`** gains `dds_backend` and `dds_domain_id` fields with safe defaults (`"mock"`, `0`). Existing `Settings(...)` constructors are unaffected.
|
|
76
|
+
- **`Ros2CliAdapter` DDS methods raise `AdapterError`** with a clear remediation path (`pip install topicforge[dds]` + `TOPICFORGE_DDS_BACKEND=cyclone`). This is the v0.2.0 MVP limitation D6 (single-adapter-at-a-time) ; a composite adapter that delegates per-tool is a v0.2.x roadmap item.
|
|
77
|
+
|
|
78
|
+
### Internal
|
|
79
|
+
|
|
80
|
+
- `parse_topic_info` and `parse_bag_info` parsers : `mode_effective` kwarg typed as `EffectiveMode` (`Literal["mock", "live"]`) rather than the broader `AdapterName`, cleanly separating the wire-facing mode from the implementation tag.
|
|
81
|
+
- New pytest marker `requires_cyclonedds` for tests that need the SDK. Auto-skips otherwise via `pytest.importorskip`.
|
|
82
|
+
|
|
83
|
+
### Notes
|
|
84
|
+
|
|
85
|
+
- **`cyclonedds` is optional.** Default installs (`pip install topicforge`) are unchanged from v0.1.2 in dependency footprint. Only `pip install topicforge[dds]` pulls the bindings (`cyclonedds>=0.10`).
|
|
86
|
+
- **No code change to the 5 ROS2 tools** — `health_check`, `list_topics`, `get_topic_info`, `sample_messages`, `analyze_bag` behave identically to v0.1.2. The wire contract (`mode_effective: Literal["mock", "live"]`) is unchanged.
|
|
87
|
+
- **v0.2.0 MVP limitation**: single adapter at a time. Users select ROS2 introspection (default) or DDS observability via `TOPICFORGE_DDS_BACKEND=cyclone`, not both simultaneously. The unselected half raises `AdapterError` with a remediation pointer. A composite adapter delegating per-tool category is a v0.2.x roadmap item.
|
|
88
|
+
|
|
89
|
+
## [0.1.2] - 2026-05-13
|
|
90
|
+
|
|
91
|
+
### Fixed
|
|
92
|
+
|
|
93
|
+
- `sample_messages` now returns real publish-time timestamps in live mode for `Header`-stamped messages. The live adapter previously shelled out to `ros2 topic echo --once`, which does not emit timestamps, so `MessageSample.timestamp_ns` was always `0`. The invocation is now `ros2 topic echo --csv --once`, whose flattened CSV exposes `header.stamp.sec` and `header.stamp.nanosec` as the first two columns for any `Header`-stamped message; the new `parse_csv_echo` parser reconstructs `timestamp_ns = sec * 1_000_000_000 + nanosec` and strips those two columns out of the payload. **Headerless message types** (e.g. `std_msgs/String`, `geometry_msgs/Twist`) still return `timestamp_ns=0` — they carry no embedded timestamp. Surfacing the rmw **receive** timestamp (rather than the publish-time `header.stamp`) for arbitrary message types remains a roadmap item tied to the future `rclpy`-backed adapter.
|
|
94
|
+
|
|
95
|
+
### Added
|
|
96
|
+
|
|
97
|
+
- **`mode_effective` on every tool response (schema, soft-breaking additive).** `TopicInfo`, `SampleResult`, and `BagAnalysis` now carry a required `mode_effective: Literal["mock", "live"]` field. A new `effective_mode` property on the `RosAdapter` protocol is the single source of truth; `Ros2CliAdapter` returns `"live"`, `MockAdapter` returns `"mock"`, services thread it through at result construction time. **Producer side**: Python code constructing these models directly must now supply `mode_effective` — models are `frozen=True, extra="forbid"` with no default. **Client side (over MCP)**: additive — an MCP client consuming JSON sees one extra key per response and is unaffected unless it strictly validates against the v0.1.1 schema with a no-extra-keys assumption.
|
|
98
|
+
- **DDS-MCP spec** (`docs/projet-file/mcp-02-spec.md`). Strategic draft for MCP 02 at the time: safety-first read-only DDS observability across middleware vendors (CycloneDDS OSS, RTI Connext Pro tier). Five tools, `MiddlewareAdapter` protocol, mock + cyclone + rti + auto modes. Reviewer notes appended (2026-05-13): wrong cross-reference in §11 flagged. (Reframed the next day as a TopicForge module after the mono-MCP pivot — see [0.2.0] Strategic section.)
|
|
99
|
+
- **DatasetForge spec** (`docs/projet-file/mcp-03-spec.md`). Vision Dataset Inspector spec, re-slotted to MCP 03 after competitive-landscape audit that surfaced zero non-ROS DDS-MCP projects and made a standalone DDS-MCP the stronger MCP 02 candidate. Reviewer notes appended (2026-05-13): contradictory §11 phrasing and two implicitly-resolved open questions flagged.
|
|
100
|
+
|
|
101
|
+
### Changed
|
|
102
|
+
|
|
103
|
+
- **Safety-first read-only repositioning.** README and `docs/product-plan.md §1` now lead with "read-only by architecture, not by configuration" as the primary identity. Pack candidate list updated: MCP 02 reframed to a non-ROS DDS observability MCP (later folded into TopicForge itself by the 2026-05-14 multi-vendor reframe — see v0.3.0 entry); DatasetForge slides to MCP 03. Strategic context in `docs/product-plan.md §4` and §8 (DDS-complete horizon).
|
|
104
|
+
- **Internal API.** `Inspector.sample_messages` now returns a `SampleResult` envelope (previously a `list[MessageSample]`). The MCP-facing tool handler is reduced to a thin pass-through. No effect on the tool's wire-level response shape (handlers already wrapped the list into `SampleResult`), but flagged here for anyone importing `Inspector` directly outside this repo.
|
|
105
|
+
|
|
106
|
+
### Internal
|
|
107
|
+
|
|
108
|
+
- Docstring fix in `parse_csv_echo`: the example output now shows post-strip payload keys as `col_0`, `col_1` (the parser re-indexes from `col_0` after dropping the two timestamp columns), matching the existing test in `tests/test_live_adapter_parse.py`.
|
|
109
|
+
|
|
110
|
+
## [0.1.1] - 2026-05-13
|
|
111
|
+
|
|
112
|
+
### Added
|
|
113
|
+
|
|
114
|
+
- **Opt-in anonymous usage telemetry** behind `TOPICFORGE_TELEMETRY=on` (default: off). When enabled, each MCP tool call emits a single event with six fields only: `tool_name`, `latency_ms`, `mode`, `version`, `session_id` (random UUID per process, never persisted), and `success`. No topic names, message bodies, bag paths, hostnames, or environment data ever leave the process. See the README "Telemetry" section for the full payload contract and opt-out instructions.
|
|
115
|
+
- `src/topicforge/telemetry/` module with `TelemetryClient`, `TelemetryEvent`, and an `instrument()` decorator that wraps tool handlers with timing + emit. When telemetry is off, `instrument()` is the identity function — zero overhead and zero possibility of a network call in the OFF code path.
|
|
116
|
+
- Pluggable `Transport` callable; v0.1.1 ships a structured-log transport. A future S3-backed HTTP endpoint will plug in without touching tool handlers.
|
|
117
|
+
- 29 telemetry tests covering: default-off behaviour, env var parsing (`on`/`1`/`true`/`yes`/`enabled` vs anything else), payload shape and key allowlist, payload privacy (user input never leaks), session id stability and per-process uniqueness, transport-exception isolation, decorator signature preservation, and end-to-end verification that the OFF code path never invokes the transport.
|
|
118
|
+
|
|
119
|
+
### Changed
|
|
120
|
+
|
|
121
|
+
- `Settings` gained a `telemetry_enabled: bool` field.
|
|
122
|
+
- `build_app(...)` accepts optional `telemetry` and `telemetry_transport` parameters for test injection.
|
|
123
|
+
- `register_tools(...)` now takes a `TelemetryClient`.
|
|
124
|
+
- `.env.example` documents `TOPICFORGE_TELEMETRY`.
|
|
125
|
+
- README adds a `Telemetry` section and updates the Security model note to reflect opt-in telemetry availability.
|
|
126
|
+
|
|
127
|
+
## [0.1.0] - 2026-05-12
|
|
128
|
+
|
|
129
|
+
Initial MVP release of TopicForge — ROS Topic Inspector & Bag Analyzer MCP server.
|
|
130
|
+
|
|
131
|
+
### Added
|
|
132
|
+
|
|
133
|
+
- Five read-only MCP tools exposed over FastMCP: `health_check`, `list_topics`, `get_topic_info`, `sample_messages`, and `analyze_bag`.
|
|
134
|
+
- `RosAdapter` protocol in `adapters/base.py` defining the contract every backend implements.
|
|
135
|
+
- Mock adapter (`adapters/ros2_mock/`) with deterministic fixtures modeling a small differential mobile robot equipped with a LIDAR and an RGB camera.
|
|
136
|
+
- Live adapter (`adapters/ros2_live/`) built on subprocess wrappers around the `ros2` CLI, with pure module-level parsers tested independently of any ROS2 install.
|
|
137
|
+
- Three runtime modes selectable via `TOPICFORGE_MODE`: `mock`, `live`, and `auto`. The `auto` resolution lives in `Settings.effective_mode`; the live-to-mock fallback when the adapter cannot start lives in `services/factory.py`.
|
|
138
|
+
- Windows-first cross-platform support: executable resolution via `shutil.which` (handles `ros2.cmd` / `ros2.bat` shims), `subprocess.run` called with absolute paths and never `shell=True`, all filesystem paths via `pathlib.Path`.
|
|
139
|
+
- Pydantic v2 schemas in `models/` configured with `extra="forbid"` and `frozen=True`, returned as the structured payload of every tool.
|
|
140
|
+
- Pytest suite that runs entirely without a ROS2 environment, covering services, mock adapter, and live-adapter parsers.
|
|
141
|
+
- Build, lint, and tooling configuration: Python 3.11+, `mcp >= 1.0.0` (FastMCP), `pydantic >= 2.6`, pytest, ruff, hatchling.
|
|
142
|
+
- Licensed under the MIT License.
|
|
143
|
+
|
|
144
|
+
### Notes
|
|
145
|
+
|
|
146
|
+
- The write path (publishing, commanding robots) is intentionally out of scope for the MVP.
|
|
147
|
+
- `analyze_bag` in live mode parses `ros2 bag info` text output; deeper anomaly detection remains mock-only for now.
|
|
148
|
+
|
|
149
|
+
[Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.3.0...HEAD
|
|
150
|
+
[0.3.0]: https://github.com/yaniswav/TopicForge/compare/v0.2.0...v0.3.0
|
|
151
|
+
[0.2.0]: https://github.com/yaniswav/TopicForge/compare/v0.1.2...v0.2.0
|
|
152
|
+
[0.1.2]: https://github.com/yaniswav/TopicForge/compare/v0.1.1...v0.1.2
|
|
153
|
+
[0.1.1]: https://github.com/yaniswav/TopicForge/compare/v0.1.0...v0.1.1
|
|
154
|
+
[0.1.0]: https://github.com/yaniswav/TopicForge/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: topicforge
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
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,16 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
40
40
|
Requires-Python: >=3.11
|
|
41
41
|
Requires-Dist: mcp>=1.0.0
|
|
42
42
|
Requires-Dist: pydantic>=2.6
|
|
43
|
+
Provides-Extra: all
|
|
44
|
+
Requires-Dist: cyclonedds>=0.10; extra == 'all'
|
|
45
|
+
Requires-Dist: fastdds<3,>=2.6.1; extra == 'all'
|
|
46
|
+
Provides-Extra: dds
|
|
47
|
+
Requires-Dist: cyclonedds>=0.10; extra == 'dds'
|
|
48
|
+
Requires-Dist: fastdds<3,>=2.6.1; extra == 'dds'
|
|
49
|
+
Provides-Extra: dds-cyclone
|
|
50
|
+
Requires-Dist: cyclonedds>=0.10; extra == 'dds-cyclone'
|
|
51
|
+
Provides-Extra: dds-fast
|
|
52
|
+
Requires-Dist: fastdds<3,>=2.6.1; extra == 'dds-fast'
|
|
43
53
|
Provides-Extra: dev
|
|
44
54
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
45
55
|
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
@@ -47,9 +57,14 @@ Description-Content-Type: text/markdown
|
|
|
47
57
|
|
|
48
58
|
# TopicForge
|
|
49
59
|
|
|
50
|
-
|
|
60
|
+
[](https://pypi.org/project/topicforge/)
|
|
61
|
+
[](https://pypi.org/project/topicforge/)
|
|
62
|
+
[](https://github.com/yaniswav/TopicForge/blob/main/LICENSE)
|
|
63
|
+
[](https://github.com/yaniswav/TopicForge#security-model)
|
|
51
64
|
|
|
52
|
-
|
|
65
|
+
> **The safety-first read-only MCP for ROS2 robotics — now with multi-vendor OMG DDS-RTPS observability (v0.3.0).** TopicForge lets AI agents inspect your ROS2 graph, ROS bag files, and (since v0.2.0) the raw DDS layer beneath ROS, without ever publishing back to the bus. v0.3.0 ships two OSS Python adapters — Eclipse CycloneDDS and eProsima Fast DDS — each joining the bus as a read-only DDS-RTPS participant that observes **every conformant vendor on the wire** (RTI Connext, OpenDDS, CoreDX, Dust DDS in Rust, etc.) via the OMG protocol guarantee. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md) for the canonical multi-vendor positioning and the OMG May 2025 interop reference.
|
|
66
|
+
|
|
67
|
+
TopicForge is a production-minded MCP (Model Context Protocol) server that lets AI agents — such as Claude — inspect ROS2 topics, analyze ROS bag files, and (since v0.2.0) observe the raw DDS layer through a clean, structured tool interface. It is read-only by **architecture**, not by configuration: there is no write path to misconfigure, no permission system to audit, no liability conversation to have. The MCP client can see the robot stack; it cannot touch it.
|
|
53
68
|
|
|
54
69
|
This stance matters because the ROS-MCP space is no longer empty — general-purpose ROS-MCP servers exist that let an LLM publish topics, call services, and command robots. That shape is fine for demos; it is untenable for production fleets, defense systems, automotive AUTOSAR Adaptive surfaces, or anything safety-certified. TopicForge is the read-only alternative for those audiences, plus the robotics developers, ML/CV engineers, and teams that want their AI tooling to *understand* their robotics stack without commanding it.
|
|
55
70
|
|
|
@@ -179,6 +194,47 @@ TOPICFORGE_MODE=live python -m topicforge
|
|
|
179
194
|
|
|
180
195
|
TopicForge invokes the `ros2` CLI under the hood, so it does **not** require `rclpy` to be importable. This keeps the live adapter portable across ROS2 distros.
|
|
181
196
|
|
|
197
|
+
### Multi-vendor DDS support (v0.3.0+)
|
|
198
|
+
|
|
199
|
+
Beyond ROS2 graph introspection, TopicForge observes the raw DDS bus directly via one of two OSS Python adapters — Eclipse CycloneDDS or eProsima Fast DDS — each joining as a **read-only DDS-RTPS participant**. By the OMG-DDS-RTPS protocol guarantee, both adapters see every conformant participant on the domain (RTI Connext, OpenDDS, CoreDX, Dust DDS in Rust, InterCOM, etc.) regardless of host language. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md) for the canonical multi-vendor positioning and the [OMG May 2025 interop reference](docs/projet-file/references/omg-dds-interop-2025-05-08.xlsx).
|
|
200
|
+
|
|
201
|
+
Useful for non-ROS DDS stacks (defense, aerospace, automotive AUTOSAR Adaptive, industrial integration) and for diagnosing why a ROS2 subscriber isn't receiving when the graph says it should. Same safety-first contract : read-only by **architecture** — the `MiddlewareAdapter` protocol does not expose a write method on any backend.
|
|
202
|
+
|
|
203
|
+
Install one or both OSS backends :
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Single vendor — install only what you need
|
|
207
|
+
pip install topicforge[dds-cyclone] # Eclipse CycloneDDS only
|
|
208
|
+
pip install topicforge[dds-fast] # eProsima Fast DDS only
|
|
209
|
+
pip install topicforge[dds] # both OSS backends (union)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Then select a backend :
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
TOPICFORGE_MODE=live TOPICFORGE_DDS_BACKEND=cyclone python -m topicforge
|
|
216
|
+
# or:
|
|
217
|
+
TOPICFORGE_MODE=live TOPICFORGE_DDS_BACKEND=fast python -m topicforge
|
|
218
|
+
# or, auto-select Fast > Cyclone > Mock:
|
|
219
|
+
TOPICFORGE_MODE=live TOPICFORGE_DDS_BACKEND=auto python -m topicforge
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Three new MCP tools (in addition to the five ROS2 tools above) :
|
|
223
|
+
|
|
224
|
+
| Tool | Purpose |
|
|
225
|
+
| ----------------------- | -------------------------------------------------------------------------------- |
|
|
226
|
+
| `list_participants` | DDS participants discovered on a domain, with vendor and hostname |
|
|
227
|
+
| `detect_qos_mismatches` | Reader/writer QoS incompatibilities preventing communication on a topic |
|
|
228
|
+
| `peek_dds_samples` | Recent samples on a raw DDS topic (distinct from `sample_messages` on ROS2 graph)|
|
|
229
|
+
|
|
230
|
+
**v0.3.0 limitation — single adapter at a time.** TopicForge selects one adapter per server run. With `TOPICFORGE_DDS_BACKEND=cyclone` (or `fast`), the 3 DDS tools work and the 5 ROS2 tools raise a clear `AdapterError` ; vice versa with the default `TOPICFORGE_DDS_BACKEND=mock`. The mock backend exposes all 8 tools against deterministic fixtures (incl. a `vendor="fast"` participant) for local development. A composite adapter delegating per-tool category is a v0.3.x roadmap item.
|
|
231
|
+
|
|
232
|
+
**v0.3.0 limitation — `peek_dds_samples` scope.** Full-fidelity on the 4 builtin DCPS topics (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`) ; arbitrary user topics raise an `AdapterError` pointing at the v0.3.x XTypes/IDL roadmap. The other two DDS tools (`list_participants`, `detect_qos_mismatches`) work end-to-end on any user-topic deployment.
|
|
233
|
+
|
|
234
|
+
**`RTI Connext`** is v0.4.0+ Pro tier (BYO license — see `docs/pro.md`).
|
|
235
|
+
|
|
236
|
+
**Full 5-minute walkthrough** — backend selection, the canonical QoS-mismatch debugging scenario, troubleshooting — lives in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md). Migration from v0.2.0 in [`docs/MIGRATION_v0.2_to_v0.3.md`](docs/MIGRATION_v0.2_to_v0.3.md).
|
|
237
|
+
|
|
182
238
|
### Configure with Claude Desktop
|
|
183
239
|
|
|
184
240
|
Add to your `claude_desktop_config.json`:
|
|
@@ -226,12 +282,14 @@ make check # both, plus tests (CI bundle)
|
|
|
226
282
|
|
|
227
283
|
## Configuration reference
|
|
228
284
|
|
|
229
|
-
| Variable
|
|
230
|
-
|
|
|
231
|
-
| `TOPICFORGE_MODE`
|
|
232
|
-
| `TOPICFORGE_LOG_LEVEL`
|
|
233
|
-
| `TOPICFORGE_ROS2_BIN`
|
|
234
|
-
| `TOPICFORGE_TELEMETRY`
|
|
285
|
+
| Variable | Default | Description |
|
|
286
|
+
| --------------------------- | ------- | ----------------------------------------------------------------------------- |
|
|
287
|
+
| `TOPICFORGE_MODE` | `auto` | `mock`, `live`, or `auto` |
|
|
288
|
+
| `TOPICFORGE_LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` |
|
|
289
|
+
| `TOPICFORGE_ROS2_BIN` | `ros2` | Name (or path) of the ROS2 CLI binary |
|
|
290
|
+
| `TOPICFORGE_TELEMETRY` | `off` | Opt-in anonymous usage telemetry. See [Telemetry](#telemetry). |
|
|
291
|
+
| `TOPICFORGE_DDS_BACKEND` | `mock` | DDS module backend: `mock`, `cyclone`, `fast`, `rti`, or `auto`. `auto` resolves to Fast > Cyclone > Mock. See [Multi-vendor DDS support](#multi-vendor-dds-support-v030). |
|
|
292
|
+
| `TOPICFORGE_DDS_DOMAIN_ID` | `0` | DDS domain id observed (0..232) when a DDS backend is active. |
|
|
235
293
|
|
|
236
294
|
See [`.env.example`](.env.example).
|
|
237
295
|
|
|
@@ -305,6 +363,10 @@ See [`docs/product-plan.md`](docs/product-plan.md) for the full product trajecto
|
|
|
305
363
|
Near-term additions on the bench:
|
|
306
364
|
|
|
307
365
|
- `rclpy`-backed live adapter for faster & richer sampling
|
|
366
|
+
- XTypes/IDL discovery to extend `peek_dds_samples` to arbitrary user topics (today: 4 builtin DCPS topics only) — v0.3.x patch
|
|
367
|
+
- Extended QoS coverage (Liveliness, Ownership, Partition, TimeBasedFilter, LatencyBudget) — v0.3.x patch
|
|
368
|
+
- Composite adapter delegating per-tool category, so ROS2 + DDS surfaces work simultaneously — v0.3.x patch
|
|
369
|
+
- `RtiConnextAdapter` in the Pro tier (BYO RTI Connext license, gated by `TOPICFORGE_LICENSE_KEY`) — v0.4.0+
|
|
308
370
|
- URDF inspector / validator MCP tools
|
|
309
371
|
- Bag anomaly detection (clock jumps, gaps, dropped frames, TF tree health)
|
|
310
372
|
- Dataset export helpers (rosbag → COCO / HF Datasets)
|
|
@@ -328,10 +390,13 @@ topicforge-mcp/
|
|
|
328
390
|
│ ├── services/ # Domain orchestration
|
|
329
391
|
│ ├── adapters/
|
|
330
392
|
│ │ ├── ros2_live/ # `ros2` CLI wrappers
|
|
331
|
-
│ │
|
|
393
|
+
│ │ ├── ros2_mock/ # Deterministic fixtures (incl. DDS)
|
|
394
|
+
│ │ ├── dds_cyclone/ # Eclipse CycloneDDS adapter (lazy import)
|
|
395
|
+
│ │ ├── dds_fast/ # eProsima Fast DDS adapter (lazy import)
|
|
396
|
+
│ │ └── common/ # Vendor-neutral helpers + pure QoS analyzer
|
|
332
397
|
│ ├── models/ # Pydantic schemas
|
|
333
398
|
│ └── config/ # Settings & mode resolution
|
|
334
|
-
└── tests/ # Pytest suite (mock-only, no ROS2 required)
|
|
399
|
+
└── tests/ # Pytest suite (mock-only, no ROS2/DDS required)
|
|
335
400
|
```
|
|
336
401
|
|
|
337
402
|
## License
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# TopicForge
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://pypi.org/project/topicforge/)
|
|
4
|
+
[](https://pypi.org/project/topicforge/)
|
|
5
|
+
[](https://github.com/yaniswav/TopicForge/blob/main/LICENSE)
|
|
6
|
+
[](https://github.com/yaniswav/TopicForge#security-model)
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
> **The safety-first read-only MCP for ROS2 robotics — now with multi-vendor OMG DDS-RTPS observability (v0.3.0).** TopicForge lets AI agents inspect your ROS2 graph, ROS bag files, and (since v0.2.0) the raw DDS layer beneath ROS, without ever publishing back to the bus. v0.3.0 ships two OSS Python adapters — Eclipse CycloneDDS and eProsima Fast DDS — each joining the bus as a read-only DDS-RTPS participant that observes **every conformant vendor on the wire** (RTI Connext, OpenDDS, CoreDX, Dust DDS in Rust, etc.) via the OMG protocol guarantee. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md) for the canonical multi-vendor positioning and the OMG May 2025 interop reference.
|
|
9
|
+
|
|
10
|
+
TopicForge is a production-minded MCP (Model Context Protocol) server that lets AI agents — such as Claude — inspect ROS2 topics, analyze ROS bag files, and (since v0.2.0) observe the raw DDS layer through a clean, structured tool interface. It is read-only by **architecture**, not by configuration: there is no write path to misconfigure, no permission system to audit, no liability conversation to have. The MCP client can see the robot stack; it cannot touch it.
|
|
6
11
|
|
|
7
12
|
This stance matters because the ROS-MCP space is no longer empty — general-purpose ROS-MCP servers exist that let an LLM publish topics, call services, and command robots. That shape is fine for demos; it is untenable for production fleets, defense systems, automotive AUTOSAR Adaptive surfaces, or anything safety-certified. TopicForge is the read-only alternative for those audiences, plus the robotics developers, ML/CV engineers, and teams that want their AI tooling to *understand* their robotics stack without commanding it.
|
|
8
13
|
|
|
@@ -132,6 +137,47 @@ TOPICFORGE_MODE=live python -m topicforge
|
|
|
132
137
|
|
|
133
138
|
TopicForge invokes the `ros2` CLI under the hood, so it does **not** require `rclpy` to be importable. This keeps the live adapter portable across ROS2 distros.
|
|
134
139
|
|
|
140
|
+
### Multi-vendor DDS support (v0.3.0+)
|
|
141
|
+
|
|
142
|
+
Beyond ROS2 graph introspection, TopicForge observes the raw DDS bus directly via one of two OSS Python adapters — Eclipse CycloneDDS or eProsima Fast DDS — each joining as a **read-only DDS-RTPS participant**. By the OMG-DDS-RTPS protocol guarantee, both adapters see every conformant participant on the domain (RTI Connext, OpenDDS, CoreDX, Dust DDS in Rust, InterCOM, etc.) regardless of host language. See [`docs/dds-interop-matrix.md`](docs/dds-interop-matrix.md) for the canonical multi-vendor positioning and the [OMG May 2025 interop reference](docs/projet-file/references/omg-dds-interop-2025-05-08.xlsx).
|
|
143
|
+
|
|
144
|
+
Useful for non-ROS DDS stacks (defense, aerospace, automotive AUTOSAR Adaptive, industrial integration) and for diagnosing why a ROS2 subscriber isn't receiving when the graph says it should. Same safety-first contract : read-only by **architecture** — the `MiddlewareAdapter` protocol does not expose a write method on any backend.
|
|
145
|
+
|
|
146
|
+
Install one or both OSS backends :
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Single vendor — install only what you need
|
|
150
|
+
pip install topicforge[dds-cyclone] # Eclipse CycloneDDS only
|
|
151
|
+
pip install topicforge[dds-fast] # eProsima Fast DDS only
|
|
152
|
+
pip install topicforge[dds] # both OSS backends (union)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Then select a backend :
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
TOPICFORGE_MODE=live TOPICFORGE_DDS_BACKEND=cyclone python -m topicforge
|
|
159
|
+
# or:
|
|
160
|
+
TOPICFORGE_MODE=live TOPICFORGE_DDS_BACKEND=fast python -m topicforge
|
|
161
|
+
# or, auto-select Fast > Cyclone > Mock:
|
|
162
|
+
TOPICFORGE_MODE=live TOPICFORGE_DDS_BACKEND=auto python -m topicforge
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Three new MCP tools (in addition to the five ROS2 tools above) :
|
|
166
|
+
|
|
167
|
+
| Tool | Purpose |
|
|
168
|
+
| ----------------------- | -------------------------------------------------------------------------------- |
|
|
169
|
+
| `list_participants` | DDS participants discovered on a domain, with vendor and hostname |
|
|
170
|
+
| `detect_qos_mismatches` | Reader/writer QoS incompatibilities preventing communication on a topic |
|
|
171
|
+
| `peek_dds_samples` | Recent samples on a raw DDS topic (distinct from `sample_messages` on ROS2 graph)|
|
|
172
|
+
|
|
173
|
+
**v0.3.0 limitation — single adapter at a time.** TopicForge selects one adapter per server run. With `TOPICFORGE_DDS_BACKEND=cyclone` (or `fast`), the 3 DDS tools work and the 5 ROS2 tools raise a clear `AdapterError` ; vice versa with the default `TOPICFORGE_DDS_BACKEND=mock`. The mock backend exposes all 8 tools against deterministic fixtures (incl. a `vendor="fast"` participant) for local development. A composite adapter delegating per-tool category is a v0.3.x roadmap item.
|
|
174
|
+
|
|
175
|
+
**v0.3.0 limitation — `peek_dds_samples` scope.** Full-fidelity on the 4 builtin DCPS topics (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`) ; arbitrary user topics raise an `AdapterError` pointing at the v0.3.x XTypes/IDL roadmap. The other two DDS tools (`list_participants`, `detect_qos_mismatches`) work end-to-end on any user-topic deployment.
|
|
176
|
+
|
|
177
|
+
**`RTI Connext`** is v0.4.0+ Pro tier (BYO license — see `docs/pro.md`).
|
|
178
|
+
|
|
179
|
+
**Full 5-minute walkthrough** — backend selection, the canonical QoS-mismatch debugging scenario, troubleshooting — lives in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md). Migration from v0.2.0 in [`docs/MIGRATION_v0.2_to_v0.3.md`](docs/MIGRATION_v0.2_to_v0.3.md).
|
|
180
|
+
|
|
135
181
|
### Configure with Claude Desktop
|
|
136
182
|
|
|
137
183
|
Add to your `claude_desktop_config.json`:
|
|
@@ -179,12 +225,14 @@ make check # both, plus tests (CI bundle)
|
|
|
179
225
|
|
|
180
226
|
## Configuration reference
|
|
181
227
|
|
|
182
|
-
| Variable
|
|
183
|
-
|
|
|
184
|
-
| `TOPICFORGE_MODE`
|
|
185
|
-
| `TOPICFORGE_LOG_LEVEL`
|
|
186
|
-
| `TOPICFORGE_ROS2_BIN`
|
|
187
|
-
| `TOPICFORGE_TELEMETRY`
|
|
228
|
+
| Variable | Default | Description |
|
|
229
|
+
| --------------------------- | ------- | ----------------------------------------------------------------------------- |
|
|
230
|
+
| `TOPICFORGE_MODE` | `auto` | `mock`, `live`, or `auto` |
|
|
231
|
+
| `TOPICFORGE_LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` |
|
|
232
|
+
| `TOPICFORGE_ROS2_BIN` | `ros2` | Name (or path) of the ROS2 CLI binary |
|
|
233
|
+
| `TOPICFORGE_TELEMETRY` | `off` | Opt-in anonymous usage telemetry. See [Telemetry](#telemetry). |
|
|
234
|
+
| `TOPICFORGE_DDS_BACKEND` | `mock` | DDS module backend: `mock`, `cyclone`, `fast`, `rti`, or `auto`. `auto` resolves to Fast > Cyclone > Mock. See [Multi-vendor DDS support](#multi-vendor-dds-support-v030). |
|
|
235
|
+
| `TOPICFORGE_DDS_DOMAIN_ID` | `0` | DDS domain id observed (0..232) when a DDS backend is active. |
|
|
188
236
|
|
|
189
237
|
See [`.env.example`](.env.example).
|
|
190
238
|
|
|
@@ -258,6 +306,10 @@ See [`docs/product-plan.md`](docs/product-plan.md) for the full product trajecto
|
|
|
258
306
|
Near-term additions on the bench:
|
|
259
307
|
|
|
260
308
|
- `rclpy`-backed live adapter for faster & richer sampling
|
|
309
|
+
- XTypes/IDL discovery to extend `peek_dds_samples` to arbitrary user topics (today: 4 builtin DCPS topics only) — v0.3.x patch
|
|
310
|
+
- Extended QoS coverage (Liveliness, Ownership, Partition, TimeBasedFilter, LatencyBudget) — v0.3.x patch
|
|
311
|
+
- Composite adapter delegating per-tool category, so ROS2 + DDS surfaces work simultaneously — v0.3.x patch
|
|
312
|
+
- `RtiConnextAdapter` in the Pro tier (BYO RTI Connext license, gated by `TOPICFORGE_LICENSE_KEY`) — v0.4.0+
|
|
261
313
|
- URDF inspector / validator MCP tools
|
|
262
314
|
- Bag anomaly detection (clock jumps, gaps, dropped frames, TF tree health)
|
|
263
315
|
- Dataset export helpers (rosbag → COCO / HF Datasets)
|
|
@@ -281,10 +333,13 @@ topicforge-mcp/
|
|
|
281
333
|
│ ├── services/ # Domain orchestration
|
|
282
334
|
│ ├── adapters/
|
|
283
335
|
│ │ ├── ros2_live/ # `ros2` CLI wrappers
|
|
284
|
-
│ │
|
|
336
|
+
│ │ ├── ros2_mock/ # Deterministic fixtures (incl. DDS)
|
|
337
|
+
│ │ ├── dds_cyclone/ # Eclipse CycloneDDS adapter (lazy import)
|
|
338
|
+
│ │ ├── dds_fast/ # eProsima Fast DDS adapter (lazy import)
|
|
339
|
+
│ │ └── common/ # Vendor-neutral helpers + pure QoS analyzer
|
|
285
340
|
│ ├── models/ # Pydantic schemas
|
|
286
341
|
│ └── config/ # Settings & mode resolution
|
|
287
|
-
└── tests/ # Pytest suite (mock-only, no ROS2 required)
|
|
342
|
+
└── tests/ # Pytest suite (mock-only, no ROS2/DDS required)
|
|
288
343
|
```
|
|
289
344
|
|
|
290
345
|
## License
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# DDS quickstart — TopicForge v0.3.0+
|
|
2
|
+
|
|
3
|
+
A 5-minute tour of TopicForge's multi-vendor DDS observability module. Both backends — Eclipse CycloneDDS and eProsima Fast DDS — join the bus as **read-only DDS-RTPS participants** and observe every conformant vendor on the wire via the OMG protocol guarantee. The `MiddlewareAdapter` protocol does not expose a write method, so the MCP client cannot publish back to the bus on any backend.
|
|
4
|
+
|
|
5
|
+
See [`docs/dds-interop-matrix.md`](dds-interop-matrix.md) for the canonical multi-vendor positioning and the [OMG May 2025 interop reference](projet-file/references/omg-dds-interop-2025-05-08.xlsx).
|
|
6
|
+
|
|
7
|
+
This guide does **not** assume you have ROS2 installed.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Mock mode — 30-second demo
|
|
12
|
+
|
|
13
|
+
The deterministic mock fixtures expose the full DDS tool surface without any DDS SDK or middleware. Useful for evaluating the tools before pulling in a real broker.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install topicforge
|
|
17
|
+
TOPICFORGE_MODE=mock python -m topicforge
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
In the spawned MCP client (Claude Desktop, Claude Code, Cursor, ...), the 3 DDS tools are now available alongside the 5 ROS2 tools:
|
|
21
|
+
|
|
22
|
+
- `list_participants(domain_id=0)` → returns 3 mock participants on domain 0 — two CycloneDDS (`mock-robot`, `mock-laptop`) and one Fast DDS (`mock-aerospace-node`). The multi-vendor mock exercises the canonical vendor enum (`cyclone`, `fast`, `rti`, `mock`, `unknown`).
|
|
23
|
+
- `detect_qos_mismatches(topic=None)` → returns 1 `MismatchReport` for `/dds/qos_mismatch` (deliberate Reliability incompatibility — RELIABLE reader vs BEST_EFFORT writer).
|
|
24
|
+
- `peek_dds_samples(topic="/dds/well_matched", count=3)` → returns 3 deterministic samples ; `peek_dds_samples(topic="/dds/qos_mismatch", count=1)` returns 1 sample with a `qos_note` field annotating the mismatch.
|
|
25
|
+
|
|
26
|
+
Mock fixtures are stable across runs — you can write integration tests against them.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. Live mode — choose your backend
|
|
31
|
+
|
|
32
|
+
v0.3.0 ships two OSS Python adapters. Pick one (or install both) :
|
|
33
|
+
|
|
34
|
+
### 2.a Eclipse CycloneDDS
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install topicforge[dds-cyclone]
|
|
38
|
+
TOPICFORGE_MODE=live TOPICFORGE_DDS_BACKEND=cyclone python -m topicforge
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The CycloneDDS adapter uses `cyclonedds.builtin.BuiltinDataReader` for polling-style discovery on the DCPS builtin topics. QoS policies are introspected via `Policy.*` class names. Bounded `take_iter` timeouts keep tool calls under 2 seconds.
|
|
42
|
+
|
|
43
|
+
### 2.b eProsima Fast DDS
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install topicforge[dds-fast]
|
|
47
|
+
TOPICFORGE_MODE=live TOPICFORGE_DDS_BACKEND=fast python -m topicforge
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The Fast DDS adapter attaches a `DomainParticipantListener`-shaped object to a freshly created participant and accumulates discovery callbacks under an RLock. A bounded `discovery_wait_ms=1500` warm-up after participant creation gives the listener time to populate before the first tool call.
|
|
51
|
+
|
|
52
|
+
### 2.c Both backends + auto resolution
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install topicforge[dds] # both Cyclone and Fast
|
|
56
|
+
TOPICFORGE_MODE=live TOPICFORGE_DDS_BACKEND=auto python -m topicforge
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`auto` resolves to the first available OSS backend in this order: `fast` > `cyclone` > `mock`. The order reflects the OMG May 2025 interop matrix where Fast DDS is validated against all five other vendors on 47/47 pairs. v0.2.0 users with only `cyclonedds` installed remain unchanged — Fast is unimportable on their host so the chain falls through to Cyclone.
|
|
60
|
+
|
|
61
|
+
### Domain selection
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
TOPICFORGE_DDS_DOMAIN_ID=42 python -m topicforge
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Accepts `0..232` (DDS spec range). Default is `0` — the same default used by most ROS2 setups.
|
|
68
|
+
|
|
69
|
+
### Pro tier — RTI Connext
|
|
70
|
+
|
|
71
|
+
`TOPICFORGE_DDS_BACKEND=rti` is reserved for the v0.4.0+ Pro tier (BYO RTI Connext license). Selecting it in the OSS core falls back to the ROS2 CLI adapter with a logged warning.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 3. The QoS mismatch scenario
|
|
76
|
+
|
|
77
|
+
Both real backends and the mock fixtures encode the canonical "subscriber doesn't receive" debugging case. From an MCP client:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
> Detect QoS mismatches on the current bus.
|
|
81
|
+
|
|
82
|
+
[tool call: detect_qos_mismatches]
|
|
83
|
+
[result]
|
|
84
|
+
[
|
|
85
|
+
{
|
|
86
|
+
"topic": "/dds/qos_mismatch",
|
|
87
|
+
"reader_guid": "010f1c2a.3b4c5d6e.7f800000.00000001",
|
|
88
|
+
"writer_guid": "010f1c2a.3b4c5d6e.7f800000.00000002",
|
|
89
|
+
"incompatible_policies": ["Reliability"],
|
|
90
|
+
"severity": "incompatible",
|
|
91
|
+
"mode_effective": "mock"
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
An LLM reading this output has enough information to suggest a concrete fix ("the writer is BEST_EFFORT but the reader requires RELIABLE — either relax the reader or upgrade the writer"). That is the diagnostic loop the DDS module is designed to support — and it works identically regardless of which backend produced the discovery samples, because the vendor-neutral pure analyzer at `src/topicforge/adapters/common/qos_analyzer.py` operates on canonical `QosProfile` Pydantic models.
|
|
97
|
+
|
|
98
|
+
The analyzer covers the four MVP policies — **Reliability**, **Durability**, **History**, **Deadline** — that explain the bulk of real-world mismatch cases. Liveliness, Ownership, Partition, TimeBasedFilter, and LatencyBudget are v0.3.x patches.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 4. Single-adapter limitation (v0.3.0)
|
|
103
|
+
|
|
104
|
+
TopicForge v0.3.0 still selects **one adapter at a time** based on `TOPICFORGE_MODE` + `TOPICFORGE_DDS_BACKEND` :
|
|
105
|
+
|
|
106
|
+
| `TOPICFORGE_MODE` | `TOPICFORGE_DDS_BACKEND` | Active adapter | ROS2 tools | DDS tools |
|
|
107
|
+
| ----------------- | ------------------------ | -------------------------- | ------------------------- | -------------------------- |
|
|
108
|
+
| `mock` | (any) | `MockAdapter` | work (fixtures) | work (fixtures) |
|
|
109
|
+
| `live` / `auto` | `mock` (default) | `Ros2CliAdapter` | work | raise with remediation |
|
|
110
|
+
| `live` / `auto` | `cyclone` | `CycloneDdsAdapter` | raise (DDS-only adapter) | work (real CycloneDDS) |
|
|
111
|
+
| `live` / `auto` | `fast` | `FastDdsAdapter` | raise (DDS-only adapter) | work (real Fast DDS) |
|
|
112
|
+
| `live` / `auto` | `rti` | falls back to ROS2 CLI | work (CLI) | raise (v0.4.0+ Pro tier) |
|
|
113
|
+
|
|
114
|
+
A composite adapter that delegates per-tool category (ROS2 graph vs DDS layer) is on the v0.3.x roadmap. For now, restart the server with a different `TOPICFORGE_DDS_BACKEND` to switch sides.
|
|
115
|
+
|
|
116
|
+
Error messages on the unselected side are explicit and point at the remediation path — no silent failures.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 5. v0.3.0 scope of `peek_dds_samples`
|
|
121
|
+
|
|
122
|
+
`peek_dds_samples` is full-fidelity on the 4 builtin DCPS topics with both backends :
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
peek_dds_samples(topic="DCPSParticipant", count=5)
|
|
126
|
+
peek_dds_samples(topic="DCPSSubscription", count=10)
|
|
127
|
+
peek_dds_samples(topic="DCPSPublication", count=10)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Arbitrary user topics raise an `AdapterError` pointing at the v0.3.x roadmap — XTypes/IDL discovery (`cyclonedds.dynamic.get_types_for_typeid` on Cyclone, XTypes remote-type lookup on Fast DDS) is the missing piece for arbitrary user-topic peek.
|
|
131
|
+
|
|
132
|
+
The other two DDS tools — `list_participants` and `detect_qos_mismatches` — work end-to-end on any user-topic deployment ; they don't depend on payload deserialization.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 6. What's next
|
|
137
|
+
|
|
138
|
+
- **v0.3.x patch** — XTypes/IDL discovery to extend `peek_dds_samples` to arbitrary user topics on both backends.
|
|
139
|
+
- **v0.3.x patch** — Extended QoS coverage : Liveliness, Ownership, Partition, TimeBasedFilter, LatencyBudget.
|
|
140
|
+
- **v0.3.x patch** — Composite adapter routing ROS2 graph tools to `Ros2CliAdapter` and DDS tools to the selected DDS adapter, so both surfaces are usable simultaneously.
|
|
141
|
+
- **v0.4.0+** — `RtiConnextAdapter` in the Pro tier (BYO RTI Connext license, gated by `TOPICFORGE_LICENSE_KEY`).
|
|
142
|
+
|
|
143
|
+
Full strategic roadmap lives in [`docs/product-plan.md`](product-plan.md) and the DDS module spec at [`docs/projet-file/mcp-02-spec.md`](projet-file/mcp-02-spec.md).
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 7. Troubleshooting
|
|
148
|
+
|
|
149
|
+
- **`pip install topicforge[dds-cyclone]` fails on Windows / macOS Python 3.13+** — `cyclonedds` wheels are typically published for Python 3.8 to 3.12. Pin Python 3.11 or 3.12 for the install host.
|
|
150
|
+
- **`pip install topicforge[dds-cyclone]` fails with `CYCLONEDDS_HOME`** — pip is trying to build `cyclonedds` from source because no wheel matches your platform/Python combination. Either switch to a supported Python (3.11/3.12) or install the native CycloneDDS C library first (see Eclipse CycloneDDS releases).
|
|
151
|
+
- **`pip install topicforge[dds-fast]` fails** — eProsima Fast DDS Python bindings (`fastdds>=2.6.1,<3`) currently ship wheels for Linux first. Windows wheels lag ; consult fast-dds.docs.eprosima.com for the current matrix.
|
|
152
|
+
- **DDS tool returns "v0.3.x roadmap" error** — you called `peek_dds_samples` on an arbitrary user topic. The 4 builtin DCPS topics work today ; arbitrary user-topic peek is a v0.3.x patch (XTypes/IDL discovery).
|
|
153
|
+
- **DDS tool returns "DDS module is not active" error** — your `TOPICFORGE_DDS_BACKEND` is `mock` while `TOPICFORGE_MODE` is `live` (the ROS2 CLI adapter is selected). Set `TOPICFORGE_DDS_BACKEND=cyclone` or `=fast` explicitly to enable the DDS adapters.
|
|
154
|
+
- **`auto` selects the wrong backend** — `auto` prefers Fast > Cyclone > Mock. If you want Cyclone explicitly, set `TOPICFORGE_DDS_BACKEND=cyclone` rather than relying on `auto`.
|
|
155
|
+
|
|
156
|
+
Report issues at https://github.com/yaniswav/TopicForge/issues.
|