Skip to main content
Glama

participant_events

Read-onlyIdempotent

Retrieve recent DDS participant lifecycle events (discovered/lost) to identify who joined or left within a time window, with GUIDs, names, timestamps, and restart counts.

Instructions

Return DDS participant lifecycle events (discovered / lost) from a recent window, e.g. 'who left in the last 5 minutes?', as a ParticipantEventListing, newest first: guid, event_type, vendor, timestamp_ns, time_source, observed_ns, age_s (seconds before the response; live only), optional name (announced name), hostname and domain_id. time_source is dds_source_timestamp (timestamp of the announcement or dispose) or observed_local (when TopicForge noticed, weakest). Crash caveat: a lost timestamp is an upper bound of the death: exact after a clean shutdown, the lease expiry after a crash, so the process died up to one lease earlier (10 s Cyclone, 20 s Fast DDS, 100 s RTI); the two cases cannot be told apart. Restarts: a restarted node is a new participant (one lost and one discovered, different guid, same name); restarts_by_name gives lives and departures per name over the window, so do not count GUIDs by hand. Capped at 200 events (truncated, note; reduce lookback_s). TopicForge only knows what happened since it started watching (health_check.observer_started_ns). Backends: Cyclone polls discovery in the background every 0.5 s, so a participant cycle faster than the reader's history depth between two passes can be missed; mock returns a fixture timeline. Right after start the call waits up to 3 s for discovery. Read-only by architecture. Raises an MCP error when no DDS module is active. Duration: DDS lock, one call at a time, ends within 45 s; busy if held.

Tool call template

This tool declares no required parameters. Add optional parameters from the input schema as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {},
    "name": "participant_events"
  }
}

Input Schema

NameRequiredDescriptionDefault
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.
lookback_sNoWindow in seconds, 1..86400, default 300. A larger window may hit the 200-event cap.

Output Schema

