Skip to content

feat(mcp): Add the find_spans MCP tool (RFC 0016 M7) - #9643

Open
om7057 wants to merge 4 commits into
jaegertracing:mainfrom
om7057:feat/rfc0016-m7-find-spans-mcp-tool
Open

om7057 wants to merge 4 commits into
jaegertracing:mainfrom
om7057:feat/rfc0016-m7-find-spans-mcp-tool

Conversation

@om7057

@om7057 om7057 commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

What

Adds a find_spans MCP tool alongside the existing ones: an agent can now ask for spans matching service/name/attribute/duration/error criteria across however many traces in one call, via querysvc.QueryService.FindSpans (the same call get_span_details's identity-filter fast path already uses).

Today that takes search_traces (trace summaries only, no spans) followed by one or more get_span_details calls, or reading whole traces: the two-call detour RFC 0016 §10 describes as the motivation for this RFC.

Design

  • Input mirrors search_traces's flat, LLM-friendly parameters (service_name, span_name, attributes, with_errors, duration_min/duration_max) rather than exposing the RFC 0005 filter AST to the model directly. The handler translates them into an AND of Eq/Gte/Lte predicates, the same way get_span_details.go's buildIdentityFilter builds a filter from Go.
  • Unlike search_traces, the time range is required (RFC 0016 §4.5): a span search has no other bound to default it against.
  • An attribute value from the input map is passed as an untyped AnyValue rather than a StringValue, so it resolves against whatever kind the attribute is actually stored as, not only a literal string (RFC 0005 §5.4).
  • with_errors reads as the error virtual attribute every backend's filter lowering special-cases (the elasticsearch backend's asErrorTagEquality, and the memory backend's own equivalent), not a stored attribute of that name.
  • Output reuses types.SpanDetail, already defined for get_span_details, rather than inventing a second span shape.
  • The memory backend declares SearchCapabilities.SpanSearch and serves the tool. A backend that does not declare it refuses with the existing ErrSpanSearchUnsupported message. There is no fallback path the way get_span_details needs one, since find_spans has no pre-RFC-0016 equivalent to fall back to.

Fixes #9639

Copilot AI lite review requested due to automatic review settings September 24, 2026 13:16
@om7057
om7057 requested a review from a team as a code owner September 24, 2026 13:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings September 24, 2026 13:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.05825% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.60%. Comparing base (d82a87c) to head (3864880).

Files with missing lines Patch % Lines
.../internal/mcptools/internal/handlers/find_spans.go 97.91% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9643      +/-   ##
==========================================
- Coverage   98.60%   98.60%   -0.01%     
==========================================
  Files         410      411       +1     
  Lines       20065    20168     +103     
==========================================
+ Hits        19785    19886     +101     
- Misses        280      282       +2     
Flag Coverage Δ
badger_direct 6.42% <ø> (ø)
badger_e2e 11.22% <0.00%> (-0.07%) ⬇️
cassandra-4.x-direct-manual 10.45% <ø> (ø)
cassandra-4.x-e2e-auto 16.80% <0.00%> (-0.10%) ⬇️
cassandra-4.x-e2e-manual 16.80% <0.00%> (-0.10%) ⬇️
cassandra-5.x-direct-manual 10.45% <ø> (ø)
cassandra-5.x-e2e-auto 16.80% <0.00%> (-0.10%) ⬇️
cassandra-5.x-e2e-manual 16.80% <0.00%> (-0.10%) ⬇️
clickhouse-direct 6.86% <ø> (ø)
clickhouse-e2e 12.88% <0.00%> (-0.08%) ⬇️
elasticsearch-7.x-direct 21.88% <ø> (ø)
elasticsearch-8.x-direct 21.93% <ø> (ø)
elasticsearch-8.x-e2e 21.39% <0.00%> (-0.13%) ⬇️
elasticsearch-9.x-direct 21.93% <ø> (ø)
elasticsearch-9.x-e2e 21.39% <0.00%> (-0.13%) ⬇️
grpc_direct 14.65% <ø> (ø)
grpc_e2e 18.93% <0.00%> (-0.11%) ⬇️
kafka-3.x-v2 26.38% <0.00%> (-0.14%) ⬇️
memory_v2 19.46% <10.67%> (-0.05%) ⬇️
opensearch-1.x-direct 21.88% <ø> (ø)
opensearch-2.x-direct 21.88% <ø> (ø)
opensearch-2.x-e2e 21.39% <0.00%> (-0.13%) ⬇️
opensearch-3.x-direct 21.88% <ø> (ø)
opensearch-3.x-e2e 21.39% <0.00%> (-0.13%) ⬇️
query 16.55% <0.00%> (-0.10%) ⬇️
tailsampling-processor 6.40% <0.00%> (-0.04%) ⬇️
unittests 97.55% <98.05%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

CI Summary Report

Metrics Comparison

⚠️ 4 metric change(s) detected (informational)

View changed metrics

For label-level diff details, open the CI run and expand the "Compare metrics and generate summary" step logs.

metrics_snapshot_kafka_v2 — ⬇️ download diff
4 modified

  • otelcol_kafka_exporter_bytes
  • otelcol_kafka_exporter_bytes_uncompressed
  • otelcol_kafka_exporter_messages
  • otelcol_kafka_exporter_records

Code Coverage

✅ Coverage 98.7% (baseline 98.7%)

➡️ View CI run | View publish logs
2026-10-04 13:21:28 UTC

Copilot AI lite review requested due to automatic review settings September 30, 2026 03:31
@om7057
om7057 force-pushed the feat/rfc0016-m7-find-spans-mcp-tool branch from 8457bfa to c022b3d Compare September 30, 2026 03:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@om7057
om7057 force-pushed the feat/rfc0016-m7-find-spans-mcp-tool branch from c022b3d to 849ab24 Compare September 30, 2026 12:49
Copilot AI lite review requested due to automatic review settings September 30, 2026 12:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

om7057 added 2 commits October 4, 2026 09:04
An agent that wants spans matching service/name/attribute/duration/error
criteria across however many traces previously had to call search_traces
(trace summaries only, no spans) and then get_span_details per trace, or
read whole traces, the two-call detour RFC 0016 §10 describes. find_spans
answers that in one call via querysvc.QueryService.FindSpans, which
search_traces and get_span_details already use for the trace and
identity-filter paths respectively.

The tool's input mirrors search_traces's flat, LLM-friendly parameters
(service_name, span_name, attributes, with_errors, duration_min/max)
rather than exposing the RFC 0005 filter AST to the model; the handler
translates them into an AND of Eq/Gte/Lte predicates the same way
get_span_details.go's buildIdentityFilter builds one from Go. Unlike
search_traces, the time range is required (RFC 0016 §4.5): a span
search has no other bound to default it against.

An attribute value from the input map is passed as an untyped AnyValue
rather than a StringValue, so "500" matches a numeric or boolean
attribute stored under that key, not only a literal string (RFC 0005
§5.4). with_errors reads as the error virtual attribute every backend's
filter lowering special-cases (e.g. the elasticsearch backend's
asErrorTagEquality, and the memory backend's own equivalent), not a
stored attribute of that name.

No backend on main yet declares SearchCapabilities.SpanSearch (RFC 0016
M3 through M6 are still landing), so the tool refuses cleanly with the
existing ErrSpanSearchUnsupported message until one does; there is no
fallback path the way get_span_details needs one, since find_spans has
no pre-RFC-0016 equivalent to fall back to.

Fixes jaegertracing#9639

Signed-off-by: Om Kulkarni <kulkarniom7057@gmail.com>
Signed-off-by: Om Kulkarni <kulkarniom7057@gmail.com>
@om7057
om7057 force-pushed the feat/rfc0016-m7-find-spans-mcp-tool branch from 849ab24 to 46ff5af Compare October 4, 2026 03:34
Copilot AI lite review requested due to automatic review settings October 4, 2026 03:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved issues affect error filtering, duration validation, pagination, zero-value bounds, and the configured result cap.

Review effort: Lite
Findings: 2 Medium severity · 1 Low severity

Open (3)

Comment thread cmd/jaeger/internal/extension/jaegerquery/internal/mcptools/server.go Outdated
…he search limit

A duration_min or duration_max of "0s" is a bound the caller sent, but
buildSearchFilter treated any zero duration as absent, so "0s" as
duration_max returned spans of every duration. The bounds are now nil
when absent, so an explicit zero is applied.

find_spans was capped by MaxSpanDetailsPerRequest, a per-request limit
for get_span_details. It now uses MaxSearchResults, the same cap
search_traces uses, which is what the PR description says it mirrors.

Signed-off-by: Om Kulkarni <kulkarniom7057@gmail.com>
Copilot AI lite review requested due to automatic review settings October 4, 2026 11:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved critical and moderate findings remain.

Review effort: Lite
Findings: 1 High severity · 1 Low severity

Open (2)
Resolved since last review (2)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Apply effective max_results to backend page size

cmd/​jaeger/​internal/​extension/​jaegerquery/​internal/​mcptools/​internal/​handlers/​find_spans.go:139

The effective max_results is applied only while flattening the response; the query is sent with a zero page size, so QueryService replaces it with DefaultPageSize (100). A request for one span still makes the backend fetch a full page, and a configured/requested limit above 100 is silently truncated to that first page. Set Pagination.PageSize to the effective positive limit (clamped to the query-service maximum), or follow NextPageToken when a larger result is intended.

with_errors was sent as a typed boolean. The Elasticsearch and OpenSearch
lowering reads a comparison's constant as text and refuses a typed
boolean there, so with_errors=true failed on those backends. The
constant is now untyped, which every span-search lowering reads as text
and parses against the error tag.

Signed-off-by: Om Kulkarni <kulkarniom7057@gmail.com>
Copilot AI lite review requested due to automatic review settings October 4, 2026 12:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add a find_spans MCP tool (RFC 0016 M7)

2 participants