# TopicForge (pypi · topicforge)

Read-only MCP server for ROS2 and DDS: topics, participants, QoS mismatches, bags.

- Trust score: 74/100 (medium)
- Change this week: +19
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-20

## Components

- pypi · `topicforge`: 74/100 (this document), [markdown](https://verifymcp.io/servers/yaniswav-topicforge/topicforge.md), [page](https://verifymcp.io/servers/yaniswav-topicforge/topicforge)

## Channel facts

- Registry: `pypi`
- Package: `topicforge`
- Version: `0.5.2`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-20.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs hatchling.build at install time, a recognised native-build step with no shell scripting around it.
  - 0 of 29 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 35/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - License check failed: the license (MIT License) isn't a recognized OSI-approved license.
  - Actively maintained (last published 28 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 65/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 3659 tokens (~332/item across 11 items; 11 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 50/100
  - Stability observed for 15 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - We read all 11 captured tool definition(s), and no name or description among them implies an irreversible operation.
  - An AI judge read all 11 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### How do I install the TopicForge MCP server?

TopicForge runs locally as a PyPI package, launched with uvx topicforge. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add yaniswav-topicforge -- uvx topicforge
```

### Cursor

```json
{
  "mcpServers": {
    "yaniswav-topicforge": {
      "command": "uvx",
      "args": [
        "topicforge"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "yaniswav-topicforge": {
      "command": "uvx",
      "args": [
        "topicforge"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add yaniswav-topicforge -- uvx topicforge
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "yaniswav-topicforge": {
      "type": "local",
      "command": [
        "uvx",
        "topicforge"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add yaniswav-topicforge --command uvx --arg topicforge
```

### Hermes

```yaml
mcp_servers:
  yaniswav-topicforge:
    command: "uvx"
    args: ["topicforge"]
```

### Netclaw

```json
{
  "McpServers": {
    "yaniswav-topicforge": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "topicforge"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add yaniswav-topicforge -t stdio -c uvx -a topicforge
```

### Other

```json
{
  "mcpServers": {
    "yaniswav-topicforge": {
      "command": "uvx",
      "args": [
        "topicforge"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-20 (score 74, +16)

- [security improvement] Malware scan: unverified → pass

### 2026-09-18 (score 58, +1)

No change was recorded against any check on this day. Stability & Change Management went from 40 to 43. That category is still filling its 30-day observation window: 12 days of observed history at the previous scan, 13 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-16 (score 57, +1)

No change was recorded against any check on this day. Stability & Change Management went from 33 to 37. That category is still filling its 30-day observation window: 10 days of observed history at the previous scan, 11 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-14 (score 56, +1)

No change was recorded against any check on this day. Stability & Change Management went from 27 to 30. That category is still filling its 30-day observation window: 8 days of observed history at the previous scan, 9 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-11 (score 55, +3)

- [functional improvement] Stability: unverified → 0.20

### 2026-09-05 (score 52)

First indexed and scored.

## MCP tools (11)

### `health_check` (~116 tokens)

Report TopicForge environment state — effective runtime mode (`"live"` or `"mock"`), whether the `ros2` CLI is on PATH, `ROS_DISTRO`, the server version, the server-side sample cap, the active DDS backend (`mock`/`cyclone`/`rti`/`none`), and the observed DDS domain id when applicable. Returns a `HealthReport`. **Always succeeds** — call this first when something looks wrong, since every other tool may raise. Read-only ; no side effects.

Output parameters:

- `dds_backend` (string): Active DDS module backend. `none` when the DDS module is not active (default for ROS2-only installs). `mock` for synthetic fixtures. **OSS tier** — `cyclone` requires `pip install topicforge[dds-cycl…
- `dds_domain_id`: DDS domain id observed when the DDS module is active.
- `max_sample_count` (integer): Server-side cap on the number of samples returned per `sample_messages` call. Requests above this limit are silently clamped; the value is exposed here so a client can size its requests proactively.…
- `middleware_available` (boolean): Whether the configured DDS backend is importable. False when the DDS module is inactive (`dds_backend == 'none'`) or when the backend's Python bindings are not installed.
- `mode` (string): Effective runtime mode: `mock` or `live`.
- `requested_mode` (string): Mode requested via configuration (may be `auto`).
- `ros2_available` (boolean): Whether a `ros2` CLI is on PATH.
- `ros2_distro`: Value of `ROS_DISTRO` if set in the environment. **Env disclosure, by design** — under the local-trust threat model (see README 'Security model'), the MCP client is a trusted agent on a machine the u…
- `ros_backend` (string): Active ROS2 backend. `ros2_cli` when the `ros2` CLI is on PATH and live mode resolves to a Ros2CliAdapter (alone or as the ROS half of a composite). `mock` when MockAdapter serves the ROS surface. `n…
- `server_version` (string): TopicForge server version (matches the PyPI release of the `topicforge` package).

### `list_topics` (~114 tokens)

List every ROS2 topic on the current graph (or the deterministic mock graph in mock mode). Returns `list[TopicInfo]` — each entry carries `name`, `message_type`, `publisher_count`, `subscriber_count`, `qos_reliability`, and `mode_effective` (`"live"` or `"mock"`) so a downstream LLM can distinguish a real graph from demo fixtures. **Empty list** when the graph has no topics or when live discovery times out. Read-only ; no side effects.

Output parameters:

- `result` (array)

### `get_topic_info` (~189 tokens)

Return detailed info for a single ROS2 topic. `topic` must be a fully qualified topic name, e.g. `/cmd_vel`. Returns a `TopicInfo` carrying `mode_effective` (`"live"` or `"mock"`) so callers can distinguish a real-graph hit from a mock fixture. **Raises an MCP error** (isError=true) if the topic name is malformed or the topic is unknown to the active graph. Read-only ; no side effects.

Input parameters:

- `topic` (string, required): Fully qualified ROS2 topic name starting with `/`, e.g. `/cmd_vel` or `/camera/image_raw`. Each `/`-separated segment must start with a letter or underscore and contain only letters, digits, and unde…

Output parameters:

- `message_type` (string): ROS2 message type, e.g. `geometry_msgs/msg/Twist`.
- `mode_effective` (string): Runtime mode the adapter actually served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Always carried by the response so a downstream LLM can distinguish a re…
- `name` (string): Fully qualified topic name, e.g. `/cmd_vel`.
- `publisher_count` (integer): Publishers known to the graph.
- `qos_profile`: Effective DDS QoS profile for this topic when resolvable. `None` from the ROS2 CLI adapter or when the DDS module is inactive. The DDS module populates this on a best-effort basis (picks one represen…
- `qos_reliability`: QoS reliability policy if known: `reliable` or `best_effort`.
- `reader_count`: DDS reader-endpoint count when the active backend can resolve endpoint-level info (Cyclone / RTI). `None` from the ROS2 CLI adapter or when the DDS module is inactive.
- `subscriber_count` (integer): Subscribers known to the graph.
- `writer_count`: DDS writer-endpoint count when the active backend can resolve endpoint-level info. `None` from the ROS2 CLI adapter or when the DDS module is inactive.

### `sample_messages` (~485 tokens)

Peek up to `count` recent ROS2 messages from `topic`, sampled from the runtime graph. `topic` must be a fully qualified name (see the `topic` parameter description). `count` defaults to 5 and is silently clamped to 50 — request more and you receive at most 50 without warning. Returns a `SampleResult` envelope `{topic, count, samples, mode_effective}` where `count` is the actual number of samples returned (may be 0) and `mode_effective` is `"live"` or `"mock"`. **Live mode** shells out to `ros2 topic echo --csv --once` with a short timeout, so the result is empty when no publisher is currently active. `samples[i].timestamp_ns` is the message's `header.stamp` (publish time) when the message is `Header`-stamped, and 0 for headerless types (e.g. `std_msgs/String`). The live parser exposes fields as positional CSV columns under `samples[i].payload` keys `col_0`, `col_1`, ..., with the verbatim CSV row under the reserved `_raw_text` key. **Mock mode** returns deterministic samples with monotonically increasing timestamps for the fictional demo robot (and no `_raw_text` key, since the payload is already structured). Read-only ; never publishes to the bus. **Distinct from `peek_dds_samples`** — that tool reads the raw DDS layer ; this one reads the ROS2 graph.

Input parameters:

- `count` (integer): Maximum number of recent messages to return. Defaults to 5; silently clamped to 50 (the hard cap that keeps tool output bounded — read it from `health_check.max_sample_count`). Negative values raise…
- `topic` (string, required): Fully qualified ROS2 topic name starting with `/`, e.g. `/cmd_vel` or `/camera/image_raw`. Each `/`-separated segment must start with a letter or underscore and contain only letters, digits, and unde…

Output parameters:

- `count` (integer): Number of samples actually returned. May be 0 (no publisher active in live mode, or empty mock fixture), less than the requested count (topic yielded fewer messages within the timeout), or capped by…
- `mode_effective` (string): Runtime mode the adapter actually served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Always carried by the response so a downstream LLM can distinguish a re…
- `samples` (array): The sampled messages, ordered as received from the backend.
- `topic` (string): Topic the samples were taken from, echoed from the request.

### `analyze_bag` (~244 tokens)

Summarize a ROS2 bag at `path`. Returns a `BagAnalysis` carrying storage format, total duration, message count, per-topic stats, detected anomalies, and `mode_effective` (`"live"` or `"mock"`) so callers can tell a real bag analysis from a mock fixture. **Live mode** shells out to `ros2 bag info` and accepts `.mcap`, `.db3`, and `.bag` files plus `rosbag2_*` directories ; **mock mode** returns rich fixture data regardless of path suffix (except blatantly non-bag extensions). **Raises an MCP error** if the path is malformed, missing in live mode, or unparseable. Deep anomaly detection is mock-only at MVP. Read-only ; no side effects.

Input parameters:

- `path` (string, required): Path to a ROS2 bag: a file ending in `.mcap`, `.db3`, or `.bag`, or a `rosbag2_*` directory. Leading/trailing whitespace is stripped. Null bytes and otherwise malformed filesystem paths are rejected.…

Output parameters:

- `anomalies` (array): Human-readable notes about gaps, clock jumps, or other oddities. MVP populates this in mock mode; live anomaly detection is roadmap.
- `bag_format`: Concrete bag container format detected by the reader — `mcap` (Foxglove MCAP), `db3` (ROS2 rosbag2 SQLite), `bag` (ROS1 legacy chunked), or `unknown` when the reader could not classify. `None` for th…
- `duration_seconds` (number): Total bag duration, in seconds (wall clock between first and last message).
- `message_count` (integer): Total number of messages across all recorded topics.
- `mode_effective` (string): Runtime mode the adapter actually served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Always carried by the response so a downstream LLM can distinguish a re…
- `participants_recorded` (array): DDS participants recorded in the bag when the container format embeds participant metadata. MCAP can carry it via channel metadata records ; ROS2 `.db3` and ROS1 `.bag` generally do not. Empty list w…
- `path` (string): Path to the analyzed bag, as supplied by the caller. May point to a file (`.mcap`, `.db3`, `.bag`) or to a `rosbag2_*` directory.
- `recording_duration_ns`: Recording duration in nanoseconds when readable from the bag's index. `None` when the v0.3.0 text-parsed path runs ; `duration_seconds` (float) is the always-populated fallback that downstream LLM co…
- `samples_decoded_count` (integer): Total decoded sample count across all topics produced by the bag reader. `0` when the reader only parsed metadata (the v0.3.0 text-parsed path) or when `rosbags` is not installed on the host. Use `pe…
- `storage_format`: `mcap`, `sqlite3`, or other storage identifier when known.
- `topics` (array): Per-topic statistics for every topic present in the bag.

### `list_participants` (~267 tokens)

List DDS participants observed on a domain. Returns `list[ParticipantInfo]` — each entry carries `guid`, `vendor` (`cyclone`/`rti`/`mock`/`unknown`), optional `hostname`, `domain_id`, and `mode_effective` (`"live"`/`"mock"`). **Distinct from ROS2 graph nodes** — operates at the raw DDS layer beneath ROS, useful for non-ROS DDS stacks or for diagnosing why a participant isn't seen by the ROS graph. **Read-only by architecture** — the underlying `MiddlewareAdapter` protocol does not expose a write method, so this tool cannot publish, modify QoS, or alter the bus. **Raises an MCP error** when no DDS module is active (install `pip install topicforge[dds]` and set `TOPICFORGE_DDS_BACKEND=cyclone`). With the v0.2.0 `CycloneDdsAdapter` stub, also raises with a v0.2.x roadmap pointer ; mock backend returns deterministic fixtures.

Input parameters:

- `domain_id` (integer): DDS domain id to observe (0..232). Defaults to 0 — the same default used by `cyclonedds` and the implicit default of most ROS2 setups.

Output parameters:

- `result` (array)

### `detect_qos_mismatches` (~251 tokens)

Detect DDS QoS incompatibilities between reader and writer endpoints on the bus. Returns `list[MismatchReport]` — one entry per incompatible (reader, writer) pair, listing the policies that block or risk degrading communication (Reliability, Durability, History, Deadline at MVP). Each report carries `severity` (`incompatible` strictly blocks communication per the DDS spec ; `risky` may degrade but is not strictly blocked) and `mode_effective` (`"live"`/`"mock"`). Pass `topic` to scope to a single topic ; omit for an exhaustive scan. **Use this when** an LLM is debugging why a subscriber doesn't receive. **Read-only by architecture** — the analyzer compares observed QoS profiles ; no method on this tool can rewrite QoS or alter the bus. **Raises an MCP error** when no DDS module is active or, in v0.2.0, when the `CycloneDdsAdapter` stub is active ; mock backend returns deterministic fixtures.

Input parameters:

- `topic`: Optional topic name to scope the scan to (`/foo/bar` shape). `None` (default) returns all mismatches across all topics.

Output parameters:

- `result` (array)

### `peek_dds_samples` (~548 tokens)

Peek up to `count` recent samples on a raw DDS topic. **Distinct from `sample_messages`** — `sample_messages` operates on the ROS2 graph via `ros2 topic echo` ; this tool reads directly from the DDS layer (Cyclone / Fast / RTI / mock). Use this for non-ROS DDS topics or when the ROS2 CLI is not available. Returns a `SampleResult` envelope `{topic, count, samples, mode_effective}` — identical shape to `sample_messages`. `count` defaults to 5 and is silently clamped to 50. **Topic categories** (v0.4.0 Phase 1): (a) The 4 builtin DCPS topics (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`) always return structured discovery payloads. (b) User-defined topics return best-effort decoded payloads — each sample's payload may carry `_decode_status` (`full`/`partial`/`raw`), `_decode_note` (short diagnostic when not `full`), and `_raw_bytes_hex` (serialized-bytes preview). **Caveat**: on the current user-topic `raw` path this preview is empty — a `raw` status means 'topic present on the bus but not decoded', not 'here are the bytes to re-decode' (capturing the on-wire CDR bytes is roadmapped). Cyclone uses `cyclonedds.dynamic` for full/partial decode ; Fast DDS 2.6.x lands on the raw path more often because its dynamic XTypes binding is partial. **Read-only by architecture** — the `MiddlewareAdapter` protocol does not expose a write method. **Raises an MCP error** when no DDS module is active OR when the topic is not announced on the bus.

Input parameters:

- `count` (integer): Maximum number of recent messages to return. Defaults to 5; silently clamped to 50 (the hard cap that keeps tool output bounded — read it from `health_check.max_sample_count`). Negative values raise…
- `topic` (string, required): Fully qualified ROS2 topic name starting with `/`, e.g. `/cmd_vel` or `/camera/image_raw`. Each `/`-separated segment must start with a letter or underscore and contain only letters, digits, and unde…

Output parameters:

- `count` (integer): Number of samples actually returned. May be 0 (no publisher active in live mode, or empty mock fixture), less than the requested count (topic yielded fewer messages within the timeout), or capped by…
- `mode_effective` (string): Runtime mode the adapter actually served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Always carried by the response so a downstream LLM can distinguish a re…
- `samples` (array): The sampled messages, ordered as received from the backend.
- `topic` (string): Topic the samples were taken from, echoed from the request.

### `participant_events` (~407 tokens)

Return DDS participant lifecycle events (`discovered` / `lost`) captured over a recent window. Use this when an LLM needs to answer *'who was on the bus 5 minutes ago and left?'* or *"when did this participant first appear?"*. Returns `list[ParticipantEvent]` — each entry carries `guid`, `event_type`, `vendor`, `timestamp_ns` (wall-clock ns since epoch), optional `hostname`, `domain_id`, and `mode_effective` (`"live"`/`"mock"`). Sorted newest-first. Hard cap at 200 events (silent truncation, mirrors `sample_messages`'s 50 cap — reduce `lookback_seconds` if you hit it). **Read-only by architecture** — the underlying `MiddlewareAdapter` protocol does not expose a write method. **Backend caveats**: Fast DDS captures arrivals AND removals via listener callbacks ; Cyclone tracks lifecycle only across `list_participants` calls (a participant that joined and left between two polls is invisible) ; mock returns a deterministic fixture timeline. **Raises an MCP error** when no DDS module is active (install `pip install topicforge[dds]` and set `TOPICFORGE_DDS_BACKEND=cyclone|fast`). Added in v0.4.0 Phase 1 — the 9th MCP tool ; v0.3.0 clients are unaffected until they call it.

Input parameters:

- `domain_id` (integer): DDS domain id to filter events on (0..232). Defaults to 0 — the same default used by `cyclonedds` and most ROS2 setups.
- `lookback_seconds` (integer): Window (in seconds) over which to return events. Defaults to 300 (5 minutes). Range: 1..86400 (1 second to 24 hours). Larger windows may hit the 200-event cap — narrow the window or filter on `domain…

Output parameters:

- `result` (array)

### `topic_metrics` (~483 tokens)

Return temporal metrics (frequency, sequence gaps, latency percentiles) for a DDS topic over a recent time window. Use this when an LLM needs to diagnose *'is this topic actually publishing at the rate its QoS Deadline declares?'*, *'are there missing sequence numbers?'*, or *'what is the p99 latency on this topic?'*. Returns a `TopicMetrics` payload carrying `samples_observed`, `frequency_hz_observed`, `frequency_hz_declared` (from QoS Deadline when known), `sequence_gaps_count`, `latency_ns_p50/p95/p99`, and boolean availability flags. **Opportunistic fill caveat**: the metrics buffer accumulates samples only as `peek_dds_samples` flows them through the adapter — neither cyclonedds nor fastdds 2.6.x Python bindings expose at-sample-receive callbacks, so a topic that hasn't been peeked recently returns `samples_observed=0`. To get useful metrics, call `peek_dds_samples` on the topic first, then this tool. **Read-only by architecture** — no method on this tool writes to the bus. **Raises an MCP error** when no DDS module is active or `window_seconds` is out of range (1..3600). Added in v0.4.0 Phase 2 ; the 10th MCP tool — the 8-tool ceiling from mcp-02-spec.md §2 was first broken at Phase 1 (`participant_events`), this is the second explicit acknowledgement.

Input parameters:

- `domain_id` (integer): DDS domain id to scope the metrics to (0..232). Defaults to 0 — the same default as the rest of the DDS tools.
- `topic` (string, required): Fully qualified ROS2 topic name starting with `/`, e.g. `/cmd_vel` or `/camera/image_raw`. Each `/`-separated segment must start with a letter or underscore and contain only letters, digits, and unde…
- `window_seconds` (integer): Window in seconds over which to compute metrics (1..3600). Defaults to 60 seconds. Smaller windows reflect more recent state ; larger windows smooth transient anomalies.

Output parameters:

- `frequency_hz_declared`: Declared frequency extracted from the topic's QoS Deadline policy when the adapter resolved it (Deadline period → 1 / period_seconds). `None` when the QoS profile does not include Deadline or the ada…
- `frequency_hz_observed`: `samples_observed / window_seconds_actual`. `None` when fewer than 2 samples were observed (a single sample does not define a frequency).
- `latency_available` (boolean): True when at least one sample in the window carried both a publish timestamp and a receive timestamp. The percentile fields are `None` when this is False.
- `latency_ns_p50`: Median publish-to-receive latency in nanoseconds, computed only when the sample type exposes a publish timestamp (typically via `header.stamp` on `Header`-stamped messages). `None` when `latency_avai…
- `latency_ns_p95`: 95th-percentile publish-to-receive latency (ns).
- `latency_ns_p99`: 99th-percentile publish-to-receive latency (ns).
- `mode_effective` (string): Runtime mode the adapter actually served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Always carried by the response so a downstream LLM can distinguish a re…
- `samples_observed` (integer): Number of samples in the buffer matching `topic` within the window. `0` means TopicForge has not seen any sample on this topic recently — it does NOT mean the topic has no publisher, only that no `pe…
- `sequence_gaps_count` (integer): Number of missing sequence numbers detected in the buffered samples. `0` either means no gaps observed OR the sample type did not expose a sequence number (check `sequence_numbers_available` to disam…
- `sequence_numbers_available` (boolean): True when the adapter successfully extracted sequence numbers from at least one sample. Sequence number support depends on the message type — `Header`-stamped messages with a `seq` field expose it ;…
- `topic` (string): Topic the metrics were computed for.
- `window_seconds` (integer): Requested window in seconds (1..3600). Echoed back from the tool call so the LLM can correlate the request.
- `window_seconds_actual` (number): Actual elapsed seconds within the window. May be smaller than `window_seconds` when the adapter buffered samples for less time than the requested window (e.g., the server just started). `0.0` when `s…

### `peek_bag_samples` (~555 tokens)

Peek up to `count` recent samples from a recorded bag file. **Distinct from `peek_dds_samples`** (live bus introspection) and `sample_messages` (ROS2 graph live peek) — this tool operates on **offline bag content** for post-mortem analysis. Supported formats: MCAP (`.mcap`), ROS2 rosbag2 SQLite (`.db3`), ROS1 legacy chunked binary (`.bag`). The reader auto-detects format from the file extension. Returns a `SampleResult` envelope identical in shape to `peek_dds_samples` — each sample's `payload` carries the same `_decode_status` annotation (`full` / `partial` / `raw`) so an LLM consumer reads one shape across live and recorded sources. `count` defaults to 5 and is silently clamped to 50. **Requires the `rosbags` library** (`pip install topicforge[bags]`) — without it, raises an `AdapterError` with the install command. The mock backend returns deterministic fixture samples on canned bag paths. **Read-only by architecture** — no method writes to the bag file. **Raises an MCP error** when the bag path does not exist, the topic is not present in the bag, or rosbags is not installed. Added in v0.4.0 Phase 3 — the 11th MCP tool ; 8-tool ceiling break #3, acknowledged in CHANGELOG.

Input parameters:

- `count` (integer): Maximum number of recent messages to return. Defaults to 5; silently clamped to 50 (the hard cap that keeps tool output bounded — read it from `health_check.max_sample_count`). Negative values raise…
- `path` (string, required): Path to a ROS2 bag: a file ending in `.mcap`, `.db3`, or `.bag`, or a `rosbag2_*` directory. Leading/trailing whitespace is stripped. Null bytes and otherwise malformed filesystem paths are rejected.…
- `topic` (string, required): Fully qualified ROS2 topic name starting with `/`, e.g. `/cmd_vel` or `/camera/image_raw`. Each `/`-separated segment must start with a letter or underscore and contain only letters, digits, and unde…

Output parameters:

- `count` (integer): Number of samples actually returned. May be 0 (no publisher active in live mode, or empty mock fixture), less than the requested count (topic yielded fewer messages within the timeout), or capped by…
- `mode_effective` (string): Runtime mode the adapter actually served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Always carried by the response so a downstream LLM can distinguish a re…
- `samples` (array): The sampled messages, ordered as received from the backend.
- `topic` (string): Topic the samples were taken from, echoed from the request.

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/yaniswav-topicforge/topicforge#diagnostics

## Score history

- 2026-09-20: 74
- 2026-09-19: 58
- 2026-09-18: 58
- 2026-09-17: 57
- 2026-09-16: 57
- 2026-09-15: 56
- 2026-09-14: 56
- 2026-09-13: 55
- 2026-09-12: 55
- 2026-09-11: 55
- 2026-09-10: 52
- 2026-09-09: 52
- 2026-09-08: 52
- 2026-09-07: 52
- 2026-09-06: 52
- 2026-09-05: 52

## Common questions

### What is the TopicForge MCP server?

TopicForge is an MCP server listed in the public MCP registry as io.github.yaniswav/topicforge. Read-only MCP server for ROS2 and DDS: topics, participants, QoS mismatches, bags. This page covers its PyPI package (topicforge).

### Is the TopicForge MCP server safe to use?

TopicForge scores 74 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 20 September 2026. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the TopicForge MCP server expose?

TopicForge exposes 11 tools: health_check, list_topics, get_topic_info, sample_messages, analyze_bag, and 6 more. Their descriptions and schemas cost roughly 3,659 tokens of context every time the server is loaded.

### Is the TopicForge MCP server still maintained?

TopicForge is still listed as active in the MCP registry. We last reached this channel on 20 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

## Links

- PyPI project: https://pypi.org/project/topicforge/
- Socket report: https://socket.dev/pypi/package/topicforge
- Repository: https://github.com/yaniswav/TopicForge
- Changelog RSS feed: https://verifymcp.io/servers/yaniswav-topicforge/topicforge.xml
- Changelog JSON feed: https://verifymcp.io/servers/yaniswav-topicforge/topicforge.json
- HTML version of this page: https://verifymcp.io/servers/yaniswav-topicforge/topicforge
