feat(test): eval/benchmark suite for MCP tool retrieval quality#2952
Closed
singsing1 wants to merge 1 commit into
Closed
feat(test): eval/benchmark suite for MCP tool retrieval quality#2952singsing1 wants to merge 1 commit into
singsing1 wants to merge 1 commit into
Conversation
Adds a deterministic benchmark for on-demand MCP tool retrieval that
measures retrieval quality (not just functional correctness, which the
existing test_mcp_tool_retrieval.py already covers).
Metrics on a fixed seed=42 fixture (30 tools / 5 clusters / 12 queries):
- Precision@k / Recall@k / Mean Reciprocal Rank
- Token-savings report: full injection vs. top-k on-demand injection
A sweep over top_k in {3,5,8,10,15} surfaces the recall-vs-token-savings
trade-off. On this dataset top_k=5 is the sweet spot: 100% precision,
83% recall, ~83% tool-schema token savings vs. injecting all MCP tools.
Pure additive (single new file under tests/), zero behaviour change, no
new runtime dependencies. Reproducible across machines so embedding-
pipeline regressions can be tracked over time.
Run:
python -m tests.eval_mcp_tool_retrieval # human report
python -m tests.eval_mcp_tool_retrieval --json # machine report
python -m pytest tests/eval_mcp_tool_retrieval.py
Owner
|
Thanks for the thoughtful PR and the detailed write-up — the honesty about the synthetic-data limitations is appreciated. However, we won't be merging this one. The main issue is that the benchmark runs entirely on synthetically generated vectors (fixed clusters, no real embedding provider), so the metrics mostly reflect the data generator rather than actual retrieval quality — and it can't catch real embedding/index regressions since that pipeline is never exercised. The core If we revisit retrieval eval later, we'd want it running against the real embedding provider on a realistic tool catalogue. Thanks again for the contribution! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(test): eval/benchmark suite for MCP tool retrieval quality
Summary
Adds a deterministic benchmark for the on-demand MCP tool-retrieval feature (
agent/tools/mcp/tool_retrieval.py) that measures retrieval quality — not just functional correctness, whichtest_mcp_tool_retrieval.pyalready covers.Relates to #2937 — the on-demand MCP tool-retrieval feature this benchmark evaluates. The feature shipped in #2940 with unit tests for correctness, but no way to measure how good the retrieval actually is. This PR fills that gap.
Today the project has no eval/benchmark anywhere (only a Playwright JS
evaluate). This PR introduces a reproducible quality baseline for the retrieval path so that regressions in the embedding model / vector index can be tracked over time.Motivation
select_mcp_toolsdecides which MCP tools get injected into each LLM turn. The existing unit tests verify the invariants (only-grows, fallback to full injection, dimension-mismatch handling) — i.e. "does it behave correctly?". But nothing answers: "is the retrieval actually any good?" — does it pick the right tools, and how many tokens does on-demand injection save versus injecting all of them?Without this, a silent regression in embedding quality would go unnoticed until users complain. This PR closes that gap with a self-contained, byte-for-byte reproducible benchmark.
What it measures
On a fixed
seed=42fixture dataset (30 MCP tools in 5 semantic clusters, 12 queries):full-injection tokens − on-demand top-k tokens(avg per query)A sweep over
top_k ∈ {3, 5, 8, 10, 15}surfaces the recall-vs-token-savings trade-off so maintainers can pick a sensible default.Example output
On this synthetic dataset
top_k=5is the sweet spot: 100% precision, 83% recall, ~83% tool-schema token savings versus injecting all MCP tools.Dataset: why synthetic, and its limits
The fixture is synthetic, not captured from a real deployment — deliberately. Precision/recall require a ground truth (which tools are relevant for a given query), and CowAgent ships no such labelled set: real conversations don't come with "the correct tool subset" annotations. Constructing one is the only way to get IR metrics at all.
The construction is grounded, not arbitrary:
{name, description, inputSchema}JSON shape CowAgent injects.What this benchmark can and cannot claim:
How to run
The pytest run only asserts metrics land in a sane band (recall ≥ 0.80, savings > 0, determinism) so a broken embedding pipeline fails CI without being overly brittle.
Backward compatibility
Pure additive — a single new file under
tests/, zero changes to production code, zero behaviour change, no new runtime dependencies (usesrandom/mathfrom the stdlib;numpyis optional and only used by the module under test, not by this benchmark). The fixture vectors are generated from a fixed seed, so every reported number is reproducible across machines and Python versions.Why this matters
top_k.