Skip to main content
Glama

search_messages

Find Kafka messages by JavaScript predicate over key, value, headers, offset, timestamp, or schema fields; group matches into buckets, count them, or export results to a file.

Instructions

Search message key, value, headers or metadata with a JavaScript predicate. The script returns true for a match and receives value (parsed JSON, a decoded Avro/Protobuf/JSON Schema record, or text), key, headers, partition, offset, timestamp, value_bytes, key_bytes, format, schema_id and decode_error. Omit it to match all messages. Schema-encoded values are searched by field exactly like JSON.

group_by summarises instead of listing: an expression over the same variables returns a bucket name, and every match in the range is counted per bucket with one example address each. Use it to break a DLQ down by error header, find hot keys on a partition, or count messages per schema_id.

Every partition is read together, one chunk deep at a time, so a limited newest-first search returns the newest matches in the topic rather than the newest in whichever partition was read first. Kafka orders records only within a partition, so matches are merged by timestamp; producers set timestamps unless the topic uses LogAppendTime.

Kafka has no server-side search, so scans are bounded. Check complete, stopped_reason and scanned_ranges before treating no matches as conclusive. Use count_only for large result sets, and download to save them to a file on the user's machine without storing anything on the server: over stdio the file is saved at once, and over HTTP the response is a link and a command to run there, the search streaming every match when it is fetched. A script that runs past timeout_seconds is interrupted and counted in script_errors; scripts are not memory-sandboxed, so keep predicates simple.

Input Schema

NameRequiredDescriptionDefault
topicYesTopic to search. Matched exactly and case-sensitively.
scriptNoOptional JavaScript that decides whether a message matches. Return true to keep it. In scope: value (the parsed document for JSON, and the decoded record for Avro, Protobuf, JSON Schema or a configured format; the raw text otherwise), key (string, decoded document when the key has a schema, or null), headers (object of header name to string), partition, offset, timestamp (a Date), value_bytes and key_bytes (raw sizes, before decoding), format (json, avro, protobuf, json_schema, msgpack, text, binary or null), schema_id (number or null) and decode_error (string or null). Examples: return key === 'order-123'; return value.eventType === 'NEW' && value.payload.amount >= 500; return value.payload.cancelledAt === null. Omit to match every message.
downloadNoOptional. Instead of returning matches, save every match to a file on the user's own machine. Over stdio the file is saved right away and download.path names it. Over HTTP the response is a link and a command, e.g. curl, to run on the user's machine; the search runs when the link is fetched. Nothing is stored on the server. Use it instead of returning thousands of messages, or whenever the user wants the messages in a file.
group_byNoOptional JavaScript expression, with the same variables as script, that returns a bucket name for each matching message, e.g. return headers['error-reason'] or return key or return schema_id. Every match in the range is counted per bucket, like count_only, and no message bodies are returned. null and undefined form the bucket null. Cannot be combined with download.
directionNoOptional scan direction: newest_first (default) or oldest_first. Decides which matches are kept when max_matches cuts the search short. Every partition is read together, so newest_first means newest in the topic, ordered by timestamp, rather than newest in one partition.
to_offsetNoOptional exclusive offset to stop scanning at, applied to every searched partition.
count_onlyNoOptional. When true, scan the whole range and return only how many messages matched, with a per-partition breakdown and no message bodies. Use this first when a query may match a great many messages, then ask the user how they want them before fetching any.
max_groupsNoOptional number of largest buckets to return with group_by. Defaults to 20, at most 1000. groups_truncated says whether more existed.
partitionsNoOptional partitions to restrict the search to. Defaults to every partition. Do not guess a partition from a message key: producers may set the partition explicitly, so the key does not determine it.
from_offsetNoOptional inclusive offset to start scanning from, applied to every searched partition.
max_matchesNoOptional maximum number of matches to return. Defaults to 10, at most 1000. Use download to export more.
parallelismNoOptional number of concurrent readers, from 1 to 16. Defaults to 1. It splits a single-partition topic's offsets between readers, which makes a full scan of one large partition faster. A multi-partition topic is already read across its partitions together, so this does not apply there. Worth using for count_only, download or a full scan of one partition.
to_timestampNoOptional exclusive end time (RFC3339). Resolved to the first offset at or after this time; messages whose own timestamp is at or after it are skipped and not counted as scanned.
from_timestampNoOptional inclusive start time (RFC3339). Resolved to the first offset at or after this time; messages whose own timestamp is earlier are skipped and not counted as scanned.
max_value_bytesNoOptional maximum value bytes to return per match. Defaults to 512, at most 1048576. Longer values are cut and flagged with truncated=true.
timeout_secondsNoOptional wall-clock limit for the scan in seconds. Defaults to 30, at most 3600.
max_messages_scannedNoOptional maximum number of messages to read before giving up. Defaults to 10000, at most 100000000.

