Skip to main content
Glama

peek_bag_samples

Read-onlyIdempotent

Reads the first N messages of a topic from a recorded bag file (MCAP, rosbag2 SQLite, ROS 1) for offline post-mortem analysis, with optional time-range and /rosout level filters.

Instructions

Peek up to count samples from a recorded bag file as a SampleResult: offline, for post-mortem (peek_dds_samples and sample_messages are live). Formats, by extension: MCAP (.mcap), rosbag2 SQLite (.db3), ROS 1 (.bag). Returns the first count messages of the topic in recording order, not the last (sample_messages keeps the newest log matches). start_s / end_s limit it to a bag-relative time range, empty with a note past the end; on /rosout, min_log_level and node keep the first count matching logs in range. Clocks: timestamp_ns is header.stamp when the message has a top-level header (stamp_source header), else the time in the body of Clock, TFMessage or Log (payload), else the bag record time (recorded); recorded_ns is always the record time. Each payload carries _decode_status (full / partial / raw). count defaults to 5, clamped to 50. Uses the rosbags library and needs the ROS 2 side of the runtime (an error on a DDS-only setup). Mock returns fixtures on canned paths. A bag without embedded definitions (Humble .db3) is decoded with those of its recorded distro, or Humble; note says which. Arrays over 4096 elements are cut and note lists them. Read-only by architecture. Raises an MCP error when the path does not exist, the topic is not in the bag, or rosbags is missing. Summary and rate: LaserScan, Odometry, Imu, Image and PointCloud2 samples carry a summary of the whole message (other types: null); rate gives frequency, interval spread and a verdict measured on recorded_ns (the bag record time of the returned messages). Use count >= 10 (50 above 20 Hz); a multi-publisher topic such as /tf reads erratic without being broken. Duration: takes no lock (in-process file read, so it runs while a ROS 2 or DDS call is busy); the time follows the bag's size and how deep the first samples are.

Input Schema

NameRequiredDescriptionDefault
nodeNo`/rosout` only: keep logs of this node and its child loggers, e.g. `/robot1/driver` (also `driver`, `robot1.driver`). Exact name, never a prefix. Other topics: error.
pathYesBag path: a `.mcap` or `.db3` file or a `rosbag2_*` directory (`peek_bag_samples` also reads ROS 1 `.bag`; `analyze_bag` does not). Existence and format are checked live; mock accepts any well-formed path.
countNoMessages to return, default 5, at most 50 (`health_check.max_sample_count`): more is capped to 50, negative raises an error. The result's `count` may be lower.
end_sNoOnly messages recorded before this bag-relative second (exclusive), default to the end.
topicYesFully qualified ROS 2 topic name starting with `/`, e.g. `/cmd_vel`. Segments are letters, digits and underscores (not starting with a digit); anything else is rejected before reaching the `ros2` CLI.
start_sNoOnly messages recorded at or after this many seconds from the bag's first message (`analyze_bag.start_ns`), default from the start.
min_log_levelNo`/rosout` (`rcl_interfaces/msg/Log`) only: keep messages at or above this level (debug 10, info 20, warn 30, error 40, fatal 50). Other topics: error.

Output Schema

