topicforge 0.4.0__tar.gz → 0.5.1__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.4.0 → topicforge-0.5.1}/.gitignore +6 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/CHANGELOG.md +329 -2
- {topicforge-0.4.0 → topicforge-0.5.1}/PKG-INFO +28 -23
- {topicforge-0.4.0 → topicforge-0.5.1}/README.md +22 -20
- {topicforge-0.4.0 → topicforge-0.5.1}/docs/DDS_QUICKSTART.md +31 -22
- topicforge-0.5.1/docs/MIGRATION_v0.3_to_v0.4.md +242 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/docs/TESTING.md +12 -3
- topicforge-0.5.1/docs/TROUBLESHOOTING.md +245 -0
- topicforge-0.5.1/docs/TUTORIEL.md +204 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/docs/product-plan.md +4 -4
- topicforge-0.5.1/examples/README.md +30 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/pyproject.toml +36 -2
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/__init__.py +1 -1
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/__init__.py +30 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/cdr_decoder.py +22 -5
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/dds_helpers.py +33 -5
- topicforge-0.5.1/src/topicforge/adapters/common/dds_introspection.py +184 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/lifecycle.py +33 -4
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/metrics_buffer.py +70 -20
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/qos_analyzer.py +21 -9
- topicforge-0.5.1/src/topicforge/adapters/common/qos_endpoints.py +95 -0
- topicforge-0.5.1/src/topicforge/adapters/common/qos_normalize.py +178 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/common/xtypes.py +8 -5
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_cyclone/adapter.py +105 -210
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_dust/adapter.py +2 -2
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_fast/adapter.py +91 -201
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_opendds/adapter.py +16 -10
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/ros2_mock/adapter.py +3 -4
- topicforge-0.5.1/src/topicforge/constants.py +28 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/services/bag_service.py +13 -16
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/services/health.py +1 -1
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/services/inspector.py +8 -11
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/tools/handlers.py +8 -5
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/test_scenarios_schema.py +2 -1
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_bag_service.py +46 -2
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_cdr_decoder.py +34 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_dds_helpers.py +63 -0
- topicforge-0.5.1/tests/test_dds_introspection.py +215 -0
- topicforge-0.5.1/tests/test_dds_qos_normalization.py +286 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_health.py +1 -1
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_lifecycle_buffer.py +38 -1
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_metrics_buffer.py +85 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_opendds_adapter.py +4 -2
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_qos_analyzer.py +21 -0
- topicforge-0.5.1/tests/test_qos_endpoints.py +156 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_tools_integration.py +23 -0
- topicforge-0.4.0/src/topicforge/services/constants.py +0 -21
- {topicforge-0.4.0 → topicforge-0.5.1}/LICENSE +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/docs/MIGRATION_v0.1_to_v0.2.md +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/docs/MIGRATION_v0.2_to_v0.3.md +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/docs/dds-interop-matrix.md +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/docs/pro.md +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/scripts/integration/README.md +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/__main__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/base.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/composite.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_cyclone/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_dust/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_fast/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/dds_opendds/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/ros2_live/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/ros2_live/adapter.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/ros2_mock/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/adapters/ros2_mock/fixtures.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/config/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/config/settings.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/models/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/models/schemas.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/server/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/server/app.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/services/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/services/factory.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/telemetry/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/telemetry/client.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/src/topicforge/tools/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/conftest.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/fixtures/csv_echo_imu.txt +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/fixtures/csv_echo_pose_multi.txt +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/__init__.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/conftest.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/lifecycle_tracking.json +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/multi_vendor_basic.json +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/qos_mismatch_detection.json +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/topic_metrics_frequency.json +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/topic_metrics_sequence_gaps.json +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/scenarios/xtypes_decode.json +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/integration/test_real_bus.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_analyze_bag_multi_format.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_composite_adapter.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_config.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_cyclone_adapter.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_dds_cross_vendor.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_dds_schemas.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_dust_adapter.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_factory.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_fast_adapter.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_inspector.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_live_adapter_parse.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_live_adapter_subprocess.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_mock_adapter.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_peek_bag_samples.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_pro_hook.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_telemetry.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_topic_metrics.py +0 -0
- {topicforge-0.4.0 → topicforge-0.5.1}/tests/test_xtypes.py +0 -0
|
@@ -217,6 +217,12 @@ CLAUDE*.md
|
|
|
217
217
|
# remain reproducible. Tracked, not part of sdist.
|
|
218
218
|
!/docs/projet-file/references/
|
|
219
219
|
!/docs/projet-file/references/**
|
|
220
|
+
# Archive of superseded strategic artifacts (old audit reports, dated
|
|
221
|
+
# launch-post drafts). Kept in git so the historical context survives
|
|
222
|
+
# but tucked away so the active `projet-file/` folder stays focused on
|
|
223
|
+
# what is currently load-bearing.
|
|
224
|
+
!/docs/projet-file/archive/
|
|
225
|
+
!/docs/projet-file/archive/**
|
|
220
226
|
/docs/assets/screencast-raw/
|
|
221
227
|
|
|
222
228
|
|
|
@@ -5,7 +5,331 @@ All notable changes to TopicForge are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.5.1] - 2026-08-22
|
|
11
|
+
|
|
12
|
+
### Fixed (hotfix — MCP SDK 2.0 incompatibility)
|
|
13
|
+
|
|
14
|
+
- **Hard-pinned `mcp < 2` — TopicForge was uninstallable from 2026-07-28
|
|
15
|
+
to 2026-08-22.** The MCP Python SDK released `2.0.0` on 2026-07-28
|
|
16
|
+
alongside the `2026-07-28` protocol revision. That release removes the
|
|
17
|
+
`mcp.server.fastmcp` module entirely (`mcp/server/` now ships `auth`,
|
|
18
|
+
`lowlevel`, and `mcpserver`), so `server/app.py`'s
|
|
19
|
+
`from mcp.server.fastmcp import FastMCP` raises `ImportError` against
|
|
20
|
+
it. The dependency was declared as an unbounded `mcp>=1.0.0`, so every
|
|
21
|
+
fresh `pip install topicforge` resolved to `2.0.0` and produced a
|
|
22
|
+
server that could not start. Local development and CI both masked the
|
|
23
|
+
break — the dev environment had `mcp 1.27.1` already installed, and
|
|
24
|
+
the last CI run predates the SDK release. Pinning `<2` restores
|
|
25
|
+
installability on the 1.x line; migrating to the 2.x API
|
|
26
|
+
(`FastMCP` -> `MCPServer`, transport options moved from the
|
|
27
|
+
constructor to `.run()`, stateless protocol) is tracked separately and
|
|
28
|
+
is deliberately **not** bundled into this hotfix.
|
|
29
|
+
|
|
30
|
+
### Note on scope
|
|
31
|
+
|
|
32
|
+
- This release also publishes the external-audit work (Lots 0-5,
|
|
33
|
+
2026-07-08) that had accumulated under `[Unreleased]`. The Cyclone
|
|
34
|
+
`take_iter` -> `read_iter` change documented below is still **validated
|
|
35
|
+
by static analysis only** (`ruff` + `py_compile`); it has not been
|
|
36
|
+
exercised against a real multi-vendor DDS bus. It ships here because
|
|
37
|
+
leaving the package uninstallable was the larger harm — a broken
|
|
38
|
+
install affects every user, while this change can only affect users
|
|
39
|
+
running the Cyclone backend against a live bus. Real-bus validation
|
|
40
|
+
remains open.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- **DDS pure logic extracted for testability (Lot 0, external audit
|
|
45
|
+
2026-07-08).** The QoS-profile normalizers (`_cyclone_qos_to_profile` /
|
|
46
|
+
`_fast_qos_to_profile`) and the discovery-sample field extractors
|
|
47
|
+
(`_extract_guid` / `_extract_vendor_id` / `_extract_hostname` /
|
|
48
|
+
`_extract_topic_name` / `_is_removal`) were moved out of
|
|
49
|
+
`adapters/dds_cyclone/adapter.py` and `adapters/dds_fast/adapter.py` —
|
|
50
|
+
which import their vendor binding at module top level and were therefore
|
|
51
|
+
never exercised by the test suite — into the binding-free
|
|
52
|
+
`adapters/common/qos_normalize.py` and `adapters/common/dds_introspection.py`.
|
|
53
|
+
The adapters import them back under their original private names, so every
|
|
54
|
+
call site is unchanged (verified by `ruff check` static analysis, since the
|
|
55
|
+
adapters are not importable without their SDKs). `fast_qos_to_profile`
|
|
56
|
+
takes the binding's int→str enum maps as parameters so it stays
|
|
57
|
+
import-free. This is the highest-value item from the audit: the QoS
|
|
58
|
+
normalization feeds `detect_qos_mismatches` (the flagship DDS diagnostic)
|
|
59
|
+
and was previously untestable and untested.
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- `tests/test_dds_qos_normalization.py` and `tests/test_dds_introspection.py`
|
|
64
|
+
drive the extracted logic with synthetic duck-typed objects (no
|
|
65
|
+
`cyclonedds` / `fastdds` needed), including regression guards for the
|
|
66
|
+
"renamed policy key silently yields no QoS profile → no mismatch ever
|
|
67
|
+
reported" failure mode. The extracted modules are now ~91–92% covered.
|
|
68
|
+
- `pytest-cov` and `rosbags` added to the `[dev]` extra, plus `[tool.coverage]`
|
|
69
|
+
config with a `fail_under = 85` floor (the two binding-only adapter shells
|
|
70
|
+
are `omit`ted as structurally unreachable without their SDKs). Adding
|
|
71
|
+
`rosbags` un-skips the real `.db3` bag-analysis I/O test.
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- `tests/test_bag_service.py` bag-generation helper updated for the current
|
|
76
|
+
`rosbags` API (`Writer(..., version=Writer.VERSION_LATEST)`; typestore keyed
|
|
77
|
+
by `std_msgs/msg/String`, not `std_msgs__msg__String`). The test was
|
|
78
|
+
previously auto-skipped and had gone silently stale — un-skipping it in CI
|
|
79
|
+
surfaced the drift.
|
|
80
|
+
- **`topic_metrics` frequency was wrong (Lot 2, audit C5).** It divided the
|
|
81
|
+
sample count by `(now − oldest_sample)` — folding in idle time since the
|
|
82
|
+
last peek — and counted N intervals instead of N−1. Now measured as
|
|
83
|
+
`(N−1) / (newest − oldest)` over the samples' own arrival span; samples
|
|
84
|
+
surfaced by a single opportunistic peek share one timestamp (span 0) and
|
|
85
|
+
correctly yield `frequency_hz_observed = null` instead of a fabricated rate.
|
|
86
|
+
- **`topic_metrics` sequence-gap count exploded on multi-writer topics,
|
|
87
|
+
publisher restarts, and counter wrap (Lot 2, audit C6).** Gaps are now
|
|
88
|
+
counted per writer (new best-effort `MetricsSample.writer_guid`), so
|
|
89
|
+
independent writers' counter offsets are not read as phantom gaps, and a
|
|
90
|
+
single jump wider than 10 000 is treated as a reset/wrap discontinuity
|
|
91
|
+
rather than that many losses.
|
|
92
|
+
- **QoS Deadline false negative (Lot 2, audit C3/P1-3).**
|
|
93
|
+
`detect_qos_mismatches` now flags a reader that requests a finite Deadline
|
|
94
|
+
against a writer that offers none — an absent deadline is the infinite
|
|
95
|
+
(loosest) period and cannot satisfy a finite request. The previous rule
|
|
96
|
+
required both sides non-null and silently missed this incompatibility.
|
|
97
|
+
- **`LifecycleBuffer` participant map was unbounded (Lot 3, audit P1-4 / M1 /
|
|
98
|
+
P1).** Only the event ring was capped; the participant dict grew one entry
|
|
99
|
+
per GUID ever seen (a churny bus mints a fresh RTPS GUID on each node
|
|
100
|
+
restart), and `list_participants` returned every tombstone forever. Now
|
|
101
|
+
capped at `MAX_PARTICIPANTS = 4096`, evicting `"left"` tombstones first then
|
|
102
|
+
the oldest-inserted entry — the docstring's "Bounded" claim is now true.
|
|
103
|
+
- **`MetricsBuffer` topic map was unbounded (Lot 3, audit P2-5).** Per-topic
|
|
104
|
+
rings were capped but the number of topic keys was not; now capped at
|
|
105
|
+
`MAX_TOPICS = 4096`, oldest-inserted topic evicted on overflow.
|
|
106
|
+
- **OpenDDS stub `is_available()` always returns False (Lot 4, audit S1).** A
|
|
107
|
+
stub that advertised availability (when a `pyopendds` module happened to be
|
|
108
|
+
importable) could be auto-selected by the factory, after which every tool
|
|
109
|
+
call raised. Now consistent with the Dust stub.
|
|
110
|
+
- **`iter_field_names` mis-decoded a string `__slots__` (Lot 4, audit C2).**
|
|
111
|
+
`__slots__ = "value"` was exploded into `['v','a','l','u','e']`; a bare
|
|
112
|
+
string slot is now treated as a single field name.
|
|
113
|
+
- **`decode_field_value` recursion is depth-capped at 32 (Lot 4, audit M6).**
|
|
114
|
+
A pathologically deep decoded object graph collapses to `repr()` instead of
|
|
115
|
+
risking `RecursionError`.
|
|
116
|
+
- **`_encode_raw_bytes` slices before hex-encoding (Lot 4, audit M5).** A large
|
|
117
|
+
raw payload no longer allocates its full 2×-size hex string only to truncate
|
|
118
|
+
it to the 4096-char preview.
|
|
119
|
+
|
|
120
|
+
### Added (Lot 4 — test hardening)
|
|
121
|
+
|
|
122
|
+
- End-to-end test that a failing tool call surfaces as an MCP error
|
|
123
|
+
(`ToolError`) rather than being masked as a success — pins the thin-handler
|
|
124
|
+
contract (CLAUDE.md §8, audit test-gap #4).
|
|
125
|
+
- Test pinning that every canonical vendor tag is a valid
|
|
126
|
+
`ParticipantInfo`/`ParticipantEvent.vendor` Literal (guards against
|
|
127
|
+
vendor-map ↔ schema drift, audit P2-3). Scenario allowlist `_KNOWN_TOOLS`
|
|
128
|
+
now includes the 11th tool `peek_bag_samples`.
|
|
129
|
+
|
|
130
|
+
### Removed (Lot 4)
|
|
131
|
+
|
|
132
|
+
- Dead `annotate_full` / `annotate_partial` imports and the `_ = (...)`
|
|
133
|
+
unused-suppressor from `services/bag_service.py`.
|
|
134
|
+
|
|
135
|
+
### Documentation (Lot 1 — reconcile the strategic source of truth)
|
|
136
|
+
|
|
137
|
+
- **`docs/product-plan.md` realigned on the shipped 11-tool surface (audit
|
|
138
|
+
M6/P1-6).** §1 and §4 said "five typed tools today" / DDS "roadmapped"
|
|
139
|
+
while six DDS/observability tools had shipped across v0.2.0–v0.4.0. §11's
|
|
140
|
+
risk register carried a self-imposed governance gate — "any 9th tool needs
|
|
141
|
+
an explicit re-scope discussion documented in this register before code
|
|
142
|
+
lands" — that was crossed during v0.4.0 without the discussion being
|
|
143
|
+
recorded. Added a retroactive re-scope decision closing that gap: the three
|
|
144
|
+
ceiling-breaking tools are accepted, the new ceiling is 11 tools, a 12th
|
|
145
|
+
needs a documented re-scope.
|
|
146
|
+
- **User-topic `raw` decode honesty (audit C1).** README and the
|
|
147
|
+
`peek_dds_samples` tool description no longer imply the `raw` fallback
|
|
148
|
+
preserves the payload in `_raw_bytes_hex` — on the current user-topic raw
|
|
149
|
+
path that field is empty (a `raw` status means "present but not decoded").
|
|
150
|
+
Capturing the on-wire CDR bytes is stated as roadmapped rather than done.
|
|
151
|
+
|
|
152
|
+
### Changed (Lot 5 — DDS adapter deduplication)
|
|
153
|
+
|
|
154
|
+
- **QoS-mismatch endpoint pairing deduplicated (audit D1/M7/P2).** The ~40
|
|
155
|
+
identical lines in each adapter's `detect_qos_mismatches` (group endpoints by
|
|
156
|
+
topic, pair reader × writer, build `MismatchReport`) moved to the binding-free
|
|
157
|
+
`common/qos_endpoints.detect_mismatches_across_endpoints`, unit-tested without
|
|
158
|
+
a binding. Each writer's QoS profile is now parsed once per topic instead of
|
|
159
|
+
once per reader (fixes the O(readers × writers) re-parse). Both adapters
|
|
160
|
+
delegate to it.
|
|
161
|
+
- **Shared `validate_domain_id` (`common/dds_helpers`).** The identical 0..232
|
|
162
|
+
bound check in all four DDS adapter constructors (Cyclone, Fast, OpenDDS,
|
|
163
|
+
Dust) is now defined once.
|
|
164
|
+
- **Cyclone discovery/sample reads switched from `take_iter` to `read_iter`
|
|
165
|
+
(audit A1/P1-5).** Destructive `take` drained the builtin discovery cache,
|
|
166
|
+
risking spurious lost / re-discovered participant flapping across polls;
|
|
167
|
+
`read` is non-destructive — the correct choice for read-only observability.
|
|
168
|
+
⚠️ **Requires real-bus validation on `scripts/integration/` before release**:
|
|
169
|
+
the read-vs-take semantics cannot be exercised without `cyclonedds` installed
|
|
170
|
+
(the adapter is not importable in the unit environment; these edits are
|
|
171
|
+
validated only by `ruff` static analysis + `py_compile`).
|
|
172
|
+
|
|
173
|
+
Baseline: 399 → 485 passed, 24 → 23 skipped, ruff clean, coverage 89.12%.
|
|
174
|
+
|
|
175
|
+
## [0.5.0] - 2026-05-21
|
|
176
|
+
|
|
177
|
+
### Sprint v0.5.0 — Polish + validation (pre-marketing-publication)
|
|
178
|
+
|
|
179
|
+
> Branch `feat/v0.5.0-polish-and-validation`. Three sub-milestones
|
|
180
|
+
> (5.1 real validation, 5.2 audit closure + error polish, 5.3 docs +
|
|
181
|
+
> examples + repo polish). No new MCP tool, no schema change. Last
|
|
182
|
+
> sprint before marketing publication ; the next version bump is
|
|
183
|
+
> the maintainer's manual `release` commit + tag.
|
|
184
|
+
|
|
185
|
+
#### Changed (sub-milestone 5.1 — Real validation)
|
|
186
|
+
|
|
187
|
+
- **CI matrix expanded** (`.github/workflows/ci.yml`) from
|
|
188
|
+
`ubuntu-latest × {3.11, 3.12}` to `{ubuntu-latest, windows-latest} ×
|
|
189
|
+
{3.11, 3.12, 3.13}` (6 cells, `fail-fast: false`). Windows-latest
|
|
190
|
+
coverage closes the largest unverified surface — TopicForge's primary
|
|
191
|
+
dev environment is Windows per `CLAUDE.md §7` and was previously only
|
|
192
|
+
hand-validated. Python 3.13 added now that wheels are stable across
|
|
193
|
+
the dependency footprint.
|
|
194
|
+
- **`pyproject.toml` classifiers** widened with `Programming Language ::
|
|
195
|
+
Python :: 3.13`.
|
|
196
|
+
|
|
197
|
+
#### Changed (sub-milestone 5.2 — AdapterError polish)
|
|
198
|
+
|
|
199
|
+
- **`DDS_ONLY_ERROR_MSG`** (`adapters/common/dds_helpers.py`) rewritten
|
|
200
|
+
with the v0.4.0 `CompositeAdapter` remediation path explicit, and
|
|
201
|
+
with every affected ROS2 tool name listed inline. Substring
|
|
202
|
+
`"DDS observability only"`, `"TOPICFORGE_DDS_BACKEND"`,
|
|
203
|
+
`"TOPICFORGE_MODE"` preserved — existing `pytest.raises(match=...)`
|
|
204
|
+
contracts honored. New token assertions added :
|
|
205
|
+
`"CompositeAdapter"`, the 4 affected tool names.
|
|
206
|
+
- **CycloneDDS adapter errors** (`adapters/dds_cyclone/adapter.py`)
|
|
207
|
+
enriched with the underlying exception type, the active domain id,
|
|
208
|
+
the topic name (where relevant), and common-cause diagnostic hints
|
|
209
|
+
(`CYCLONEDDS_URI` misconfiguration, multicast firewall, domain
|
|
210
|
+
mismatch). Three sites : participant discovery, endpoint discovery,
|
|
211
|
+
sample peek.
|
|
212
|
+
- **Fast DDS participant-init error** (`adapters/dds_fast/adapter.py`)
|
|
213
|
+
enriched with an ABI-mismatch diagnostic — Fast DDS 2.6.x Python
|
|
214
|
+
binding wheels frequently desynchronize with system-installed Fast
|
|
215
|
+
DDS native libraries, and the v0.4.0 wording masked the cause behind
|
|
216
|
+
a bare `"returned None"`. New message points at the pyproject pin
|
|
217
|
+
(`fastdds>=2.6.1,<3`) and the `FastDDS_DEFAULT_PROFILES_FILE` env
|
|
218
|
+
var as the two likely culprits.
|
|
219
|
+
- **`BagService` IO errors** (`services/bag_service.py`) wrap the
|
|
220
|
+
rosbags-side exception class name into the AdapterError message so
|
|
221
|
+
the LLM caller can pivot on `PermissionError` / `IsADirectoryError`
|
|
222
|
+
/ etc. without inspecting `__cause__`.
|
|
223
|
+
- **`_ROSBAGS_REQUIRED_MSG`** rewording — clarifies that `analyze_bag`
|
|
224
|
+
has a v0.3.0 text-parse fallback while `peek_bag_samples` does not.
|
|
225
|
+
|
|
226
|
+
#### Closed (sub-milestone 5.2 — audit triage)
|
|
227
|
+
|
|
228
|
+
- **`docs/projet-file/audit-followup-triage-v0.2.0.md` refreshed**
|
|
229
|
+
against the current tree (was last touched pre-v0.3.0). Strict
|
|
230
|
+
B-class : 3 items now CLOSED (B6 in v0.2.0, B9 in v0.3.0, B10 in
|
|
231
|
+
v0.5.0). 6 items remain DEFER (B1-B5 hosted-context security
|
|
232
|
+
hardening ; B7, B8 wire-contract decisions for v0.6+).
|
|
233
|
+
- **`TODO(roadmap, audit-2026-05-14)` at `services/inspector.py:76`
|
|
234
|
+
retired as WONT-FIX-by-design** — the `list_topics` Inspector gate
|
|
235
|
+
is intentionally empty (no MCP-level args to validate). The comment
|
|
236
|
+
block is now a permanent design note rather than a roadmap pointer.
|
|
237
|
+
|
|
238
|
+
#### Added (sub-milestone 5.2 — regression tests)
|
|
239
|
+
|
|
240
|
+
- **5 new tests** in `tests/test_dds_helpers.py` and
|
|
241
|
+
`tests/test_bag_service.py` (regex-token assertions, not exact
|
|
242
|
+
wording) — pin the polished message contract without locking the
|
|
243
|
+
exact prose. Baseline grows from 394 to 399 passed ; 24 skipped
|
|
244
|
+
unchanged ; ruff clean.
|
|
245
|
+
|
|
246
|
+
#### Changed (sub-milestone 5.3 — Documentation cascade)
|
|
247
|
+
|
|
248
|
+
- **`README.md`** — CI badge added ; tagline refreshed from "v0.3.0"
|
|
249
|
+
to "v0.4.0" framing emphasising observability + bag analysis ; the
|
|
250
|
+
3-row DDS tool mini-table grew into a 6-row table listing every
|
|
251
|
+
DDS / observability tool with its sub-milestone of origin ;
|
|
252
|
+
`peek_dds_samples` scope rewritten around `_decode_status` (full /
|
|
253
|
+
partial / raw) ; telemetry contract field description switched from
|
|
254
|
+
"five MVP tools" to "eleven MCP tools" ; `TOPICFORGE_DDS_BACKEND`
|
|
255
|
+
Literal values listed in the config reference ; Roadmap section
|
|
256
|
+
pruned of items that shipped in v0.4.0 (Composite adapter, XTypes
|
|
257
|
+
Cyclone push).
|
|
258
|
+
- **`docs/DDS_QUICKSTART.md`** — header bumped to v0.4.0+ ; §4
|
|
259
|
+
"Single-adapter limitation (v0.3.0)" replaced by "Composite adapter
|
|
260
|
+
(v0.4.0 Phase 1+)" with the new routing table ; §5 documents the
|
|
261
|
+
v0.4.0 Phase 1.5 best-effort XTypes story (no more
|
|
262
|
+
`AdapterError` on user topics) ; §6 "What's next" pruned of shipped
|
|
263
|
+
items ; §7 Troubleshooting updated.
|
|
264
|
+
- **`docs/TESTING.md`** — "five MCP tools" → "eleven MCP tools" in the
|
|
265
|
+
three documented occurrences ("Pick your path" table row, the lead
|
|
266
|
+
paragraph, Path 1 header). New v0.4.0 tool-surface callout above
|
|
267
|
+
the path picker.
|
|
268
|
+
|
|
269
|
+
#### Added (sub-milestone 5.3 — new docs and examples)
|
|
270
|
+
|
|
271
|
+
- **`docs/MIGRATION_v0.3_to_v0.4.md`** (new, sibling to the existing
|
|
272
|
+
`MIGRATION_v0.2_to_v0.3.md`). 8 sections : new tools, env vars and
|
|
273
|
+
extras, soft-breaking schema widening (`ParticipantInfo` +4 fields,
|
|
274
|
+
`BagAnalysis` +4 fields, `HealthReport.ros_backend`, `dds_backend`
|
|
275
|
+
widening, `AdapterName` widening, new `TopicMetrics` /
|
|
276
|
+
`ParticipantEvent` schemas), `CompositeAdapter`, `peek_dds_samples`
|
|
277
|
+
user-topic story, protocol expansions, plus a quick checklist.
|
|
278
|
+
- **`docs/TROUBLESHOOTING.md`** (new). One section per polished
|
|
279
|
+
AdapterError message, plus the cross-cutting "ROS2 CLI not found on
|
|
280
|
+
PATH" / `auto` fallback to mock case. Each section quotes the
|
|
281
|
+
message and lists the diagnostics in order.
|
|
282
|
+
- **`examples/`** (new). 4 mock-mode-runnable walkthroughs covering
|
|
283
|
+
the headline value props :
|
|
284
|
+
- `01-discover-ros2-stack.md` — `health_check` + `list_topics` +
|
|
285
|
+
`get_topic_info` + `sample_messages`
|
|
286
|
+
- `02-debug-qos-mismatch.md` — `list_participants` +
|
|
287
|
+
`detect_qos_mismatches` + `peek_dds_samples` (canonical
|
|
288
|
+
Reliability mismatch story)
|
|
289
|
+
- `03-analyze-recording.md` — `analyze_bag` + `peek_bag_samples`
|
|
290
|
+
post-mortem inspection
|
|
291
|
+
- `04-monitor-topic-frequency.md` — `topic_metrics` +
|
|
292
|
+
`participant_events` with the opportunistic-fill caveat
|
|
293
|
+
Each example pairs an MCP-client prompt with the expected tool
|
|
294
|
+
calls and a short LLM-facing synthesis.
|
|
295
|
+
|
|
296
|
+
#### Added (sub-milestone 5.3 — repo polish)
|
|
297
|
+
|
|
298
|
+
- **`CONTRIBUTING.md`** (new). What contributions land easily vs hard,
|
|
299
|
+
development setup, the `make check` contract, mock-first
|
|
300
|
+
development convention, layer separation, pure-parser convention,
|
|
301
|
+
commit conventions.
|
|
302
|
+
- **`SECURITY.md`** (new). Local-trust threat model, the
|
|
303
|
+
read-only-by-architecture stance, vulnerability disclosure email,
|
|
304
|
+
response SLAs, supported version policy.
|
|
305
|
+
- **`.github/ISSUE_TEMPLATE/bug_report.yml`** (new) — structured form
|
|
306
|
+
with version, mode, OS, Python, repro, env vars, troubleshooting
|
|
307
|
+
check.
|
|
308
|
+
- **`.github/ISSUE_TEMPLATE/feature_request.yml`** (new) —
|
|
309
|
+
problem-first framing, tier disambiguation (OSS / Pro), explicit
|
|
310
|
+
read-only-by-architecture acknowledgment.
|
|
311
|
+
- **`.github/ISSUE_TEMPLATE/config.yml`** (new) — disables blank
|
|
312
|
+
issues, links to `SECURITY.md`, `docs/TROUBLESHOOTING.md`,
|
|
313
|
+
`docs/DDS_QUICKSTART.md`.
|
|
314
|
+
- **`.github/PULL_REQUEST_TEMPLATE.md`** (new) — summary, test plan,
|
|
315
|
+
backward-compatibility checklist (covers the 11-tool cap, schema
|
|
316
|
+
additive-only invariant, telemetry 6-field contract, env var docs,
|
|
317
|
+
public API removal flag).
|
|
318
|
+
|
|
319
|
+
#### Notes
|
|
320
|
+
|
|
321
|
+
- **Backward compat preserved.** Zero schema changes, zero new tools,
|
|
322
|
+
zero env-var renames. The `DDS_ONLY_ERROR_MSG` substring tokens
|
|
323
|
+
pinned by existing tests (`"DDS observability only"`,
|
|
324
|
+
`"TOPICFORGE_DDS_BACKEND"`, `"TOPICFORGE_MODE"`) are preserved
|
|
325
|
+
intact ; v0.4.0 producers and clients keep working byte-for-byte.
|
|
326
|
+
- **CHANGELOG entry deferred to release time.** This branch leaves
|
|
327
|
+
`pyproject.toml` at `0.4.0`, `__version__` at `"0.4.0"`, and the
|
|
328
|
+
`## [Unreleased]` heading populated with the polish notes above.
|
|
329
|
+
The version bump and `v0.5.0` tag are the maintainer's manual
|
|
330
|
+
steps after final review.
|
|
331
|
+
|
|
332
|
+
## [0.4.0] - 2026-05-15
|
|
9
333
|
|
|
10
334
|
### Sprint v0.4.0 — Phase 3 (bag analysis multi-format)
|
|
11
335
|
|
|
@@ -554,7 +878,10 @@ Initial MVP release of TopicForge — ROS Topic Inspector & Bag Analyzer MCP ser
|
|
|
554
878
|
- The write path (publishing, commanding robots) is intentionally out of scope for the MVP.
|
|
555
879
|
- `analyze_bag` in live mode parses `ros2 bag info` text output; deeper anomaly detection remains mock-only for now.
|
|
556
880
|
|
|
557
|
-
[Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.
|
|
881
|
+
[Unreleased]: https://github.com/yaniswav/TopicForge/compare/v0.5.1...HEAD
|
|
882
|
+
[0.5.1]: https://github.com/yaniswav/TopicForge/compare/v0.5.0...v0.5.1
|
|
883
|
+
[0.5.0]: https://github.com/yaniswav/TopicForge/compare/v0.4.0...v0.5.0
|
|
884
|
+
[0.4.0]: https://github.com/yaniswav/TopicForge/compare/v0.3.0...v0.4.0
|
|
558
885
|
[0.3.0]: https://github.com/yaniswav/TopicForge/compare/v0.2.0...v0.3.0
|
|
559
886
|
[0.2.0]: https://github.com/yaniswav/TopicForge/compare/v0.1.2...v0.2.0
|
|
560
887
|
[0.1.2]: https://github.com/yaniswav/TopicForge/compare/v0.1.1...v0.1.2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: topicforge
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
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
|
|
@@ -37,8 +37,9 @@ Classifier: Operating System :: OS Independent
|
|
|
37
37
|
Classifier: Programming Language :: Python :: 3
|
|
38
38
|
Classifier: Programming Language :: Python :: 3.11
|
|
39
39
|
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
40
41
|
Requires-Python: >=3.11
|
|
41
|
-
Requires-Dist: mcp
|
|
42
|
+
Requires-Dist: mcp<2,>=1.0.0
|
|
42
43
|
Requires-Dist: pydantic>=2.6
|
|
43
44
|
Provides-Extra: all
|
|
44
45
|
Requires-Dist: cyclonedds>=0.10; extra == 'all'
|
|
@@ -62,18 +63,21 @@ Requires-Dist: fastdds<3,>=2.6.1; extra == 'dds-fast'
|
|
|
62
63
|
Provides-Extra: dds-opendds
|
|
63
64
|
Requires-Dist: pyopendds>=0.1; extra == 'dds-opendds'
|
|
64
65
|
Provides-Extra: dev
|
|
66
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
65
67
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
68
|
+
Requires-Dist: rosbags>=0.9; extra == 'dev'
|
|
66
69
|
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
67
70
|
Description-Content-Type: text/markdown
|
|
68
71
|
|
|
69
72
|
# TopicForge
|
|
70
73
|
|
|
71
74
|
[](https://pypi.org/project/topicforge/)
|
|
75
|
+
[](https://github.com/yaniswav/TopicForge/actions/workflows/ci.yml)
|
|
72
76
|
[](https://pypi.org/project/topicforge/)
|
|
73
77
|
[](https://github.com/yaniswav/TopicForge/blob/main/LICENSE)
|
|
74
78
|
[](https://github.com/yaniswav/TopicForge#security-model)
|
|
75
79
|
|
|
76
|
-
> **The safety-first read-only MCP for ROS2 robotics —
|
|
80
|
+
> **The safety-first read-only MCP for ROS2 robotics — observability + multi-vendor OMG DDS-RTPS (v0.4.0).** TopicForge lets AI agents inspect your ROS2 graph, recorded bag files, and the raw DDS layer beneath ROS — without ever publishing back to the bus. **Eleven typed read-only tools** (5 ROS2 graph + 3 DDS + 3 observability/bag) share a single Pydantic envelope so an LLM caller reads one schema across the whole stack. v0.4.0 adds participant lifecycle tracking (`participant_events`), temporal metrics (`topic_metrics`), and post-mortem bag sample peek (`peek_bag_samples`) on top of v0.3.0's two OSS Python DDS participants — Eclipse CycloneDDS and eProsima Fast DDS — each observing **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.
|
|
77
81
|
|
|
78
82
|
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.
|
|
79
83
|
|
|
@@ -239,21 +243,24 @@ CoreDX, InterCOM) ship under the optional `topicforge-pro` package with
|
|
|
239
243
|
BYO vendor license. See [`docs/pro.md`](docs/pro.md) for the early-access
|
|
240
244
|
slot and pricing terms ; nothing is collected today.
|
|
241
245
|
|
|
242
|
-
|
|
246
|
+
Six DDS / observability tools (in addition to the five ROS2 tools above) :
|
|
243
247
|
|
|
244
|
-
| Tool | Purpose
|
|
245
|
-
| ----------------------- |
|
|
246
|
-
| `list_participants` | DDS participants discovered on a domain, with vendor and
|
|
247
|
-
| `detect_qos_mismatches` | Reader/writer QoS incompatibilities preventing communication on a topic
|
|
248
|
-
| `peek_dds_samples` | Recent samples on a raw DDS topic (distinct from `sample_messages` on ROS2 graph)|
|
|
248
|
+
| Tool | Since | Purpose |
|
|
249
|
+
| ----------------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
|
|
250
|
+
| `list_participants` | v0.2.0 | DDS participants discovered on a domain, with vendor, hostname, and (v0.4.0) lifecycle fields |
|
|
251
|
+
| `detect_qos_mismatches` | v0.2.0 | Reader/writer QoS incompatibilities preventing communication on a topic |
|
|
252
|
+
| `peek_dds_samples` | v0.2.0 | Recent samples on a raw DDS topic — v0.4.0 adds best-effort XTypes user-topic decode (distinct from `sample_messages` on ROS2 graph) |
|
|
253
|
+
| `participant_events` | v0.4.0 | Lifecycle stream — `discovered` / `lost` participant events over a configurable window |
|
|
254
|
+
| `topic_metrics` | v0.4.0 | Temporal metrics — observed frequency, sequence gaps, latency p50/p95/p99 over a sliding window |
|
|
255
|
+
| `peek_bag_samples` | v0.4.0 | Post-mortem inspection — decoded samples from a recorded `.mcap` / `.db3` / `.bag` file |
|
|
249
256
|
|
|
250
|
-
**Composite adapter (v0.4.0 Phase 1+).** When `TOPICFORGE_MODE=live` is paired with a DDS backend (`cyclone`, `fast`, …), TopicForge instantiates **both** a ROS2 CLI adapter and the chosen DDS adapter and routes per-tool category — the 5 ROS2 tools hit the CLI, the
|
|
257
|
+
**Composite adapter (v0.4.0 Phase 1+).** When `TOPICFORGE_MODE=live` is paired with a DDS backend (`cyclone`, `fast`, …), TopicForge instantiates **both** a ROS2 CLI adapter and the chosen DDS adapter and routes per-tool category — the 5 ROS2 tools hit the CLI, the DDS / observability tools hit the DDS backend. ROS2-only or DDS-only setups still work — the missing half is skipped and the present half serves what it can. The mock backend continues to expose all 11 tools against deterministic fixtures for local development.
|
|
251
258
|
|
|
252
|
-
|
|
259
|
+
**`peek_dds_samples` payload shape (v0.4.0 Phase 1.5).** Full-fidelity on the 4 builtin DCPS topics (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`). Arbitrary user topics return best-effort decoded samples with a `_decode_status` annotation : `"full"` (every IDL field decoded — currently a v0.4.0+ Cyclone XTypes path), `"partial"` (some fields decoded, others opaque), or `"raw"` (binding could not resolve the dynamic XTypes). The diagnostic key `_decode_note` carries a short explanation when the status is non-`full`. The wire shape is identical across Cyclone and Fast backends. **Caveat (v0.5.x):** on the current user-topic *raw* path `_raw_bytes_hex` is **empty** — a `"raw"` status means "topic present on the bus but not decoded", not "here are the serialized bytes to re-decode". Capturing the on-wire CDR bytes into the fallback is roadmapped ; until then use the Cyclone full/partial XTypes path for actual user-topic payloads. The 4 builtin DCPS topics are unaffected (always structured).
|
|
253
260
|
|
|
254
261
|
**`RTI Connext`** is v0.4.0+ Pro tier (BYO license — see `docs/pro.md`).
|
|
255
262
|
|
|
256
|
-
**Full 5-minute walkthrough** — backend selection, the canonical QoS-mismatch debugging scenario, troubleshooting — lives in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md). Migration
|
|
263
|
+
**Full 5-minute walkthrough** — backend selection, the canonical QoS-mismatch debugging scenario, troubleshooting — lives in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md). Migration history : [v0.2 → v0.3](docs/MIGRATION_v0.2_to_v0.3.md), [v0.3 → v0.4](docs/MIGRATION_v0.3_to_v0.4.md).
|
|
257
264
|
|
|
258
265
|
### Configure with Claude Desktop
|
|
259
266
|
|
|
@@ -308,7 +315,7 @@ make check # both, plus tests (CI bundle)
|
|
|
308
315
|
| `TOPICFORGE_LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` |
|
|
309
316
|
| `TOPICFORGE_ROS2_BIN` | `ros2` | Name (or path) of the ROS2 CLI binary |
|
|
310
317
|
| `TOPICFORGE_TELEMETRY` | `off` | Opt-in anonymous usage telemetry. See [Telemetry](#telemetry). |
|
|
311
|
-
| `TOPICFORGE_DDS_BACKEND` | `mock` | DDS module backend: `mock`, `cyclone`, `fast`, `rti`, or `auto`.
|
|
318
|
+
| `TOPICFORGE_DDS_BACKEND` | `mock` | DDS module backend: `mock`, `cyclone`, `fast`, `rti`, `opensplice`, `coredx`, `intercom`, `opendds`, `dust`, or `auto`. The v0.4.0 Phase 1.5 auto-detect chain resolves to: `rti > opensplice > coredx > intercom` (Pro tier, if installed) `> opendds > fast > cyclone > dust > mock`. See [Multi-vendor DDS support](#multi-vendor-dds-support-v030). |
|
|
312
319
|
| `TOPICFORGE_DDS_DOMAIN_ID` | `0` | DDS domain id observed (0..232) when a DDS backend is active. |
|
|
313
320
|
|
|
314
321
|
See [`.env.example`](.env.example).
|
|
@@ -336,7 +343,7 @@ When telemetry is on, each MCP tool call emits a single event with **only** thes
|
|
|
336
343
|
|
|
337
344
|
| Field | Example | Notes |
|
|
338
345
|
| ----------------- | ---------------- | ---------------------------------------------------------------------- |
|
|
339
|
-
| `tool_name` | `"list_topics"` | One of the
|
|
346
|
+
| `tool_name` | `"list_topics"` | One of the eleven MCP tools — never argument values. |
|
|
340
347
|
| `latency_ms` | `12.34` | Wall-clock duration of the handler, rounded to 2 decimals. |
|
|
341
348
|
| `mode` | `"mock"` | Effective runtime mode: `mock` or `live`. |
|
|
342
349
|
| `version` | `"0.1.2"` | TopicForge server version. |
|
|
@@ -382,16 +389,14 @@ See [`docs/product-plan.md`](docs/product-plan.md) for the full product trajecto
|
|
|
382
389
|
|
|
383
390
|
Near-term additions on the bench:
|
|
384
391
|
|
|
385
|
-
- `rclpy`-backed live adapter for faster & richer sampling
|
|
386
|
-
-
|
|
387
|
-
-
|
|
388
|
-
-
|
|
389
|
-
-
|
|
390
|
-
- URDF inspector / validator MCP tools
|
|
391
|
-
- Bag anomaly detection (clock jumps, gaps, dropped frames, TF tree health)
|
|
392
|
+
- `rclpy`-backed live adapter for faster & richer sampling (per-message rmw receive timestamps, windowed sampling) — gated on external user demand
|
|
393
|
+
- Extended QoS coverage (Liveliness, Ownership, Partition, TimeBasedFilter, LatencyBudget) — v0.5.x patch
|
|
394
|
+
- Real `RtiConnextAdapter` in the Pro tier (BYO RTI Connext license, gated by `TOPICFORGE_LICENSE_KEY`) — the v0.4.0 Phase 1.5 framework is in place ; production binding pending Pro tier launch
|
|
395
|
+
- URDF inspector / validator MCP tools (Pro tier)
|
|
396
|
+
- Bag anomaly detection (clock jumps, gaps, dropped frames, TF tree health) — Pro tier
|
|
392
397
|
- Dataset export helpers (rosbag → COCO / HF Datasets)
|
|
393
398
|
- Synthetic data pipeline controller (Blender, Gazebo, Isaac Sim)
|
|
394
|
-
- Hosted MCP endpoint with auth
|
|
399
|
+
- Hosted MCP endpoint with auth (Phase 3 — depends on Pro tier traction)
|
|
395
400
|
|
|
396
401
|
## Project layout
|
|
397
402
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# TopicForge
|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/topicforge/)
|
|
4
|
+
[](https://github.com/yaniswav/TopicForge/actions/workflows/ci.yml)
|
|
4
5
|
[](https://pypi.org/project/topicforge/)
|
|
5
6
|
[](https://github.com/yaniswav/TopicForge/blob/main/LICENSE)
|
|
6
7
|
[](https://github.com/yaniswav/TopicForge#security-model)
|
|
7
8
|
|
|
8
|
-
> **The safety-first read-only MCP for ROS2 robotics —
|
|
9
|
+
> **The safety-first read-only MCP for ROS2 robotics — observability + multi-vendor OMG DDS-RTPS (v0.4.0).** TopicForge lets AI agents inspect your ROS2 graph, recorded bag files, and the raw DDS layer beneath ROS — without ever publishing back to the bus. **Eleven typed read-only tools** (5 ROS2 graph + 3 DDS + 3 observability/bag) share a single Pydantic envelope so an LLM caller reads one schema across the whole stack. v0.4.0 adds participant lifecycle tracking (`participant_events`), temporal metrics (`topic_metrics`), and post-mortem bag sample peek (`peek_bag_samples`) on top of v0.3.0's two OSS Python DDS participants — Eclipse CycloneDDS and eProsima Fast DDS — each observing **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
|
|
|
10
11
|
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.
|
|
11
12
|
|
|
@@ -171,21 +172,24 @@ CoreDX, InterCOM) ship under the optional `topicforge-pro` package with
|
|
|
171
172
|
BYO vendor license. See [`docs/pro.md`](docs/pro.md) for the early-access
|
|
172
173
|
slot and pricing terms ; nothing is collected today.
|
|
173
174
|
|
|
174
|
-
|
|
175
|
+
Six DDS / observability tools (in addition to the five ROS2 tools above) :
|
|
175
176
|
|
|
176
|
-
| Tool | Purpose
|
|
177
|
-
| ----------------------- |
|
|
178
|
-
| `list_participants` | DDS participants discovered on a domain, with vendor and
|
|
179
|
-
| `detect_qos_mismatches` | Reader/writer QoS incompatibilities preventing communication on a topic
|
|
180
|
-
| `peek_dds_samples` | Recent samples on a raw DDS topic (distinct from `sample_messages` on ROS2 graph)|
|
|
177
|
+
| Tool | Since | Purpose |
|
|
178
|
+
| ----------------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
|
|
179
|
+
| `list_participants` | v0.2.0 | DDS participants discovered on a domain, with vendor, hostname, and (v0.4.0) lifecycle fields |
|
|
180
|
+
| `detect_qos_mismatches` | v0.2.0 | Reader/writer QoS incompatibilities preventing communication on a topic |
|
|
181
|
+
| `peek_dds_samples` | v0.2.0 | Recent samples on a raw DDS topic — v0.4.0 adds best-effort XTypes user-topic decode (distinct from `sample_messages` on ROS2 graph) |
|
|
182
|
+
| `participant_events` | v0.4.0 | Lifecycle stream — `discovered` / `lost` participant events over a configurable window |
|
|
183
|
+
| `topic_metrics` | v0.4.0 | Temporal metrics — observed frequency, sequence gaps, latency p50/p95/p99 over a sliding window |
|
|
184
|
+
| `peek_bag_samples` | v0.4.0 | Post-mortem inspection — decoded samples from a recorded `.mcap` / `.db3` / `.bag` file |
|
|
181
185
|
|
|
182
|
-
**Composite adapter (v0.4.0 Phase 1+).** When `TOPICFORGE_MODE=live` is paired with a DDS backend (`cyclone`, `fast`, …), TopicForge instantiates **both** a ROS2 CLI adapter and the chosen DDS adapter and routes per-tool category — the 5 ROS2 tools hit the CLI, the
|
|
186
|
+
**Composite adapter (v0.4.0 Phase 1+).** When `TOPICFORGE_MODE=live` is paired with a DDS backend (`cyclone`, `fast`, …), TopicForge instantiates **both** a ROS2 CLI adapter and the chosen DDS adapter and routes per-tool category — the 5 ROS2 tools hit the CLI, the DDS / observability tools hit the DDS backend. ROS2-only or DDS-only setups still work — the missing half is skipped and the present half serves what it can. The mock backend continues to expose all 11 tools against deterministic fixtures for local development.
|
|
183
187
|
|
|
184
|
-
|
|
188
|
+
**`peek_dds_samples` payload shape (v0.4.0 Phase 1.5).** Full-fidelity on the 4 builtin DCPS topics (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`). Arbitrary user topics return best-effort decoded samples with a `_decode_status` annotation : `"full"` (every IDL field decoded — currently a v0.4.0+ Cyclone XTypes path), `"partial"` (some fields decoded, others opaque), or `"raw"` (binding could not resolve the dynamic XTypes). The diagnostic key `_decode_note` carries a short explanation when the status is non-`full`. The wire shape is identical across Cyclone and Fast backends. **Caveat (v0.5.x):** on the current user-topic *raw* path `_raw_bytes_hex` is **empty** — a `"raw"` status means "topic present on the bus but not decoded", not "here are the serialized bytes to re-decode". Capturing the on-wire CDR bytes into the fallback is roadmapped ; until then use the Cyclone full/partial XTypes path for actual user-topic payloads. The 4 builtin DCPS topics are unaffected (always structured).
|
|
185
189
|
|
|
186
190
|
**`RTI Connext`** is v0.4.0+ Pro tier (BYO license — see `docs/pro.md`).
|
|
187
191
|
|
|
188
|
-
**Full 5-minute walkthrough** — backend selection, the canonical QoS-mismatch debugging scenario, troubleshooting — lives in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md). Migration
|
|
192
|
+
**Full 5-minute walkthrough** — backend selection, the canonical QoS-mismatch debugging scenario, troubleshooting — lives in [`docs/DDS_QUICKSTART.md`](docs/DDS_QUICKSTART.md). Migration history : [v0.2 → v0.3](docs/MIGRATION_v0.2_to_v0.3.md), [v0.3 → v0.4](docs/MIGRATION_v0.3_to_v0.4.md).
|
|
189
193
|
|
|
190
194
|
### Configure with Claude Desktop
|
|
191
195
|
|
|
@@ -240,7 +244,7 @@ make check # both, plus tests (CI bundle)
|
|
|
240
244
|
| `TOPICFORGE_LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` |
|
|
241
245
|
| `TOPICFORGE_ROS2_BIN` | `ros2` | Name (or path) of the ROS2 CLI binary |
|
|
242
246
|
| `TOPICFORGE_TELEMETRY` | `off` | Opt-in anonymous usage telemetry. See [Telemetry](#telemetry). |
|
|
243
|
-
| `TOPICFORGE_DDS_BACKEND` | `mock` | DDS module backend: `mock`, `cyclone`, `fast`, `rti`, or `auto`.
|
|
247
|
+
| `TOPICFORGE_DDS_BACKEND` | `mock` | DDS module backend: `mock`, `cyclone`, `fast`, `rti`, `opensplice`, `coredx`, `intercom`, `opendds`, `dust`, or `auto`. The v0.4.0 Phase 1.5 auto-detect chain resolves to: `rti > opensplice > coredx > intercom` (Pro tier, if installed) `> opendds > fast > cyclone > dust > mock`. See [Multi-vendor DDS support](#multi-vendor-dds-support-v030). |
|
|
244
248
|
| `TOPICFORGE_DDS_DOMAIN_ID` | `0` | DDS domain id observed (0..232) when a DDS backend is active. |
|
|
245
249
|
|
|
246
250
|
See [`.env.example`](.env.example).
|
|
@@ -268,7 +272,7 @@ When telemetry is on, each MCP tool call emits a single event with **only** thes
|
|
|
268
272
|
|
|
269
273
|
| Field | Example | Notes |
|
|
270
274
|
| ----------------- | ---------------- | ---------------------------------------------------------------------- |
|
|
271
|
-
| `tool_name` | `"list_topics"` | One of the
|
|
275
|
+
| `tool_name` | `"list_topics"` | One of the eleven MCP tools — never argument values. |
|
|
272
276
|
| `latency_ms` | `12.34` | Wall-clock duration of the handler, rounded to 2 decimals. |
|
|
273
277
|
| `mode` | `"mock"` | Effective runtime mode: `mock` or `live`. |
|
|
274
278
|
| `version` | `"0.1.2"` | TopicForge server version. |
|
|
@@ -314,16 +318,14 @@ See [`docs/product-plan.md`](docs/product-plan.md) for the full product trajecto
|
|
|
314
318
|
|
|
315
319
|
Near-term additions on the bench:
|
|
316
320
|
|
|
317
|
-
- `rclpy`-backed live adapter for faster & richer sampling
|
|
318
|
-
-
|
|
319
|
-
-
|
|
320
|
-
-
|
|
321
|
-
-
|
|
322
|
-
- URDF inspector / validator MCP tools
|
|
323
|
-
- Bag anomaly detection (clock jumps, gaps, dropped frames, TF tree health)
|
|
321
|
+
- `rclpy`-backed live adapter for faster & richer sampling (per-message rmw receive timestamps, windowed sampling) — gated on external user demand
|
|
322
|
+
- Extended QoS coverage (Liveliness, Ownership, Partition, TimeBasedFilter, LatencyBudget) — v0.5.x patch
|
|
323
|
+
- Real `RtiConnextAdapter` in the Pro tier (BYO RTI Connext license, gated by `TOPICFORGE_LICENSE_KEY`) — the v0.4.0 Phase 1.5 framework is in place ; production binding pending Pro tier launch
|
|
324
|
+
- URDF inspector / validator MCP tools (Pro tier)
|
|
325
|
+
- Bag anomaly detection (clock jumps, gaps, dropped frames, TF tree health) — Pro tier
|
|
324
326
|
- Dataset export helpers (rosbag → COCO / HF Datasets)
|
|
325
327
|
- Synthetic data pipeline controller (Blender, Gazebo, Isaac Sim)
|
|
326
|
-
- Hosted MCP endpoint with auth
|
|
328
|
+
- Hosted MCP endpoint with auth (Phase 3 — depends on Pro tier traction)
|
|
327
329
|
|
|
328
330
|
## Project layout
|
|
329
331
|
|