NameRequiredDescriptionDefault
noteNoOne sentence of context, or `None`.
totalYes
eventsYes
returnedYes
domain_idYesDDS domain observed (joined at startup).
truncatedYesTrue when cut to the 200-event cap.
restarts_noteNoSet when unnamed participants were left out of `restarts_by_name`.
mode_effectiveYes`live` or `mock` (fixtures).
restarts_by_nameNoNames seen with several GUIDs or lost at least once (absent: ran once). A restart is a new GUID, same name. Window only.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changedv0.11.0
    • addedOutput schema / $defs / ParticipantEvent / properties / is_topicforge_cli
      Added value: +{
      +  "description": "True when `node_names` show a `ros2` CLI or TopicForge helper node: tooling, not the robot.",
      +  "type": "boolean"
      +}
  2. Changed1 schema field changedv0.10.0
    • addedOutput schema / $defs / ParticipantEvent / properties / node_names
      Added value: +{
      +  "description": "ROS node names of the participant, resolved when read; empty if unknown.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
  3. Changed2 schema fields changedv0.9.0
    • changedOutput schema / $defs / ParticipantEvent / properties / time_source / description
      Previous value: -"`dds_source_timestamp` (DDS timestamp of the announcement or dispose; for `lost` an upper bound of the death) or `observed_local` (when TopicForge noticed, weakest)."New value: +"`dds_source_timestamp` (of the announcement or dispose; for `lost` an upper bound) or `observed_local` (weakest)."
    • changedOutput schema / properties / restarts_by_name / description
      Previous value: -"Names seen with several GUIDs or lost at least once (absent: ran once and stayed). A restart is a new GUID with the same name. Window only."New value: +"Names seen with several GUIDs or lost at least once (absent: ran once). A restart is a new GUID, same name. Window only."
  4. Changed22 schema fields changedv0.8.0
    • removedOutput schema / $defs / ParticipantEvent / properties / age_s / anyOf
      Removed value: -[
      -  {
      -    "minimum": 0,
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / ParticipantEvent / properties / age_s / default
      Removed value: -null
    • addedOutput schema / $defs / ParticipantEvent / properties / age_s / minimum
      Added value: +0
    • addedOutput schema / $defs / ParticipantEvent / properties / age_s / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedOutput schema / $defs / ParticipantEvent / properties / hostname / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / ParticipantEvent / properties / hostname / default
      Removed value: -null
    • addedOutput schema / $defs / ParticipantEvent / properties / hostname / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / $defs / ParticipantEvent / properties / name / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / ParticipantEvent / properties / name / default
      Removed value: -null
    • addedOutput schema / $defs / ParticipantEvent / properties / name / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / $defs / ParticipantEvent / properties / observed_ns / anyOf
      Removed value: -[
      -  {
      -    "minimum": 0,
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / ParticipantEvent / properties / observed_ns / default
      Removed value: -null
    • addedOutput schema / $defs / ParticipantEvent / properties / observed_ns / minimum
      Added value: +0
    • addedOutput schema / $defs / ParticipantEvent / properties / observed_ns / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedOutput schema / $defs / ParticipantEvent / properties / time_source / default
      Removed value: -"observed_local"
    • removedOutput schema / description
      Removed value: -"Envelope returned by `participant_events`."
    • 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 / restarts_note / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / restarts_note / default
      Removed value: -null
    • addedOutput schema / properties / restarts_note / type
      Added value: +[
      +  "string",
      +  "null"
      +]
  5. Changed43 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"
    • addedInput schema / properties / lookback_s
      Added value: +{
      +  "default": 300,
      +  "description": "Window in seconds, 1..86400, default 300. A larger window may hit the 200-event cap.",
      +  "maximum": 86400,
      +  "minimum": 1,
      +  "type": "integer"
      +}
    • removedInput schema / properties / lookback_seconds
      Removed value: -{
      -  "default": 300,
      -  "description": "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_id` when that happens.",
      -  "maximum": 86400,
      -  "minimum": 1,
      -  "title": "Lookback Seconds",
      -  "type": "integer"
      -}
    • removedInput schema / title
      Removed value: -"participant_eventsArguments"
    • addedOutput schema / $defs / NameLifecycle
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "departures": {
      +      "description": "Times a participant with this name was lost.",
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "lives": {
      +      "description": "Distinct GUIDs that carried this name; restarts = `lives` - 1.",
      +      "minimum": 1,
      +      "type": "integer"
      +    }
      +  },
      +  "required": [
      +    "lives",
      +    "departures"
      +  ],
      +  "type": "object"
      +}
    • removedOutput schema / $defs / ParticipantEvent / description
      Removed value: -"A point-in-time lifecycle event (discovered or lost) for a DDS participant.\n\n`ParticipantInfo` is the current state; this is one event in its history.\nReturned by `participant_events`."
    • addedOutput schema / $defs / ParticipantEvent / properties / age_s
      Added value: +{
      +  "anyOf": [
      +    {
      +      "minimum": 0,
      +      "type": "number"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Seconds between `timestamp_ns` and this response; `None` in mock mode."
      +}
    • removedOutput schema / $defs / ParticipantEvent / properties / domain_id / description
      Removed value: -"DDS domain id the event occurred on."
    • removedOutput schema / $defs / ParticipantEvent / properties / domain_id / title
      Removed value: -"Domain Id"
    • changedOutput schema / $defs / ParticipantEvent / properties / event_type / description
      Previous value: -"`discovered` when the participant first joined the bus (or rejoined after leaving). `lost` when TopicForge observed a `REMOVED` callback (Fast DDS) or a polling delta where the GUID no longer appears in the DCPSParticipant builtin reader snapshot (Cyclone). The transition is reported once per state change."New value: +"`discovered` (joined or rejoined) or `lost` (removed, or gone from discovery); reported once per change."
    • removedOutput schema / $defs / ParticipantEvent / properties / event_type / title
      Removed value: -"Event Type"
    • removedOutput schema / $defs / ParticipantEvent / properties / guid / description
      Removed value: -"DDS Global Unique Identifier of the participant involved."
    • removedOutput schema / $defs / ParticipantEvent / properties / guid / title
      Removed value: -"Guid"
    • removedOutput schema / $defs / ParticipantEvent / properties / hostname / description
      Removed value: -"Hostname announced by the participant when known, else `None`."
    • removedOutput schema / $defs / ParticipantEvent / properties / hostname / title
      Removed value: -"Hostname"
    • removedOutput schema / $defs / ParticipantEvent / properties / mode_effective
      Removed value: -{
      -  "description": "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`.",
      -  "enum": [
      -    "mock",
      -    "live"
      -  ],
      -  "title": "Mode Effective",
      -  "type": "string"
      -}
    • removedOutput schema / $defs / ParticipantEvent / properties / name / description
      Removed value: -"Participant name (EntityName QoS) when known, else `None`."
    • removedOutput schema / $defs / ParticipantEvent / properties / name / title
      Removed value: -"Name"
    • changedOutput schema / $defs / ParticipantEvent / properties / observed_ns / description
      Previous value: -"Local wall-clock time (ns since epoch) at which TopicForge noticed the event. Always at or after `timestamp_ns` when the latter is DDS-derived. `None` when not tracked."New value: +"Local ns when TopicForge noticed the event, never before a DDS-derived `timestamp_ns`; `None` when untracked."
    • removedOutput schema / $defs / ParticipantEvent / properties / observed_ns / title
      Removed value: -"Observed Ns"
    • changedOutput schema / $defs / ParticipantEvent / properties / time_source / description
      Previous value: -"Where `timestamp_ns` comes from. `dds_source_timestamp`: the DDS timestamp of the discovery announcement or dispose. For a `lost` event it is an upper bound: exact after a clean shutdown, the lease expiry after a crash (the process died up to one lease earlier: 10 s Cyclone default, 20 s Fast, 100 s RTI), and the two cannot be told apart. `observed_local`: the moment TopicForge noticed (weakest)."New value: +"`dds_source_timestamp` (DDS timestamp of the announcement or dispose; for `lost` an upper bound of the death) or `observed_local` (when TopicForge noticed, weakest)."
    • removedOutput schema / $defs / ParticipantEvent / properties / time_source / title
      Removed value: -"Time Source"
    • changedOutput schema / $defs / ParticipantEvent / properties / timestamp_ns / description
      Previous value: -"Wall-clock timestamp (nanoseconds since epoch) when TopicForge captured the event. For Fast DDS this is when the listener callback ran; for Cyclone this is when the polling delta was computed; for mock fixtures this is a deterministic anchor."New value: +"Wall-clock ns of the event; its origin is `time_source`."
    • removedOutput schema / $defs / ParticipantEvent / properties / timestamp_ns / title
      Removed value: -"Timestamp Ns"
    • removedOutput schema / $defs / ParticipantEvent / properties / vendor / description
      Removed value: -"DDS implementation tag for the participant involved, decoded the same way as `ParticipantInfo.vendor`."
    • removedOutput schema / $defs / ParticipantEvent / properties / vendor / title
      Removed value: -"Vendor"
    • changedOutput schema / $defs / ParticipantEvent / required
      Previous value: -[
      -  "guid",
      -  "event_type",
      -  "vendor",
      -  "timestamp_ns",
      -  "domain_id",
      -  "mode_effective"
      -]New value: +[
      +  "guid",
      +  "event_type",
      +  "vendor",
      +  "timestamp_ns",
      +  "domain_id"
      +]
    • removedOutput schema / $defs / ParticipantEvent / title
      Removed value: -"ParticipantEvent"
    • addedOutput schema / additionalProperties
      Added value: +false
    • addedOutput schema / description
      Added value: +"Envelope returned by `participant_events`."
    • addedOutput schema / properties / domain_id
      Added value: +{
      +  "description": "DDS domain observed (joined at startup).",
      +  "maximum": 232,
      +  "minimum": 0,
      +  "type": "integer"
      +}
    • addedOutput schema / properties / events
      Added value: +{
      +  "items": {
      +    "$ref": "#/$defs/ParticipantEvent"
      +  },
      +  "type": "array"
      +}
    • addedOutput schema / properties / mode_effective
      Added value: +{
      +  "description": "`live` or `mock` (fixtures).",
      +  "enum": [
      +    "mock",
      +    "live"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / note
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "One sentence of context, or `None`."
      +}
    • addedOutput schema / properties / restarts_by_name
      Added value: +{
      +  "additionalProperties": {
      +    "$ref": "#/$defs/NameLifecycle"
      +  },
      +  "description": "Names seen with several GUIDs or lost at least once (absent: ran once and stayed). A restart is a new GUID with the same name. Window only.",
      +  "type": "object"
      +}
    • addedOutput schema / properties / restarts_note
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Set when unnamed participants were left out of `restarts_by_name`."
      +}
    • removedOutput schema / properties / result
      Removed value: -{
      -  "items": {
      -    "$ref": "#/$defs/ParticipantEvent"
      -  },
      -  "title": "Result",
      -  "type": "array"
      -}
    • addedOutput schema / properties / returned
      Added value: +{
      +  "minimum": 0,
      +  "type": "integer"
      +}
    • addedOutput schema / properties / total
      Added value: +{
      +  "minimum": 0,
      +  "type": "integer"
      +}
    • addedOutput schema / properties / truncated
      Added value: +{
      +  "description": "True when cut to the 200-event cap.",
      +  "type": "boolean"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "result"
      -]New value: +[
      +  "events",
      +  "returned",
      +  "total",
      +  "truncated",
      +  "domain_id",
      +  "mode_effective"
      +]
    • changedOutput schema / title
      Previous value: -"participant_eventsOutput"New value: +"ParticipantEventListing"
  6. First observedv0.5.6

TDQS

A4.5/5.0
Behavior5/5

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

Goes far beyond the annotations: discloses the crash caveat that `lost` timestamps are upper bounds bounded by lease expiry (10 s / 20 s / 100 s by vendor), explains restart semantics as a lost+discovered pair with a new guid, and warns TopicForge only knows events since it started watching. It also documents the 200-event cap with `truncated`/`note`, backend polling granularity (0.5 s Cyclone, possible misses), the up-to-3 s startup wait, the MCP error with no active DDS module, and the 45 s lock / `busy` behavior — all consistent with readOnlyHint=true.

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

Conciseness3/5

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

Front-loaded correctly — purpose and example come first, caveats after. But it is dense and the long enumeration of returned fields (`guid`, `event_type`, `vendor`, `timestamp_ns`, ...) duplicates what the existing output schema already provides, which is wasted length for this tool.

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?

For a tool with real subtleties — eventual-consistency from polling discovery, lease-bounded death timestamps, restart attribution, and a hard event cap — everything an agent needs to interpret results correctly is present. An output schema exists, yet the description still supplies the interpretive semantics the schema cannot.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, and the description earns a bump by tying `lookback_s` to the 200-event cap ('reduce `lookback_s`' when truncated) and by noting the window is bounded. It does not restate the schema's own `domain_id` semantics, which is appropriate since that parameter's description already explains it is ignored.

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?

States a specific verb and resource: 'Return DDS participant lifecycle events (`discovered` / `lost`) from a recent window', plus a concrete framing question ('who left in the last 5 minutes?'). The lifecycle-event scope is distinguishable from a general listing tool such as list_participants without opening either schema.

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?

Usage context is clear and actionable: it answers 'who left recently', tells the agent to use `restarts_by_name` rather than counting GUIDs by hand, and to reduce `lookback_s` when `truncated` fires. It stops short of naming a sibling alternative or stating exclusions (e.g., when to prefer list_participants or health_check instead), so it is strong context without explicit routing.

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