Output Schema

NameRequiredDescriptionDefault
topicYes
groupsNo
matchesYes
completeYes
downloadNo
match_countYes
script_errorsNo
scanned_rangesYes
stopped_reasonYes
groups_truncatedNo
scanned_messagesYes
written_messagesNo
matches_by_partitionNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed16 schema fields changedv0.2.7
    • addedInput schema / properties / download
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "Optional. Instead of returning matches, save every match to a file on the user's own machine. Over stdio the file is saved right away and download.path names it. Over HTTP the response is a link and a command, e.g. curl, to run on the user's machine; the search runs when the link is fetched. Nothing is stored on the server. Use it instead of returning thousands of messages, or whenever the user wants the messages in a file.",
      +  "properties": {
      +    "filename": {
      +      "description": "Optional name to save the file as on the user's machine. Over stdio it may be a path, relative to the server's working directory or absolute, and an existing file is never replaced; over HTTP it is a plain file name used in the command. Defaults to <topic>.jsonl or <topic>.csv.",
      +      "type": "string"
      +    },
      +    "format": {
      +      "description": "Optional file format: jsonl (default, one JSON message per line) or csv. Both are kafka-console's export layout, so the file can be imported there: values are never cut (max_value_bytes does not apply), null keys and values stay null, and payloads are exact bytes (key_encoding, value_encoding utf8 or base64) or, with a Schema Registry schema id, the decoded document with that id.",
      +      "type": "string"
      +    }
      +  },
      +  "type": [
      +    "null",
      +    "object"
      +  ]
      +}
    • changedInput schema / properties / from_timestamp / description
      Previous value: -"Optional inclusive start time (RFC3339). Resolved to the first offset at or after this time."New value: +"Optional inclusive start time (RFC3339). Resolved to the first offset at or after this time; messages whose own timestamp is earlier are skipped and not counted as scanned."
    • addedInput schema / properties / group_by
      Added value: +{
      +  "description": "Optional JavaScript expression, with the same variables as script, that returns a bucket name for each matching message, e.g. return headers['error-reason'] or return key or return schema_id. Every match in the range is counted per bucket, like count_only, and no message bodies are returned. null and undefined form the bucket null. Cannot be combined with download.",
      +  "type": "string"
      +}
    • addedInput schema / properties / max_groups
      Added value: +{
      +  "description": "Optional number of largest buckets to return with group_by. Defaults to 20, at most 1000. groups_truncated says whether more existed.",
      +  "type": "integer"
      +}
    • changedInput schema / properties / max_matches / description
      Previous value: -"Optional maximum number of matches to return. Defaults to 10."New value: +"Optional maximum number of matches to return. Defaults to 10, at most 1000. Use download to export more."
    • changedInput schema / properties / max_messages_scanned / description
      Previous value: -"Optional maximum number of messages to read before giving up. Defaults to 10000."New value: +"Optional maximum number of messages to read before giving up. Defaults to 10000, at most 100000000."
    • changedInput schema / properties / max_value_bytes / description
      Previous value: -"Optional maximum value bytes to return per match. Defaults to 512. Longer values are cut and flagged with truncated=true."New value: +"Optional maximum value bytes to return per match. Defaults to 512, at most 1048576. Longer values are cut and flagged with truncated=true."
    • removedInput schema / properties / output_file
      Removed value: -{
      -  "description": "Optional file name to write every match to, as one JSON message per line. Use this instead of returning thousands of messages. A name only, not a path: the server chooses the directory. The response reports the path, the number written and a short preview.",
      -  "type": "string"
      -}
    • changedInput schema / properties / parallelism / description
      Previous value: -"Optional number of concurrent readers, from 1 to 16. Defaults to 1. It splits a single-partition topic's offsets between readers, which makes a full scan of one large partition faster. A multi-partition topic is already read across its partitions together, so this does not apply there. Worth using for count_only, output_file or a full scan of one partition."New value: +"Optional number of concurrent readers, from 1 to 16. Defaults to 1. It splits a single-partition topic's offsets between readers, which makes a full scan of one large partition faster. A multi-partition topic is already read across its partitions together, so this does not apply there. Worth using for count_only, download or a full scan of one partition."
    • changedInput schema / properties / script / description
      Previous value: -"Optional JavaScript that decides whether a message matches. Return true to keep it. In scope: value (the parsed document for JSON, and the decoded record for Avro, Protobuf, JSON Schema or a configured format; the raw text otherwise), key (string, decoded document when the key has a schema, or null), headers (object of header name to string), partition, offset and timestamp (a Date). Examples: return key === 'order-123'; return value.eventType === 'NEW' && value.payload.amount >= 500; return value.payload.cancelledAt === null. Omit to match every message."New value: +"Optional JavaScript that decides whether a message matches. Return true to keep it. In scope: value (the parsed document for JSON, and the decoded record for Avro, Protobuf, JSON Schema or a configured format; the raw text otherwise), key (string, decoded document when the key has a schema, or null), headers (object of header name to string), partition, offset, timestamp (a Date), value_bytes and key_bytes (raw sizes, before decoding), format (json, avro, protobuf, json_schema, msgpack, text, binary or null), schema_id (number or null) and decode_error (string or null). Examples: return key === 'order-123'; return value.eventType === 'NEW' && value.payload.amount >= 500; return value.payload.cancelledAt === null. Omit to match every message."
    • changedInput schema / properties / timeout_seconds / description
      Previous value: -"Optional wall-clock limit for the scan in seconds. Defaults to 30."New value: +"Optional wall-clock limit for the scan in seconds. Defaults to 30, at most 3600."
    • changedInput schema / properties / to_timestamp / description
      Previous value: -"Optional exclusive end time (RFC3339). Resolved to the first offset at or after this time."New value: +"Optional exclusive end time (RFC3339). Resolved to the first offset at or after this time; messages whose own timestamp is at or after it are skipped and not counted as scanned."
    • addedOutput schema / properties / download
      Added value: +{
      +  "additionalProperties": false,
      +  "properties": {
      +    "command": {
      +      "type": "string"
      +    },
      +    "expires_at": {
      +      "type": [
      +        "null",
      +        "string"
      +      ]
      +    },
      +    "method": {
      +      "type": "string"
      +    },
      +    "note": {
      +      "type": "string"
      +    },
      +    "path": {
      +      "type": "string"
      +    },
      +    "result": true,
      +    "url": {
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "method",
      +    "note"
      +  ],
      +  "type": [
      +    "null",
      +    "object"
      +  ]
      +}
    • addedOutput schema / properties / groups
      Added value: +{
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "count": {
      +        "type": "integer"
      +      },
      +      "example": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "offset": {
      +            "type": "integer"
      +          },
      +          "partition": {
      +            "maximum": 2147483647,
      +            "minimum": -2147483648,
      +            "type": "integer"
      +          }
      +        },
      +        "required": [
      +          "partition",
      +          "offset"
      +        ],
      +        "type": "object"
      +      },
      +      "key": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "key",
      +      "count",
      +      "example"
      +    ],
      +    "type": "object"
      +  },
      +  "type": [
      +    "null",
      +    "array"
      +  ]
      +}
    • addedOutput schema / properties / groups_truncated
      Added value: +{
      +  "type": "boolean"
      +}
    • removedOutput schema / properties / output_file
      Removed value: -{
      -  "type": "string"
      -}
  2. First observedv0.2.0

