Skip to main content
Glama

list_participants

Read-onlyIdempotent

Lists DDS participants observed on the bus with GUID, vendor, hostname, domain and lifecycle status, so you can identify active and departed nodes and detect restarts without counting GUIDs by hand.

Instructions

List DDS participants observed on the bus as a ParticipantListing: guid, vendor (read from the GUID prefix, unknown for vendors that do not put theirs there, e.g. Dust DDS and RTI Connext; see vendor_source), optional name (the announced EntityName, e.g. lidar_driver; a participant with none is normal, not a fault: identify it by its topics), hostname, domain_id and is_observer (TopicForge's own read-only participant, listed like any other). Lifecycle: status (active / left), first_seen_ns / last_seen_ns (TopicForge's clock), announced_ns (DDS source timestamp) and, once left, lost_ns + lost_time_source: an upper bound of the death (exact after a clean shutdown; after a crash the lease expiry, so up to one lease earlier: 10 s Cyclone, 20 s Fast DDS, 100 s RTI). Restarts: a restarted node is a new GUID with the same name; restarts_by_name gives lives (GUIDs) and departures per name, so do not count GUIDs by hand (seen_count is not a restart count). Cyclone tracks discovery in the background; right after start the call waits up to 3 s for it. Only the domain joined at startup is observed (health_check dds_domain_id): a participant on another domain is INVISIBLE here, and domain_id does not switch domains (restart with TOPICFORGE_DDS_DOMAIN_ID). Works at the raw DDS layer, so non-ROS participants appear too. Read-only by architecture: it cannot publish, modify QoS or alter the bus. Raises an MCP error when no DDS module is active (install topicforge[dds], set TOPICFORGE_DDS_BACKEND=cyclone); the mock backend returns fixtures. 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
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`.
totalYes
returnedYes
domain_idYesDDS domain observed (joined at startup).
truncatedYes
participantsYes
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 and stayed). A restart is a new GUID with the same name.
restarts_by_nodeNo`restarts_by_name` keyed by ROS 2 node full name.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed41 schema fields changedv0.8.0
    • removedOutput schema / $defs / ParticipantInfo / properties / announced_ns / anyOf
      Removed value: -[
      -  {
      -    "minimum": 0,
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / ParticipantInfo / properties / announced_ns / default
      Removed value: -null
    • addedOutput schema / $defs / ParticipantInfo / properties / announced_ns / minimum
      Added value: +0
    • addedOutput schema / $defs / ParticipantInfo / properties / announced_ns / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • addedOutput schema / $defs / ParticipantInfo / properties / endpoint_count
      Added value: +{
      +  "description": "Writers plus readers seen for this participant; `None` without an endpoint cache.",
      +  "minimum": 0,
      +  "type": [
      +    "integer",
      +    "null"
      +  ]
      +}
    • removedOutput schema / $defs / ParticipantInfo / properties / first_seen_ns / anyOf
      Removed value: -[
      -  {
      -    "minimum": 0,
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / ParticipantInfo / properties / first_seen_ns / default
      Removed value: -null
    • addedOutput schema / $defs / ParticipantInfo / properties / first_seen_ns / minimum
      Added value: +0
    • addedOutput schema / $defs / ParticipantInfo / properties / first_seen_ns / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedOutput schema / $defs / ParticipantInfo / properties / hostname / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / ParticipantInfo / properties / hostname / default
      Removed value: -null
    • addedOutput schema / $defs / ParticipantInfo / properties / hostname / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / $defs / ParticipantInfo / properties / is_observer / default
      Removed value: -false
    • addedOutput schema / $defs / ParticipantInfo / properties / is_topicforge_cli
      Added value: +{
      +  "description": "True for a `ros2` CLI process TopicForge spawned (lingers ~2 s); not the robot.",
      +  "type": "boolean"
      +}
    • removedOutput schema / $defs / ParticipantInfo / properties / last_seen_ns / anyOf
      Removed value: -[
      -  {
      -    "minimum": 0,
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / ParticipantInfo / properties / last_seen_ns / default
      Removed value: -null
    • addedOutput schema / $defs / ParticipantInfo / properties / last_seen_ns / minimum
      Added value: +0
    • addedOutput schema / $defs / ParticipantInfo / properties / last_seen_ns / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedOutput schema / $defs / ParticipantInfo / properties / lost_ns / anyOf
      Removed value: -[
      -  {
      -    "minimum": 0,
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / ParticipantInfo / properties / lost_ns / default
      Removed value: -null
    • addedOutput schema / $defs / ParticipantInfo / properties / lost_ns / minimum
      Added value: +0
    • addedOutput schema / $defs / ParticipantInfo / properties / lost_ns / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedOutput schema / $defs / ParticipantInfo / properties / lost_time_source / anyOf
      Removed value: -[
      -  {
      -    "enum": [
      -      "dds_source_timestamp",
      -      "observed_local"
      -    ],
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / ParticipantInfo / properties / lost_time_source / default
      Removed value: -null
    • addedOutput schema / $defs / ParticipantInfo / properties / lost_time_source / enum
      Added value: +[
      +  "dds_source_timestamp",
      +  "observed_local",
      +  null
      +]
    • addedOutput schema / $defs / ParticipantInfo / properties / lost_time_source / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / $defs / ParticipantInfo / properties / name / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / ParticipantInfo / properties / name / default
      Removed value: -null
    • addedOutput schema / $defs / ParticipantInfo / properties / name / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • removedOutput schema / $defs / ParticipantInfo / properties / node_names_source / default
      Removed value: -"none"
    • removedOutput schema / $defs / ParticipantInfo / properties / seen_count / default
      Removed value: -1
    • removedOutput schema / $defs / ParticipantInfo / properties / status / default
      Removed value: -"unknown"
    • removedOutput schema / $defs / ParticipantInfo / properties / vendor_source / default
      Removed value: -"none"
    • removedOutput schema / description
      Removed value: -"Envelope returned by `list_participants`."
    • 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"
      +]
    • addedOutput schema / properties / restarts_by_node
      Added value: +{
      +  "additionalProperties": {
      +    "$ref": "#/$defs/NameLifecycle"
      +  },
      +  "description": "`restarts_by_name` keyed by ROS 2 node full name.",
      +  "type": "object"
      +}
    • 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"
      +]
  2. Changed52 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"
    • removedInput schema / title
      Removed value: -"list_participantsArguments"
    • 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 / ParticipantInfo / description
      Removed value: -"DDS participant discovered on the configured domain.\n\nCarries identity (`guid`, `name`, `vendor`) plus lifecycle fields\n(`first_seen_ns`, `last_seen_ns`, `status`, `seen_count`)."
    • changedOutput schema / $defs / ParticipantInfo / properties / announced_ns / description
      Previous value: -"Nanoseconds since epoch (DDS source timestamp of the participant's most recent announcement, taken from the announcing side's clock; the local clock when the announcer sent none). `None` when the backend does not expose it. Distinct from `first_seen_ns` / `last_seen_ns`, which are TopicForge's own local clock."New value: +"Source timestamp of the latest announcement, on the announcer's clock; `None` if unavailable."
    • removedOutput schema / $defs / ParticipantInfo / properties / announced_ns / title
      Removed value: -"Announced Ns"
    • removedOutput schema / $defs / ParticipantInfo / properties / domain_id / description
      Removed value: -"DDS domain id the participant is bound to."
    • removedOutput schema / $defs / ParticipantInfo / properties / domain_id / title
      Removed value: -"Domain Id"
    • changedOutput schema / $defs / ParticipantInfo / properties / first_seen_ns / description
      Previous value: -"Wall-clock timestamp (nanoseconds since epoch) of the **first** discovery sample TopicForge observed for this participant. `None` when the adapter does not track lifecycle."New value: +"Wall-clock ns of the first discovery sample seen; `None` without lifecycle tracking."
    • removedOutput schema / $defs / ParticipantInfo / properties / first_seen_ns / title
      Removed value: -"First Seen Ns"
    • removedOutput schema / $defs / ParticipantInfo / properties / guid / description
      Removed value: -"DDS Global Unique Identifier of the participant: hex string, stable across discovery events within a single deployment."
    • removedOutput schema / $defs / ParticipantInfo / properties / guid / title
      Removed value: -"Guid"
    • removedOutput schema / $defs / ParticipantInfo / properties / hostname / description
      Removed value: -"Hostname announced in discovery (Cyclone `__Hostname` property). `None` when the vendor does not announce it."
    • removedOutput schema / $defs / ParticipantInfo / properties / hostname / title
      Removed value: -"Hostname"
    • changedOutput schema / $defs / ParticipantInfo / properties / is_observer / description
      Previous value: -"True for TopicForge's own read-only observer participant, which joins the domain to watch it and appears in this list; false for every other participant."New value: +"True for TopicForge's own read-only observer participant."
    • removedOutput schema / $defs / ParticipantInfo / properties / is_observer / title
      Removed value: -"Is Observer"
    • changedOutput schema / $defs / ParticipantInfo / properties / last_seen_ns / description
      Previous value: -"Wall-clock timestamp (nanoseconds since epoch) of the **most recent** discovery sample observed. Used by `participant_events` to distinguish currently-active participants from those that left the bus. `None` when the adapter does not track lifecycle."New value: +"Wall-clock ns of the latest discovery sample; `None` without lifecycle tracking."
    • removedOutput schema / $defs / ParticipantInfo / properties / last_seen_ns / title
      Removed value: -"Last Seen Ns"
    • changedOutput schema / $defs / ParticipantInfo / properties / lost_ns / description
      Previous value: -"Nanoseconds since epoch of the moment the participant left, once `status` is `left`; `None` while it is active. How precise it is depends on `lost_time_source`. It is an upper bound of when the participant died: after a clean shutdown it is the exact leave time, after a crash it is when the lease expired, and the two cannot be told apart. The process died at or before `lost_ns`, and at most one lease earlier (10 s Cyclone default, 20 s Fast, 100 s RTI)."New value: +"Once `left`: ns upper bound of the death (exact after a clean shutdown; after a crash the lease expiry, up to one lease earlier: 10 s Cyclone, 20 s Fast, 100 s RTI). `None` while active."
    • removedOutput schema / $defs / ParticipantInfo / properties / lost_ns / title
      Removed value: -"Lost Ns"
    • changedOutput schema / $defs / ParticipantInfo / properties / lost_time_source / description
      Previous value: -"Where `lost_ns` comes from: `dds_source_timestamp` (timestamp carried by the discovery dispose) or `observed_local` (the moment TopicForge noticed, the weakest). `None` while active."New value: +"Origin of `lost_ns`: `dds_source_timestamp` or `observed_local` (weakest); `None` while active."
    • removedOutput schema / $defs / ParticipantInfo / properties / lost_time_source / title
      Removed value: -"Lost Time Source"
    • removedOutput schema / $defs / ParticipantInfo / 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"
      -}
    • changedOutput schema / $defs / ParticipantInfo / properties / name / description
      Previous value: -"Participant name the application announced through the standard DDS EntityName QoS (PID_ENTITY_NAME in RTPS discovery), for example `lidar_driver`. `None` when the application did not set one (Dust DDS cannot) or the backend does not expose it."New value: +"Announced EntityName (e.g. `lidar_driver`); `None` when the application set none (Dust DDS cannot)."
    • removedOutput schema / $defs / ParticipantInfo / properties / name / title
      Removed value: -"Name"
    • addedOutput schema / $defs / ParticipantInfo / properties / node_names
      Added value: +{
      +  "description": "Fully qualified ROS 2 node names sharing this participant; empty when `node_names_source` is `none`.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
    • addedOutput schema / $defs / ParticipantInfo / properties / node_names_source
      Added value: +{
      +  "default": "none",
      +  "description": "`ros_discovery_info`, `ros2_cli` or `none` (no link made).",
      +  "enum": [
      +    "ros_discovery_info",
      +    "ros2_cli",
      +    "none"
      +  ],
      +  "type": "string"
      +}
    • changedOutput schema / $defs / ParticipantInfo / properties / seen_count / description
      Previous value: -"Number of distinct discovery samples observed for this participant across all calls to `list_participants` during this server's lifetime. Starts at 1 and increments on each observation."New value: +"Discovery observations of this GUID in this server's lifetime. Not a restart count: a restart is a new GUID (see `restarts_by_name`)."
    • removedOutput schema / $defs / ParticipantInfo / properties / seen_count / title
      Removed value: -"Seen Count"
    • changedOutput schema / $defs / ParticipantInfo / properties / status / description
      Previous value: -"Lifecycle status. `active` means TopicForge has a recent discovery sample for this GUID and the bus has not signalled removal. `left` means the participant was observed earlier but has since disappeared (a Fast DDS `REMOVED` callback or a Cyclone polling delta). `unknown` when the adapter does not track lifecycle."New value: +"`active`, `left` (seen earlier, since gone) or `unknown` (no lifecycle tracking)."
    • removedOutput schema / $defs / ParticipantInfo / properties / status / title
      Removed value: -"Status"
    • changedOutput schema / $defs / ParticipantInfo / properties / vendor / description
      Previous value: -"DDS implementation that announced this participant, decoded from the OMG-RTPS `vendor_id` field on the discovery sample. `cyclone` (Eclipse Cyclone DDS), `fast` (eProsima Fast DDS), `rti` (RTI Connext), `rti_micro` (RTI Connext Micro), `opensplice` (ADLink OpenSplice), `opendds` (OCI OpenDDS), `coredx` (Twin Oaks CoreDX), `intercom` (Kongsberg InterCOM), `dust` (S2E Dust DDS). `mock` is reserved for synthetic fixtures. `unknown` means the vendor could not be determined: TopicForge reads it from the vendor prefix of the participant GUID, and some vendors (for example Dust DDS and RTI Connext) do not put their vendor id there, while the Cyclone Python binding does not expose the RTPS header vendor id. The participant is still reported; see `vendor_source`. Any conformant DDS-RTPS participant should appear through standard discovery; Cyclone DDS and Dust DDS have been observed on a live bus. See `docs/dds-interop-matrix.md`."New value: +"DDS implementation read from the GUID vendor prefix: cyclone, fast, rti, rti_micro, opensplice, opendds, coredx, intercom, dust, mock or `unknown` (Dust DDS and RTI Connext do not put theirs there); see `vendor_source`."
    • removedOutput schema / $defs / ParticipantInfo / properties / vendor / title
      Removed value: -"Vendor"
    • changedOutput schema / $defs / ParticipantInfo / properties / vendor_source / description
      Previous value: -"Where `vendor` came from: `guid_prefix` when it was read from the vendor prefix of the participant GUID, `none` when the vendor is unknown (or synthetic in mock mode)."New value: +"`guid_prefix` when `vendor` was read from the GUID, else `none`."
    • removedOutput schema / $defs / ParticipantInfo / properties / vendor_source / title
      Removed value: -"Vendor Source"
    • changedOutput schema / $defs / ParticipantInfo / required
      Previous value: -[
      -  "guid",
      -  "vendor",
      -  "domain_id",
      -  "mode_effective"
      -]New value: +[
      +  "guid",
      +  "vendor",
      +  "domain_id"
      +]
    • removedOutput schema / $defs / ParticipantInfo / title
      Removed value: -"ParticipantInfo"
    • addedOutput schema / additionalProperties
      Added value: +false
    • addedOutput schema / description
      Added value: +"Envelope returned by `list_participants`."
    • addedOutput schema / properties / domain_id
      Added value: +{
      +  "description": "DDS domain observed (joined at startup).",
      +  "maximum": 232,
      +  "minimum": 0,
      +  "type": "integer"
      +}
    • 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 / participants
      Added value: +{
      +  "items": {
      +    "$ref": "#/$defs/ParticipantInfo"
      +  },
      +  "type": "array"
      +}
    • 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.",
      +  "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/ParticipantInfo"
      -  },
      -  "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: +{
      +  "type": "boolean"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "result"
      -]New value: +[
      +  "participants",
      +  "returned",
      +  "total",
      +  "truncated",
      +  "domain_id",
      +  "mode_effective"
      +]
    • changedOutput schema / title
      Previous value: -"list_participantsOutput"New value: +"ParticipantListing"
  3. First observedv0.5.6

TDQS

A4.2/5.0
Behavior5/5

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

Despite annotations already declaring read-only and idempotent behavior, the description adds substantial behavioral detail: startup discovery wait up to 3 s, visibility limited to the startup domain, mock backend behavior, MCP errors when no DDS module is active, lock contention with a busy error, and a 45 s duration bound. This is unusually complete transparency beyond the annotations.

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

Conciseness4/5

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

The description is long but front-loaded and structured with bold sections, and almost every sentence conveys operational meaning. Some return-field detail may duplicate the output schema, but the field semantics it adds, such as unknown vendors and optional names, are useful rather than 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?

For a complex DDS discovery tool with an output schema and safety annotations, the description covers the important operational gaps: domain visibility, restart counting, lifecycle timestamps, error conditions, backend requirements, and duration limits. An agent has enough context to invoke and interpret the tool correctly.

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?

There is one parameter and schema description coverage is 100%, so the schema already explains that domain_id is only range-checked and does not switch domains. The description reinforces that constraint but does not add syntax or format details beyond what the schema provides.

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 verb and resource: list DDS participants observed on the bus, returned as a ParticipantListing. It also distinguishes the tool from ROS-graph tools by noting it works at the raw DDS layer, so non-ROS participants appear too.

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

Usage Guidelines3/5

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

Usage context is implied through domain restrictions and restart semantics, but the description never explicitly names alternatives such as participant_events or list_nodes and never says when to choose this tool over them. An agent can infer the use case, but the guidance is not explicit.

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