Skip to main content
Glama

topic_metrics

Read-onlyIdempotent

Computes temporal metrics for a DDS topic over a recent window: observed and declared frequency, sequence gaps, and latency percentiles. Use it to assess topic behavior and data availability.

Instructions

Return temporal metrics (frequency, sequence gaps, latency percentiles) for a DDS topic over a recent window as a TopicMetrics. Read status first: unsupported_user_topic means the payload is not decoded, so no metric exists: every null or 0 is not a measurement; no_samples_yet is a builtin topic with nothing buffered; ok means computed. Limits: the buffer is filled only when peek_dds_samples runs on the topic, so observed_frequency_hz reflects how often it was called, not the publish rate: a coarse presence signal. declared_frequency_hz is 1 / deadline of the shortest writer Deadline announced in discovery (declared, not measured; null when none). Also sequence_gaps_count, latency_ns_p50/p95/p99 and availability flags. Read-only by architecture. Raises an MCP error when no DDS module is active or window_s is outside 1..3600. Right after start the call waits up to 3 s for discovery. Duration: DDS binding behind the DDS lock, one call at a time; ends within 45 s; busy error if the lock is held (retry).

Input Schema

NameRequiredDescriptionDefault
topicYesDDS topic name: bare (`scan`), ROS 2 mangled (`rt/scan`) or a builtin discovery topic (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`). Letters, digits, `_`, `/` and `::` only.
window_sNoWindow in seconds, 1..3600, default 60.
domain_idNoIgnored: it does not switch domains and is only range-checked (0..232). The domain observed is the one joined at startup (`TOPICFORGE_DDS_DOMAIN_ID`), reported in the response `domain_id`; `note` flags a different non-zero value.

Output Schema

NameRequiredDescriptionDefault
noteNoOne sentence of context, or `None`.
topicYes
statusNo`ok`; `no_samples_yet` (empty buffer); `unsupported_user_topic` (payload not decoded: null fields are not measurements).
window_sYesRequested window in seconds (echoed).
latency_ns_p50NoMedian publish-to-receive latency in ns; `None` unless `latency_available`.
latency_ns_p95No95th-percentile publish-to-receive latency (ns).
latency_ns_p99No99th-percentile publish-to-receive latency (ns).
mode_effectiveYes`live` or `mock` (fixtures).
window_actual_sYesSeconds actually covered; shorter than `window_s` after a recent start; `0.0` without samples.
samples_observedYesSamples buffered in the window. `0` does NOT mean no publisher, only that no `peek_dds_samples` call captured one.
latency_availableNoTrue when a sample carried both a publish and a receive timestamp.
sequence_gaps_countNoMissing sequence numbers in the buffered samples; `0` also when none exist (see `sequence_numbers_available`).
declared_frequency_hzNo`1 / deadline` of the shortest writer Deadline announced: the promised rate, not the observed one; `None` when none announced.
observed_frequency_hzNo`samples_observed / window_actual_s`; `None` under 2 samples.
sequence_numbers_availableNoTrue when a sequence number could be read from a sample.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed25 schema fields changedv0.8.0
    • removedOutput schema / properties / declared_frequency_hz / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / declared_frequency_hz / default
      Removed value: -null
    • addedOutput schema / properties / declared_frequency_hz / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedOutput schema / properties / latency_available / default
      Removed value: -false
    • removedOutput schema / properties / latency_ns_p50 / anyOf
      Removed value: -[
      -  {
      -    "minimum": 0,
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / latency_ns_p50 / default
      Removed value: -null
    • addedOutput schema / properties / latency_ns_p50 / minimum
      Added value: +0
    • addedOutput schema / properties / latency_ns_p50 / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedOutput schema / properties / latency_ns_p95 / anyOf
      Removed value: -[
      -  {
      -    "minimum": 0,
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / latency_ns_p95 / default
      Removed value: -null
    • addedOutput schema / properties / latency_ns_p95 / minimum
      Added value: +0
    • addedOutput schema / properties / latency_ns_p95 / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedOutput schema / properties / latency_ns_p99 / anyOf
      Removed value: -[
      -  {
      -    "minimum": 0,
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / latency_ns_p99 / default
      Removed value: -null
    • addedOutput schema / properties / latency_ns_p99 / minimum
      Added value: +0
    • addedOutput schema / properties / latency_ns_p99 / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedOutput schema / properties / note / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / note / default
      Removed value: -null
    • addedOutput schema / properties / note / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / properties / observed_frequency_hz / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / observed_frequency_hz / default
      Removed value: -null
    • addedOutput schema / properties / observed_frequency_hz / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedOutput schema / properties / sequence_gaps_count / default
      Removed value: -0
    • removedOutput schema / properties / sequence_numbers_available / default
      Removed value: -false
    • removedOutput schema / properties / status / default
      Removed value: -"ok"
  2. Changed36 schema fields changedv0.7.1
    • changedInput schema / properties / domain_id / description
      Previous value: -"Accepted for compatibility (0..232). TopicForge observes the domain it joined at startup (TOPICFORGE_DDS_DOMAIN_ID); this argument does not switch domains, and the response `domain_id` says which one was observed."New value: +"Ignored: it does not switch domains and is only range-checked (0..232). The domain observed is the one joined at startup (`TOPICFORGE_DDS_DOMAIN_ID`), reported in the response `domain_id`; `note` flags a different non-zero value."
    • removedInput schema / properties / domain_id / title
      Removed value: -"Domain Id"
    • changedInput schema / properties / topic / description
      Previous value: -"DDS topic name. Bare DDS names such as `scan` are valid, as are ROS 2 mangled names such as `rt/scan` and the builtin discovery topics `DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`. Letters, digits, `_`, `/` and `::` are allowed; anything else is rejected."New value: +"DDS topic name: bare (`scan`), ROS 2 mangled (`rt/scan`) or a builtin discovery topic (`DCPSParticipant`, `DCPSSubscription`, `DCPSPublication`). Letters, digits, `_`, `/` and `::` only."
    • removedInput schema / properties / topic / title
      Removed value: -"Topic"
    • addedInput schema / properties / window_s
      Added value: +{
      +  "default": 60,
      +  "description": "Window in seconds, 1..3600, default 60.",
      +  "maximum": 3600,
      +  "minimum": 1,
      +  "type": "integer"
      +}
    • removedInput schema / properties / window_seconds
      Removed value: -{
      -  "default": 60,
      -  "description": "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.",
      -  "maximum": 3600,
      -  "minimum": 1,
      -  "title": "Window Seconds",
      -  "type": "integer"
      -}
    • removedInput schema / title
      Removed value: -"topic_metricsArguments"
    • changedOutput schema / description
      Previous value: -"Temporal metrics for a single DDS topic over a recent window.\n\nBuilt from samples that pass through `peek_dds_samples`. The buffer is\nfilled on demand because neither the `cyclonedds` nor the `fastdds`\nPython binding has a reliable per-sample callback, so a burst between two\ncalls is not seen.\n\nNumeric fields are `None` (`0` for `sequence_gaps_count`) when the data is\nmissing: no samples, no source timestamps, no sequence number in the\npayload. `samples_observed=0` is a valid result, not an error."New value: +"Temporal metrics for a single DDS topic over a recent window."
    • addedOutput schema / properties / declared_frequency_hz
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "number"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "`1 / deadline` of the shortest writer Deadline announced: the promised rate, not the observed one; `None` when none announced."
      +}
    • removedOutput schema / properties / frequency_hz_declared
      Removed value: -{
      -  "anyOf": [
      -    {
      -      "type": "number"
      -    },
      -    {
      -      "type": "null"
      -    }
      -  ],
      -  "default": null,
      -  "description": "Declared, not measured: `1 / deadline` for the shortest QoS Deadline period announced by a writer on this topic in discovery. `None` when no writer announced a finite Deadline or the topic is not announced. It is the rate the application promised, not the rate observed.",
      -  "title": "Frequency Hz Declared"
      -}
    • removedOutput schema / properties / frequency_hz_observed
      Removed value: -{
      -  "anyOf": [
      -    {
      -      "type": "number"
      -    },
      -    {
      -      "type": "null"
      -    }
      -  ],
      -  "default": null,
      -  "description": "`samples_observed / window_seconds_actual`. `None` when fewer than 2 samples were observed (a single sample does not define a frequency).",
      -  "title": "Frequency Hz Observed"
      -}
    • changedOutput schema / properties / latency_available / description
      Previous value: -"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."New value: +"True when a sample carried both a publish and a receive timestamp."
    • removedOutput schema / properties / latency_available / title
      Removed value: -"Latency Available"
    • changedOutput schema / properties / latency_ns_p50 / description
      Previous value: -"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_available=False`."New value: +"Median publish-to-receive latency in ns; `None` unless `latency_available`."
    • removedOutput schema / properties / latency_ns_p50 / title
      Removed value: -"Latency Ns P50"
    • removedOutput schema / properties / latency_ns_p95 / title
      Removed value: -"Latency Ns P95"
    • removedOutput schema / properties / latency_ns_p99 / title
      Removed value: -"Latency Ns P99"
    • changedOutput schema / properties / mode_effective / description
      Previous value: -"Runtime mode the adapter served this response in: `live` (real ROS2 introspection) or `mock` (deterministic fixtures). Lets a caller tell a real graph from a demo one without calling `health_check`."New value: +"`live` or `mock` (fixtures)."
    • removedOutput schema / properties / mode_effective / title
      Removed value: -"Mode Effective"
    • addedOutput schema / properties / note
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "One sentence of context, or `None`."
      +}
    • addedOutput schema / properties / observed_frequency_hz
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "number"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "`samples_observed / window_actual_s`; `None` under 2 samples."
      +}
    • changedOutput schema / properties / samples_observed / description
      Previous value: -"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 `peek_dds_samples` call captured one in the window."New value: +"Samples buffered in the window. `0` does NOT mean no publisher, only that no `peek_dds_samples` call captured one."
    • removedOutput schema / properties / samples_observed / title
      Removed value: -"Samples Observed"
    • changedOutput schema / properties / sequence_gaps_count / description
      Previous value: -"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 disambiguate)."New value: +"Missing sequence numbers in the buffered samples; `0` also when none exist (see `sequence_numbers_available`)."
    • removedOutput schema / properties / sequence_gaps_count / title
      Removed value: -"Sequence Gaps Count"
    • changedOutput schema / properties / sequence_numbers_available / description
      Previous value: -"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; primitives like `std_msgs/String` do not."New value: +"True when a sequence number could be read from a sample."
    • removedOutput schema / properties / sequence_numbers_available / title
      Removed value: -"Sequence Numbers Available"
    • changedOutput schema / properties / status / description
      Previous value: -"How to read the numbers. `unsupported_user_topic`: the topic is a user topic, whose payload is not decoded, so no metric exists and the null fields are not a measurement. `no_samples_yet`: a supported topic with nothing buffered in the window. `ok`: metrics computed from buffered samples."New value: +"`ok`; `no_samples_yet` (empty buffer); `unsupported_user_topic` (payload not decoded: null fields are not measurements)."
    • removedOutput schema / properties / status / title
      Removed value: -"Status"
    • removedOutput schema / properties / topic / description
      Removed value: -"Topic the metrics were computed for."
    • removedOutput schema / properties / topic / title
      Removed value: -"Topic"
    • addedOutput schema / properties / window_actual_s
      Added value: +{
      +  "description": "Seconds actually covered; shorter than `window_s` after a recent start; `0.0` without samples.",
      +  "minimum": 0,
      +  "type": "number"
      +}
    • addedOutput schema / properties / window_s
      Added value: +{
      +  "description": "Requested window in seconds (echoed).",
      +  "maximum": 3600,
      +  "minimum": 1,
      +  "type": "integer"
      +}
    • removedOutput schema / properties / window_seconds
      Removed value: -{
      -  "description": "Requested window in seconds (1..3600). Echoed back from the tool call so the LLM can correlate the request.",
      -  "maximum": 3600,
      -  "minimum": 1,
      -  "title": "Window Seconds",
      -  "type": "integer"
      -}
    • removedOutput schema / properties / window_seconds_actual
      Removed value: -{
      -  "description": "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 `samples_observed=0`.",
      -  "minimum": 0,
      -  "title": "Window Seconds Actual",
      -  "type": "number"
      -}
    • changedOutput schema / required
      Previous value: -[
      -  "topic",
      -  "window_seconds",
      -  "window_seconds_actual",
      -  "samples_observed",
      -  "mode_effective"
      -]New value: +[
      +  "topic",
      +  "window_s",
      +  "window_actual_s",
      +  "samples_observed",
      +  "mode_effective"
      +]
  3. First observedv0.5.6

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the read-only annotations, the description explains status values, what null/zero values mean, the distinction between observed and declared frequency, error conditions, discovery wait, locking, duration, and busy retry behavior. This is unusually rich behavioral context for an agent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but well front-loaded: purpose first, then critical status semantics, then limits, errors, and concurrency. Despite its length, every section provides operationally useful information with no evident filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, existing output schema, and annotations, the description covers the critical gaps an agent needs: status interpretation, buffer limitations, domain behavior, errors, and timing. Nothing essential for correct invocation appears missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents topic, window_s, and domain_id semantics thoroughly. The description reinforces window bounds and domain_id behavior but adds little parameter meaning beyond the structured schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific operation and resource: return temporal metrics for a DDS topic, naming frequency, sequence gaps, and latency percentiles. It clearly distinguishes the tool as a metrics-computation endpoint rather than a raw sample reader like peek_dds_samples.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear operational guidance: read status first, and understand that the buffer is populated only when peek_dds_samples runs on the topic. It does not explicitly compare this tool against all sibling alternatives, so it stops short of a full when-to-use/when-not matrix.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.