NameRequiredDescriptionDefault
noteNoWhy `samples` is empty or limited, when not obvious; else `None`.
rateNoHow the topic delivered the messages, with a verdict; `null` for `peek_dds_samples`. Use `count` >= 10.
countYesNumber of samples in `samples`: fewer than requested when the topic gave fewer, never more than 50 (a larger request is capped to 50).
topicYes
samplesYesThe samples, in backend order.
mode_effectiveYes`live` or `mock` (fixtures).

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed46 schema fields changedv0.8.0
    • addedInput schema / properties / end_s
      Added value: +{
      +  "anyOf": [
      +    {
      +      "minimum": 0,
      +      "type": "number"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Only messages recorded before this bag-relative second (exclusive), default to the end."
      +}
    • addedInput schema / properties / min_log_level
      Added value: +{
      +  "anyOf": [
      +    {
      +      "enum": [
      +        "debug",
      +        "info",
      +        "warn",
      +        "error",
      +        "fatal"
      +      ],
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "`/rosout` (`rcl_interfaces/msg/Log`) only: keep messages at or above this level (debug 10, info 20, warn 30, error 40, fatal 50). Other topics: error."
      +}
    • addedInput schema / properties / node
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "`/rosout` only: keep logs of this node and its child loggers, e.g. `/robot1/driver` (also `driver`, `robot1.driver`). Exact name, never a prefix. Other topics: error."
      +}
    • addedInput schema / properties / start_s
      Added value: +{
      +  "anyOf": [
      +    {
      +      "minimum": 0,
      +      "type": "number"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Only messages recorded at or after this many seconds from the bag's first message (`analyze_bag.start_ns`), default from the start."
      +}
    • removedOutput schema / $defs / ImageSummary
      Removed value: -{
      -  "additionalProperties": false,
      -  "properties": {
      -    "data_length": {
      -      "anyOf": [
      -        {
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Pixel buffer size in bytes (see `data_length_basis`)."
      -    },
      -    "data_length_basis": {
      -      "anyOf": [
      -        {
      -          "enum": [
      -            "payload",
      -            "step_x_height"
      -          ],
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`payload` (counted) or `step_x_height` (array cut, so step times height)."
      -    },
      -    "encoding": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Pixel encoding, e.g. `rgb8`."
      -    },
      -    "frame_id": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`header.frame_id` of the camera."
      -    },
      -    "height": {
      -      "anyOf": [
      -        {
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Pixels."
      -    },
      -    "is_bigendian": {
      -      "anyOf": [
      -        {
      -          "type": "boolean"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Big-endian pixels."
      -    },
      -    "note": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "default": null,
      -      "description": "One sentence when partial; else `null`."
      -    },
      -    "step": {
      -      "anyOf": [
      -        {
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Row length, bytes."
      -    },
      -    "summary_type": {
      -      "const": "image",
      -      "type": "string"
      -    },
      -    "width": {
      -      "anyOf": [
      -        {
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Pixels."
      -    }
      -  },
      -  "required": [
      -    "summary_type",
      -    "frame_id",
      -    "width",
      -    "height",
      -    "encoding",
      -    "step",
      -    "is_bigendian",
      -    "data_length",
      -    "data_length_basis"
      -  ],
      -  "type": "object"
      -}
    • removedOutput schema / $defs / ImuSummary
      Removed value: -{
      -  "additionalProperties": false,
      -  "properties": {
      -    "angular_velocity_norm": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Norm of `angular_velocity`, rad/s."
      -    },
      -    "frame_id": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`header.frame_id` of the IMU."
      -    },
      -    "linear_acceleration_norm": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Norm of `linear_acceleration`, m/s^2."
      -    },
      -    "note": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "default": null,
      -      "description": "One sentence when a value is unusable; else `null`."
      -    },
      -    "orientation_valid": {
      -      "description": "False when the IMU gives no orientation: `roll`, `pitch`, `yaw` are `null`.",
      -      "type": "boolean"
      -    },
      -    "pitch": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Rotation about y, radians."
      -    },
      -    "roll": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Rotation about x, radians."
      -    },
      -    "summary_type": {
      -      "const": "imu",
      -      "type": "string"
      -    },
      -    "yaw": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Rotation about z, radians."
      -    }
      -  },
      -  "required": [
      -    "summary_type",
      -    "frame_id",
      -    "orientation_valid",
      -    "roll",
      -    "pitch",
      -    "yaw",
      -    "angular_velocity_norm",
      -    "linear_acceleration_norm"
      -  ],
      -  "type": "object"
      -}
    • removedOutput schema / $defs / LaserScanSummary
      Removed value: -{
      -  "additionalProperties": false,
      -  "properties": {
      -    "angle_increment": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Angle between beams, rad."
      -    },
      -    "angle_max": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Bearing of the last beam, rad."
      -    },
      -    "angle_min": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Bearing of beam 0, rad."
      -    },
      -    "beam_count": {
      -      "description": "Beams in `ranges`.",
      -      "minimum": 0,
      -      "type": "integer"
      -    },
      -    "closest_obstacle": {
      -      "anyOf": [
      -        {
      -          "$ref": "#/$defs/ScanReturn"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Closest valid return of the scan; `null` with `note`."
      -    },
      -    "finite_count": {
      -      "description": "Beams with a finite range.",
      -      "minimum": 0,
      -      "type": "integer"
      -    },
      -    "frame_id": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`header.frame_id`; every angle is in this sensor frame, not the base frame."
      -    },
      -    "inf_count": {
      -      "description": "Beams at `+inf`.",
      -      "minimum": 0,
      -      "type": "integer"
      -    },
      -    "nan_count": {
      -      "description": "Beams at `nan`.",
      -      "minimum": 0,
      -      "type": "integer"
      -    },
      -    "neg_inf_count": {
      -      "description": "Beams at `-inf`.",
      -      "minimum": 0,
      -      "type": "integer"
      -    },
      -    "note": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "default": null,
      -      "description": "One sentence when the summary is partial; else `null`."
      -    },
      -    "out_of_range_count": {
      -      "description": "Finite ranges outside `range_min`..`range_max` (not valid).",
      -      "minimum": 0,
      -      "type": "integer"
      -    },
      -    "range_max": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Largest valid range, m."
      -    },
      -    "range_min": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Smallest valid range, m."
      -    },
      -    "sectors": {
      -      "$ref": "#/$defs/ScanSectors",
      -      "description": "Closest valid return per sector."
      -    },
      -    "summary_type": {
      -      "const": "laser_scan",
      -      "type": "string"
      -    }
      -  },
      -  "required": [
      -    "summary_type",
      -    "frame_id",
      -    "beam_count",
      -    "angle_min",
      -    "angle_max",
      -    "angle_increment",
      -    "range_min",
      -    "range_max",
      -    "finite_count",
      -    "inf_count",
      -    "neg_inf_count",
      -    "nan_count",
      -    "out_of_range_count",
      -    "closest_obstacle",
      -    "sectors"
      -  ],
      -  "type": "object"
      -}
    • removedOutput schema / $defs / MessageSample / properties / received_ns / anyOf
      Removed value: -[
      -  {
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / MessageSample / properties / received_ns / default
      Removed value: -null
    • addedOutput schema / $defs / MessageSample / properties / received_ns / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedOutput schema / $defs / MessageSample / properties / recorded_ns / anyOf
      Removed value: -[
      -  {
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / MessageSample / properties / recorded_ns / default
      Removed value: -null
    • addedOutput schema / $defs / MessageSample / properties / recorded_ns / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedOutput schema / $defs / MessageSample / properties / summary / anyOf
      Removed value: -[
      -  {
      -    "discriminator": {
      -      "mapping": {
      -        "image": "#/$defs/ImageSummary",
      -        "imu": "#/$defs/ImuSummary",
      -        "laser_scan": "#/$defs/LaserScanSummary",
      -        "odometry": "#/$defs/OdometrySummary",
      -        "point_cloud2": "#/$defs/PointCloud2Summary"
      -      },
      -      "propertyName": "summary_type"
      -    },
      -    "oneOf": [
      -      {
      -        "$ref": "#/$defs/LaserScanSummary"
      -      },
      -      {
      -        "$ref": "#/$defs/OdometrySummary"
      -      },
      -      {
      -        "$ref": "#/$defs/ImuSummary"
      -      },
      -      {
      -        "$ref": "#/$defs/ImageSummary"
      -      },
      -      {
      -        "$ref": "#/$defs/PointCloud2Summary"
      -      }
      -    ]
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / MessageSample / properties / summary / default
      Removed value: -null
    • addedOutput schema / $defs / MessageSample / properties / summary / properties
      Added value: +{
      +  "summary_type": {
      +    "enum": [
      +      "laser_scan",
      +      "odometry",
      +      "imu",
      +      "image",
      +      "point_cloud2"
      +    ],
      +    "type": "string"
      +  }
      +}
    • addedOutput schema / $defs / MessageSample / properties / summary / required
      Added value: +[
      +  "summary_type"
      +]
    • addedOutput schema / $defs / MessageSample / properties / summary / type
      Added value: +[
      +  "object",
      +  "null"
      +]
    • removedOutput schema / $defs / OdometrySummary
      Removed value: -{
      -  "additionalProperties": false,
      -  "properties": {
      -    "angular_z": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`twist.twist.angular.z`, rad/s."
      -    },
      -    "child_frame_id": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`child_frame_id` (frame of the twist)."
      -    },
      -    "frame_id": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`header.frame_id` (frame of the pose)."
      -    },
      -    "linear_speed": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Norm of `twist.twist.linear`, m/s."
      -    },
      -    "note": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "default": null,
      -      "description": "One sentence when a value is missing; else `null`."
      -    },
      -    "position_x": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`pose.pose.position.x`, m."
      -    },
      -    "position_y": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`pose.pose.position.y`, m."
      -    },
      -    "position_z": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`pose.pose.position.z`, m."
      -    },
      -    "summary_type": {
      -      "const": "odometry",
      -      "type": "string"
      -    },
      -    "yaw": {
      -      "anyOf": [
      -        {
      -          "type": "number"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Heading from the pose quaternion, radians."
      -    }
      -  },
      -  "required": [
      -    "summary_type",
      -    "frame_id",
      -    "child_frame_id",
      -    "linear_speed",
      -    "angular_z",
      -    "position_x",
      -    "position_y",
      -    "position_z",
      -    "yaw"
      -  ],
      -  "type": "object"
      -}
    • removedOutput schema / $defs / PointCloud2Summary
      Removed value: -{
      -  "additionalProperties": false,
      -  "properties": {
      -    "fields": {
      -      "description": "Point layout.",
      -      "items": {
      -        "$ref": "#/$defs/PointFieldSummary"
      -      },
      -      "type": "array"
      -    },
      -    "frame_id": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`header.frame_id` of the cloud."
      -    },
      -    "height": {
      -      "anyOf": [
      -        {
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Rows (1: unorganized)."
      -    },
      -    "is_dense": {
      -      "anyOf": [
      -        {
      -          "type": "boolean"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "True when no point is invalid."
      -    },
      -    "note": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "default": null,
      -      "description": "One sentence when partial; else `null`."
      -    },
      -    "point_count": {
      -      "anyOf": [
      -        {
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "`width` times `height`."
      -    },
      -    "point_step": {
      -      "anyOf": [
      -        {
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Bytes per point."
      -    },
      -    "row_step": {
      -      "anyOf": [
      -        {
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Bytes per row."
      -    },
      -    "summary_type": {
      -      "const": "point_cloud2",
      -      "type": "string"
      -    },
      -    "width": {
      -      "anyOf": [
      -        {
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Points per row."
      -    }
      -  },
      -  "required": [
      -    "summary_type",
      -    "frame_id",
      -    "width",
      -    "height",
      -    "point_count",
      -    "point_step",
      -    "row_step",
      -    "is_dense",
      -    "fields"
      -  ],
      -  "type": "object"
      -}
    • removedOutput schema / $defs / PointFieldSummary
      Removed value: -{
      -  "additionalProperties": false,
      -  "properties": {
      -    "count": {
      -      "anyOf": [
      -        {
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Elements per point."
      -    },
      -    "datatype": {
      -      "description": "Element type, e.g. `FLOAT32`.",
      -      "type": "string"
      -    },
      -    "name": {
      -      "description": "Field name, e.g. `x`.",
      -      "type": "string"
      -    },
      -    "offset": {
      -      "anyOf": [
      -        {
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Byte offset in a point."
      -    }
      -  },
      -  "required": [
      -    "name",
      -    "datatype",
      -    "offset",
      -    "count"
      -  ],
      -  "type": "object"
      -}
    • removedOutput schema / $defs / ScanReturn
      Removed value: -{
      -  "additionalProperties": false,
      -  "properties": {
      -    "beam_index": {
      -      "description": "Index in `ranges`.",
      -      "minimum": 0,
      -      "type": "integer"
      -    },
      -    "bearing": {
      -      "description": "Beam direction in radians, sensor frame: 0 is +x, counter-clockwise positive.",
      -      "type": "number"
      -    },
      -    "range": {
      -      "description": "Distance, meters.",
      -      "type": "number"
      -    }
      -  },
      -  "required": [
      -    "range",
      -    "bearing",
      -    "beam_index"
      -  ],
      -  "type": "object"
      -}
    • removedOutput schema / $defs / ScanSector
      Removed value: -{
      -  "additionalProperties": false,
      -  "properties": {
      -    "beam_count": {
      -      "description": "Beams inside the sector (0: not covered).",
      -      "minimum": 0,
      -      "type": "integer"
      -    },
      -    "closest": {
      -      "anyOf": [
      -        {
      -          "$ref": "#/$defs/ScanReturn"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "description": "Closest valid return in the sector; `null` with `note`."
      -    },
      -    "note": {
      -      "anyOf": [
      -        {
      -          "type": "string"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "default": null,
      -      "description": "Why `closest` is `null`; `null` when it is set."
      -    }
      -  },
      -  "required": [
      -    "beam_count",
      -    "closest"
      -  ],
      -  "type": "object"
      -}
    • removedOutput schema / $defs / ScanSectors
      Removed value: -{
      -  "additionalProperties": false,
      -  "properties": {
      -    "front": {
      -      "$ref": "#/$defs/ScanSector",
      -      "description": "Bearings within +-45 degrees of +x."
      -    },
      -    "left": {
      -      "$ref": "#/$defs/ScanSector",
      -      "description": "Bearings from 45 to 135 degrees."
      -    },
      -    "rear": {
      -      "$ref": "#/$defs/ScanSector",
      -      "description": "Bearings beyond +-135 degrees."
      -    },
      -    "right": {
      -      "$ref": "#/$defs/ScanSector",
      -      "description": "Bearings from -135 to -45 degrees."
      -    }
      -  },
      -  "required": [
      -    "front",
      -    "left",
      -    "right",
      -    "rear"
      -  ],
      -  "type": "object"
      -}
    • addedOutput schema / $defs / TopicRate / properties / clock_basis
      Added value: +{
      +  "description": "Stamp clock: `wall` (stamps are dates), `sim` (since a start), `none` (no stamps), `mixed` (several kinds, e.g. two publishers).",
      +  "enum": [
      +    "wall",
      +    "sim",
      +    "none",
      +    "mixed"
      +  ],
      +  "type": "string"
      +}
    • removedOutput schema / $defs / TopicRate / properties / interval_cv / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / $defs / TopicRate / properties / interval_cv / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedOutput schema / $defs / TopicRate / properties / interval_median_s / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / $defs / TopicRate / properties / interval_median_s / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedOutput schema / $defs / TopicRate / properties / max_gap_s / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / $defs / TopicRate / properties / max_gap_s / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedOutput schema / $defs / TopicRate / properties / mean_interval_s / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / $defs / TopicRate / properties / mean_interval_s / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedOutput schema / $defs / TopicRate / properties / observed_frequency_hz / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / $defs / TopicRate / properties / observed_frequency_hz / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • removedOutput schema / $defs / TopicRate / properties / sim_frequency_hz / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / $defs / TopicRate / properties / sim_frequency_hz / type
      Added value: +[
      +  "number",
      +  "null"
      +]
    • addedOutput schema / $defs / TopicRate / properties / stamp_age_median_s
      Added value: +{
      +  "description": "Median of arrival minus stamp, s, if `wall`; negative = publisher ahead.",
      +  "type": [
      +    "number",
      +    "null"
      +  ]
      +}
    • addedOutput schema / $defs / TopicRate / properties / stamp_age_p95_s
      Added value: +{
      +  "description": "95th percentile of that age (max under 20 messages).",
      +  "type": [
      +    "number",
      +    "null"
      +  ]
      +}
    • addedOutput schema / $defs / TopicRate / properties / stamp_regression_count
      Added value: +{
      +  "description": "Times a stamp went back versus the previous one.",
      +  "minimum": 0,
      +  "type": "integer"
      +}
    • removedOutput schema / $defs / TopicRate / properties / trailing_gap_s / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / $defs / TopicRate / properties / trailing_gap_s / type
      Added value: +[
      +  "number",
      +  "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 / rate / default
      Removed value: -null
  2. Changed51 schema fields changedv0.7.1
    • changedInput schema / properties / count / description
      Previous value: -"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 an error. The returned `SampleResult.count` reflects the actual number of samples produced: it can be lower than the request (empty topic, timeout, mock fixture shorter than requested)."New value: +"Messages to return, default 5, at most 50 (`health_check.max_sample_count`): more is capped to 50, negative raises an error. The result's `count` may be lower."
    • removedInput schema / properties / count / title
      Removed value: -"Count"
    • changedInput schema / properties / path / description
      Previous value: -"Path to a bag: a file ending in `.mcap` or `.db3`, or a `rosbag2_*` directory. `peek_bag_samples` also reads ROS 1 `.bag` files; `analyze_bag` does not (`ros2 bag info` cannot open them). Leading/trailing whitespace is stripped. Null bytes and otherwise malformed filesystem paths are rejected. Existence and bag format are validated by the live adapter (mock mode accepts any well-formed path)."New value: +"Bag path: a `.mcap` or `.db3` file or a `rosbag2_*` directory (`peek_bag_samples` also reads ROS 1 `.bag`; `analyze_bag` does not). Existence and format are checked live; mock accepts any well-formed path."
    • removedInput schema / properties / path / title
      Removed value: -"Path"
    • changedInput schema / properties / topic / description
      Previous value: -"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 underscores; everything else (whitespace, quotes, shell metacharacters, `//`, trailing `/`) is rejected before reaching the `ros2` CLI."New value: +"Fully qualified ROS 2 topic name starting with `/`, e.g. `/cmd_vel`. Segments are letters, digits and underscores (not starting with a digit); anything else is rejected before reaching the `ros2` CLI."
    • removedInput schema / properties / topic / title
      Removed value: -"Topic"
    • removedInput schema / title
      Removed value: -"peek_bag_samplesArguments"
    • addedOutput schema / $defs / ImageSummary
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "data_length": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Pixel buffer size in bytes (see `data_length_basis`)."
      +    },
      +    "data_length_basis": {
      +      "anyOf": [
      +        {
      +          "enum": [
      +            "payload",
      +            "step_x_height"
      +          ],
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`payload` (counted) or `step_x_height` (array cut, so step times height)."
      +    },
      +    "encoding": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Pixel encoding, e.g. `rgb8`."
      +    },
      +    "frame_id": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`header.frame_id` of the camera."
      +    },
      +    "height": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Pixels."
      +    },
      +    "is_bigendian": {
      +      "anyOf": [
      +        {
      +          "type": "boolean"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Big-endian pixels."
      +    },
      +    "note": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "One sentence when partial; else `null`."
      +    },
      +    "step": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Row length, bytes."
      +    },
      +    "summary_type": {
      +      "const": "image",
      +      "type": "string"
      +    },
      +    "width": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Pixels."
      +    }
      +  },
      +  "required": [
      +    "summary_type",
      +    "frame_id",
      +    "width",
      +    "height",
      +    "encoding",
      +    "step",
      +    "is_bigendian",
      +    "data_length",
      +    "data_length_basis"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / $defs / ImuSummary
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "angular_velocity_norm": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Norm of `angular_velocity`, rad/s."
      +    },
      +    "frame_id": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`header.frame_id` of the IMU."
      +    },
      +    "linear_acceleration_norm": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Norm of `linear_acceleration`, m/s^2."
      +    },
      +    "note": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "One sentence when a value is unusable; else `null`."
      +    },
      +    "orientation_valid": {
      +      "description": "False when the IMU gives no orientation: `roll`, `pitch`, `yaw` are `null`.",
      +      "type": "boolean"
      +    },
      +    "pitch": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Rotation about y, radians."
      +    },
      +    "roll": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Rotation about x, radians."
      +    },
      +    "summary_type": {
      +      "const": "imu",
      +      "type": "string"
      +    },
      +    "yaw": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Rotation about z, radians."
      +    }
      +  },
      +  "required": [
      +    "summary_type",
      +    "frame_id",
      +    "orientation_valid",
      +    "roll",
      +    "pitch",
      +    "yaw",
      +    "angular_velocity_norm",
      +    "linear_acceleration_norm"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / $defs / LaserScanSummary
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "angle_increment": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Angle between beams, rad."
      +    },
      +    "angle_max": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Bearing of the last beam, rad."
      +    },
      +    "angle_min": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Bearing of beam 0, rad."
      +    },
      +    "beam_count": {
      +      "description": "Beams in `ranges`.",
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "closest_obstacle": {
      +      "anyOf": [
      +        {
      +          "$ref": "#/$defs/ScanReturn"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Closest valid return of the scan; `null` with `note`."
      +    },
      +    "finite_count": {
      +      "description": "Beams with a finite range.",
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "frame_id": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`header.frame_id`; every angle is in this sensor frame, not the base frame."
      +    },
      +    "inf_count": {
      +      "description": "Beams at `+inf`.",
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "nan_count": {
      +      "description": "Beams at `nan`.",
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "neg_inf_count": {
      +      "description": "Beams at `-inf`.",
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "note": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "One sentence when the summary is partial; else `null`."
      +    },
      +    "out_of_range_count": {
      +      "description": "Finite ranges outside `range_min`..`range_max` (not valid).",
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "range_max": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Largest valid range, m."
      +    },
      +    "range_min": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Smallest valid range, m."
      +    },
      +    "sectors": {
      +      "$ref": "#/$defs/ScanSectors",
      +      "description": "Closest valid return per sector."
      +    },
      +    "summary_type": {
      +      "const": "laser_scan",
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "summary_type",
      +    "frame_id",
      +    "beam_count",
      +    "angle_min",
      +    "angle_max",
      +    "angle_increment",
      +    "range_min",
      +    "range_max",
      +    "finite_count",
      +    "inf_count",
      +    "neg_inf_count",
      +    "nan_count",
      +    "out_of_range_count",
      +    "closest_obstacle",
      +    "sectors"
      +  ],
      +  "type": "object"
      +}
    • removedOutput schema / $defs / MessageSample / description
      Removed value: -"A single sampled message on a topic."
    • removedOutput schema / $defs / MessageSample / properties / message_type / description
      Removed value: -"ROS2 message type of this sample, e.g. `geometry_msgs/msg/Twist`."
    • removedOutput schema / $defs / MessageSample / properties / message_type / title
      Removed value: -"Message Type"
    • changedOutput schema / $defs / MessageSample / properties / payload / description
      Previous value: -"Message fields as nested named values, e.g. `{\"header\": {\"stamp\": {\"sec\": 12, \"nanosec\": 5}, \"frame_id\": \"base_laser\"}, \"ranges\": [1.0, 1.001]}`. Non-finite floats are the strings `nan`, `inf`, `-inf`. Live `sample_messages` adds `_truncated_fields` (dotted paths of arrays, strings or bytes cut at `max_array_length`; a string of exactly that length plus `...` that was not cut is listed too) when something was cut, and `_raw_text` (the unparsed output) only when a message could not be parsed. With `arrays_summary_only` an array is a string like `<sequence type: float, length: 541>`. Large messages may be dropped to keep tool output bounded."New value: +"Message fields as nested values; `nan`/`inf` are strings. Live `sample_messages` adds `_truncated_fields` (cut paths) and `_raw_text` (unparsable output)."
    • removedOutput schema / $defs / MessageSample / properties / payload / title
      Removed value: -"Payload"
    • changedOutput schema / $defs / MessageSample / properties / received_ns / description
      Previous value: -"Wall-clock nanoseconds since epoch at which the `ros2` CLI printed this message, after it deserialized and formatted it: not the network receive time, and it lags more for large messages. Accurate to a fraction of a second. Live `sample_messages` only; `None` otherwise."New value: +"Wall-clock ns when the `ros2` CLI printed the message (live `sample_messages` only)."
    • removedOutput schema / $defs / MessageSample / properties / received_ns / title
      Removed value: -"Received Ns"
    • changedOutput schema / $defs / MessageSample / properties / recorded_ns / description
      Previous value: -"Nanoseconds at which the recorder wrote this message into the bag (the bag's own record time, wall clock unless the recorder ran on simulated time). `peek_bag_samples` only; `None` for live samples, where `received_ns` plays that role."New value: +"Bag record time in ns (`peek_bag_samples` only)."
    • removedOutput schema / $defs / MessageSample / properties / recorded_ns / title
      Removed value: -"Recorded Ns"
    • removedOutput schema / $defs / MessageSample / properties / stamp_source / anyOf
      Removed value: -[
      -  {
      -    "enum": [
      -      "header",
      -      "none",
      -      "recorded"
      -    ],
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / $defs / MessageSample / properties / stamp_source / default
      Removed value: -null
    • changedOutput schema / $defs / MessageSample / properties / stamp_source / description
      Previous value: -"Where `timestamp_ns` comes from: `header` (the message's `header.stamp`), `none` (headerless live message, `timestamp_ns` is 0) or `recorded` (headerless message from a bag, `timestamp_ns` is the bag record time). `None` when the backend does not say."New value: +"Origin of `timestamp_ns`: `header`, `payload`, `recorded` (bag record time), `dds_source` or `none` (0)."
    • addedOutput schema / $defs / MessageSample / properties / stamp_source / enum
      Added value: +[
      +  "header",
      +  "payload",
      +  "recorded",
      +  "dds_source",
      +  "none"
      +]
    • removedOutput schema / $defs / MessageSample / properties / stamp_source / title
      Removed value: -"Stamp Source"
    • addedOutput schema / $defs / MessageSample / properties / stamp_source / type
      Added value: +"string"
    • addedOutput schema / $defs / MessageSample / properties / summary
      Added value: +{
      +  "anyOf": [
      +    {
      +      "discriminator": {
      +        "mapping": {
      +          "image": "#/$defs/ImageSummary",
      +          "imu": "#/$defs/ImuSummary",
      +          "laser_scan": "#/$defs/LaserScanSummary",
      +          "odometry": "#/$defs/OdometrySummary",
      +          "point_cloud2": "#/$defs/PointCloud2Summary"
      +        },
      +        "propertyName": "summary_type"
      +      },
      +      "oneOf": [
      +        {
      +          "$ref": "#/$defs/LaserScanSummary"
      +        },
      +        {
      +          "$ref": "#/$defs/OdometrySummary"
      +        },
      +        {
      +          "$ref": "#/$defs/ImuSummary"
      +        },
      +        {
      +          "$ref": "#/$defs/ImageSummary"
      +        },
      +        {
      +          "$ref": "#/$defs/PointCloud2Summary"
      +        }
      +      ]
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Condensed reading of the whole message (see `summary_type`); `null` for other types and DDS peeks."
      +}
    • changedOutput schema / $defs / MessageSample / properties / timestamp_ns / description
      Previous value: -"Nanoseconds from the message's own top-level `header.stamp` (`stamp_source` `header`), or 0 when the message has no header (`stamp_source` `none`, e.g. `std_msgs/String`, `geometry_msgs/Twist`). It is the publisher's clock, not the arrival time: on simulated time it is sim time since the simulation started (a few seconds, not a date) and 0 is a valid value. From `peek_bag_samples` a message without a header gets the bag record time instead (`stamp_source` `recorded`), also given in `recorded_ns`. Use `received_ns` for when the CLI printed the message. Mock samples with a header carry synthetic increasing values."New value: +"Publisher-clock ns: `header.stamp`, else the time in the body of `Clock`, `TFMessage` or `Log`, else 0 (see `stamp_source`); not the arrival time."
    • removedOutput schema / $defs / MessageSample / properties / timestamp_ns / title
      Removed value: -"Timestamp Ns"
    • removedOutput schema / $defs / MessageSample / properties / topic / description
      Removed value: -"Fully qualified topic name the sample was taken from, e.g. `/cmd_vel`."
    • removedOutput schema / $defs / MessageSample / properties / topic / title
      Removed value: -"Topic"
    • changedOutput schema / $defs / MessageSample / required
      Previous value: -[
      -  "topic",
      -  "message_type",
      -  "timestamp_ns"
      -]New value: +[
      +  "topic",
      +  "message_type",
      +  "timestamp_ns",
      +  "stamp_source"
      +]
    • removedOutput schema / $defs / MessageSample / title
      Removed value: -"MessageSample"
    • addedOutput schema / $defs / OdometrySummary
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "angular_z": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`twist.twist.angular.z`, rad/s."
      +    },
      +    "child_frame_id": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`child_frame_id` (frame of the twist)."
      +    },
      +    "frame_id": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`header.frame_id` (frame of the pose)."
      +    },
      +    "linear_speed": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Norm of `twist.twist.linear`, m/s."
      +    },
      +    "note": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "One sentence when a value is missing; else `null`."
      +    },
      +    "position_x": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`pose.pose.position.x`, m."
      +    },
      +    "position_y": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`pose.pose.position.y`, m."
      +    },
      +    "position_z": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`pose.pose.position.z`, m."
      +    },
      +    "summary_type": {
      +      "const": "odometry",
      +      "type": "string"
      +    },
      +    "yaw": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Heading from the pose quaternion, radians."
      +    }
      +  },
      +  "required": [
      +    "summary_type",
      +    "frame_id",
      +    "child_frame_id",
      +    "linear_speed",
      +    "angular_z",
      +    "position_x",
      +    "position_y",
      +    "position_z",
      +    "yaw"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / $defs / PointCloud2Summary
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "fields": {
      +      "description": "Point layout.",
      +      "items": {
      +        "$ref": "#/$defs/PointFieldSummary"
      +      },
      +      "type": "array"
      +    },
      +    "frame_id": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`header.frame_id` of the cloud."
      +    },
      +    "height": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Rows (1: unorganized)."
      +    },
      +    "is_dense": {
      +      "anyOf": [
      +        {
      +          "type": "boolean"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "True when no point is invalid."
      +    },
      +    "note": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "One sentence when partial; else `null`."
      +    },
      +    "point_count": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "`width` times `height`."
      +    },
      +    "point_step": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Bytes per point."
      +    },
      +    "row_step": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Bytes per row."
      +    },
      +    "summary_type": {
      +      "const": "point_cloud2",
      +      "type": "string"
      +    },
      +    "width": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Points per row."
      +    }
      +  },
      +  "required": [
      +    "summary_type",
      +    "frame_id",
      +    "width",
      +    "height",
      +    "point_count",
      +    "point_step",
      +    "row_step",
      +    "is_dense",
      +    "fields"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / $defs / PointFieldSummary
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "count": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Elements per point."
      +    },
      +    "datatype": {
      +      "description": "Element type, e.g. `FLOAT32`.",
      +      "type": "string"
      +    },
      +    "name": {
      +      "description": "Field name, e.g. `x`.",
      +      "type": "string"
      +    },
      +    "offset": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Byte offset in a point."
      +    }
      +  },
      +  "required": [
      +    "name",
      +    "datatype",
      +    "offset",
      +    "count"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / $defs / ScanReturn
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "beam_index": {
      +      "description": "Index in `ranges`.",
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "bearing": {
      +      "description": "Beam direction in radians, sensor frame: 0 is +x, counter-clockwise positive.",
      +      "type": "number"
      +    },
      +    "range": {
      +      "description": "Distance, meters.",
      +      "type": "number"
      +    }
      +  },
      +  "required": [
      +    "range",
      +    "bearing",
      +    "beam_index"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / $defs / ScanSector
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "beam_count": {
      +      "description": "Beams inside the sector (0: not covered).",
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "closest": {
      +      "anyOf": [
      +        {
      +          "$ref": "#/$defs/ScanReturn"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Closest valid return in the sector; `null` with `note`."
      +    },
      +    "note": {
      +      "anyOf": [
      +        {
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "Why `closest` is `null`; `null` when it is set."
      +    }
      +  },
      +  "required": [
      +    "beam_count",
      +    "closest"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / $defs / ScanSectors
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "front": {
      +      "$ref": "#/$defs/ScanSector",
      +      "description": "Bearings within +-45 degrees of +x."
      +    },
      +    "left": {
      +      "$ref": "#/$defs/ScanSector",
      +      "description": "Bearings from 45 to 135 degrees."
      +    },
      +    "rear": {
      +      "$ref": "#/$defs/ScanSector",
      +      "description": "Bearings beyond +-135 degrees."
      +    },
      +    "right": {
      +      "$ref": "#/$defs/ScanSector",
      +      "description": "Bearings from -135 to -45 degrees."
      +    }
      +  },
      +  "required": [
      +    "front",
      +    "left",
      +    "right",
      +    "rear"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / $defs / TopicRate
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "basis": {
      +      "description": "Clock the rate is measured on: `received_ns` (live) or `recorded_ns` (bag).",
      +      "enum": [
      +        "received_ns",
      +        "recorded_ns"
      +      ],
      +      "type": "string"
      +    },
      +    "interval_cv": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Interval standard deviation over mean; `null` under 2 messages."
      +    },
      +    "interval_median_s": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Median interval; `null` under 2 messages."
      +    },
      +    "max_gap_s": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Longest interval; `null` under 2 messages."
      +    },
      +    "mean_interval_s": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Mean interval; `null` under 2 messages."
      +    },
      +    "message_count": {
      +      "description": "Messages the rate is computed from.",
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "observed_frequency_hz": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Messages per second on `basis`, drain excluded; the rate the caller received. `null` under 2 messages."
      +    },
      +    "sim_frequency_hz": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Same rate on the messages' own stamps (publisher clock); `null` without advancing stamps."
      +    },
      +    "startup_burst_count": {
      +      "description": "Leading messages set aside as a start-up drain (live only); still in `message_count`.",
      +      "minimum": 0,
      +      "type": "integer"
      +    },
      +    "trailing_gap_s": {
      +      "anyOf": [
      +        {
      +          "type": "number"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "description": "Time from the last message to the end of a live observation; else `null`."
      +    },
      +    "verdict": {
      +      "description": "First match: `silent` (none), `insufficient` (under 5), `intermittent` (an interval over 3x the median), `stable` (cv < 0.2), `jittery` (< 0.5), `erratic`. Describes this window, not a diagnosis.",
      +      "enum": [
      +        "silent",
      +        "insufficient",
      +        "intermittent",
      +        "stable",
      +        "jittery",
      +        "erratic"
      +      ],
      +      "type": "string"
      +    },
      +    "verdict_note": {
      +      "description": "The verdict in plain words.",
      +      "type": "string"
      +    },
      +    "window_s": {
      +      "description": "Observation length, seconds.",
      +      "minimum": 0,
      +      "type": "number"
      +    }
      +  },
      +  "required": [
      +    "basis",
      +    "message_count",
      +    "startup_burst_count",
      +    "window_s",
      +    "mean_interval_s",
      +    "interval_median_s",
      +    "max_gap_s",
      +    "interval_cv",
      +    "observed_frequency_hz",
      +    "sim_frequency_hz",
      +    "trailing_gap_s",
      +    "verdict",
      +    "verdict_note"
      +  ],
      +  "type": "object"
      +}
    • changedOutput schema / description
      Previous value: -"Envelope returned by the `sample_messages` tool."New value: +"Samples returned by `sample_messages`, `peek_dds_samples` and `peek_bag_samples`."
    • changedOutput schema / properties / count / description
      Previous value: -"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 the the silent maximum of 50: request `count > 50` and you will receive at most 50 without warning."New value: +"Number of samples in `samples`: fewer than requested when the topic gave fewer, never more than 50 (a larger request is capped to 50)."
    • removedOutput schema / properties / count / title
      Removed value: -"Count"
    • 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"
    • changedOutput schema / properties / note / description
      Previous value: -"Why `samples` is empty or limited, when the cause is not obvious (for example payload decoding is disabled for DDS user topics). `None` when there is nothing to add."New value: +"Why `samples` is empty or limited, when not obvious; else `None`."
    • removedOutput schema / properties / note / title
      Removed value: -"Note"
    • addedOutput schema / properties / rate
      Added value: +{
      +  "anyOf": [
      +    {
      +      "$ref": "#/$defs/TopicRate"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "How the topic delivered the messages, with a verdict; `null` for `peek_dds_samples`. Use `count` >= 10."
      +}
    • changedOutput schema / properties / samples / description
      Previous value: -"The sampled messages, ordered as received from the backend."New value: +"The samples, in backend order."
    • removedOutput schema / properties / samples / title
      Removed value: -"Samples"
    • removedOutput schema / properties / topic / description
      Removed value: -"Topic the samples were taken from, echoed from the request."
    • removedOutput schema / properties / topic / title
      Removed value: -"Topic"
  3. Changed5 schema fields changedv0.6.3
    • changedOutput schema / $defs / MessageSample / properties / payload / description
      Previous value: -"Structured message payload. **In live mode the parser exposes the message fields as positional CSV columns** keyed as `col_0`, `col_1`, ... (`header.stamp.sec`/`nanosec` are stripped out into `timestamp_ns` when detected). The raw CSV row is preserved verbatim under the reserved `_raw_text` key so clients can re-parse against the message's IDL when needed. In mock mode the payload is fully structured and `_raw_text` is absent. Large messages (e.g. images) may be summarized to keep tool output bounded."New value: +"Message fields as nested named values, e.g. `{\"header\": {\"stamp\": {\"sec\": 12, \"nanosec\": 5}, \"frame_id\": \"base_laser\"}, \"ranges\": [1.0, 1.001]}`. Non-finite floats are the strings `nan`, `inf`, `-inf`. Live `sample_messages` adds `_truncated_fields` (dotted paths of arrays, strings or bytes cut at `max_array_length`; a string of exactly that length plus `...` that was not cut is listed too) when something was cut, and `_raw_text` (the unparsed output) only when a message could not be parsed. With `arrays_summary_only` an array is a string like `<sequence type: float, length: 541>`. Large messages may be dropped to keep tool output bounded."
    • addedOutput schema / $defs / MessageSample / properties / received_ns
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "integer"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Wall-clock nanoseconds since epoch at which the `ros2` CLI printed this message, after it deserialized and formatted it: not the network receive time, and it lags more for large messages. Accurate to a fraction of a second. Live `sample_messages` only; `None` otherwise.",
      +  "title": "Received Ns"
      +}
    • addedOutput schema / $defs / MessageSample / properties / recorded_ns
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "integer"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Nanoseconds at which the recorder wrote this message into the bag (the bag's own record time, wall clock unless the recorder ran on simulated time). `peek_bag_samples` only; `None` for live samples, where `received_ns` plays that role.",
      +  "title": "Recorded Ns"
      +}
    • addedOutput schema / $defs / MessageSample / properties / stamp_source
      Added value: +{
      +  "anyOf": [
      +    {
      +      "enum": [
      +        "header",
      +        "none",
      +        "recorded"
      +      ],
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Where `timestamp_ns` comes from: `header` (the message's `header.stamp`), `none` (headerless live message, `timestamp_ns` is 0) or `recorded` (headerless message from a bag, `timestamp_ns` is the bag record time). `None` when the backend does not say.",
      +  "title": "Stamp Source"
      +}
    • changedOutput schema / $defs / MessageSample / properties / timestamp_ns / description
      Previous value: -"Timestamp in nanoseconds since epoch. In live mode this is the `header.stamp` of the sampled message when present: the live adapter invokes `ros2 topic echo --csv --once`, whose flattened CSV exposes `header.stamp.sec`/`nanosec` as the first two columns for any `Header`-stamped message. **Headerless message types** (e.g. `std_msgs/String`, `geometry_msgs/Twist`) carry no embedded timestamp, and `timestamp_ns` falls back to 0; an rclpy-backed adapter will eventually expose rmw receive times for those. Mock mode emits monotonically increasing values for deterministic ordering."New value: +"Nanoseconds from the message's own top-level `header.stamp` (`stamp_source` `header`), or 0 when the message has no header (`stamp_source` `none`, e.g. `std_msgs/String`, `geometry_msgs/Twist`). It is the publisher's clock, not the arrival time: on simulated time it is sim time since the simulation started (a few seconds, not a date) and 0 is a valid value. From `peek_bag_samples` a message without a header gets the bag record time instead (`stamp_source` `recorded`), also given in `recorded_ns`. Use `received_ns` for when the CLI printed the message. Mock samples with a header carry synthetic increasing values."
  4. First observedv0.5.6

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already declare read-only/idempotent/closed-world, and the description adds substantial context beyond them: which error conditions raise an MCP error, that it runs without a lock while other calls are busy, that it needs the ROS 2 side of the runtime, decode-status semantics, clamping behavior, and rate/verdict caveats for multi-publisher topics. Nothing contradicts the annotations — 'read-only by architecture' and offline/mock behavior match readOnlyHint and openWorldHint=false.

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?

Front-loaded and well-organized: purpose and null-hypothesis sibling contrast come first, then time range, clocks, errors, rate, and duration in ordered blocks. It is dense and slightly over-stuffed — the clock-resolution and duration paragraphs border on reference documentation — but almost every sentence carries invocation-relevant information, and repeated facts (count default/clamp) are a minor redundancy with the schema.

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 7-parameter tool with an output schema, the description is more than complete: it explains result fields (`_decode_status`, `timestamp_ns` sources, `summary`, `rate`), failure modes, and environment prerequisites, so an agent can predict both success and error behavior. Nothing needed to call it correctly is missing.

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; the description earns extra credit by explaining cross-parameter semantics the schema states only piecewise — that `min_log_level`/`node` apply only on `/rosout` and jointly keep the first `count` matching logs within the `start_s`/`end_s` window, and that `start_s` is measured from the bag's first message. Remaining parameters (path, topic) get no added meaning beyond schema text.

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 first sentence names a specific verb and resource (peek samples from a recorded bag file) and immediately scopes it as offline/post-mortem, explicitly contrasting with `peek_dds_samples` and `sample_messages` which are live. An agent can distinguish it from every sibling without opening a schema.

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

Usage Guidelines5/5

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

It states when to use this tool (offline post-mortem on recorded bags) versus the live alternatives by name, and notes the ordering contract (first `count` messages, not newest like `sample_messages`). It also flags the ROS 1 `.bag` support that `analyze_bag` lacks, giving an explicit selection criterion between the two bag readers.

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