TDQS

A4.3/5.0
Behavior5/5

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

With zero annotations the description carries the full burden and does so well: it discloses that scans are bounded, per-partition reads are merged by timestamp, results have complete/stopped_reason/scanned_ranges to check, scripts past timeout_seconds are interrupted and counted in script_errors, and scripts are not memory-sandboxed. Download semantics are spelled out per transport (stdio writes the file immediately; HTTP returns a link and curl command, nothing stored server-side).

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?

Purpose is front-loaded and most paragraphs (bounded scans, partition merge ordering, DLQ/group_by examples) earn their place. However, the enumerated decoder variable list and the stdio-vs-HTTP download explanation duplicate the corresponding schema property descriptions almost verbatim, adding length without new information.

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 17-parameter, no-annotation tool with an output schema, the description covers the gaps that matter: what the script receives, how aggregation modes differ, scan bounds and the fields that signal them, and the transport difference for downloads. An agent has enough to call it correctly and to interpret truncated or empty results; return-value shape is left to the output schema, as permitted.

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 adds real meaning on top by explaining that group_by 'summarises instead of listing' with one example address per bucket, that direction chooses which matches survive truncation, and that every partition is read together ('newest in the topic rather than newest in one partition'). The script variable list and download explanation largely restate the schema text, limiting the added value.

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

Purpose4/5

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

Opens with a specific verb, resource and mechanism ('Search message key, value, headers or metadata with a JavaScript predicate') and then delimits the two output modes (listing vs. group_by summarisation). An agent can tell what it does without opening the schema. It stops short of naming a sibling (e.g. sample_messages or get_message) as the alternative, so it is clear but not sibling-differentiated.

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

Usage Guidelines4/5

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

Gives concrete when-to-use scenarios ('break a DLQ down by error header, find hot keys on a partition, or count messages per schema_id') and workflow advice ('Use count_only for large result sets', 'Use this first when a query may match a great many messages, then ask the user'). It also warns how to interpret results ('Check complete, stopped_reason and scanned_ranges before treating no matches as conclusive'). No explicit when-NOT-to-use or named alternative tool, which keeps it off the top score.

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