Repository navigation
feat(evals): tool-selection eval for description routing regressions - #274
Merged
Merged
Conversation
Runs 17 prompts against Claude and grades which tools it calls against expected trajectories, with all descriptions loaded and again behind the API's tool search. The tool surface comes from a real McpServer read back over MCP; tool results are canned per case, so no Mapbox calls are made. EVAL_SERVER_ROOT compares another checkout (e.g. a PR worktree). Runner and lite report builder are vendored from the claude-api skill's eval scaffold. Adds @anthropic-ai/sdk and tsx as dev dependencies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 12, 2026
- EVAL_PROVIDER=openai runs the same cases through the Responses API, with server-side tool search for deferred mode (eval:tools:openai). - Each stubbed tool result gets its own ref, so an invented or hand-built render_map_tool call is caught by ref_ok. - Two site-building cases add a write_file host tool and expect export_map_html_tool (#275); needs_tools lets them fail cleanly on servers without it. - New no_token_ask metric flags asking the user for a Mapbox token or writing a placeholder token into a file. - EVAL_MODES selects eager/deferred; rescore reads EVAL_FLOW. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ctufts
approved these changes
Oct 13, 2026
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.
Description
Adds an eval that checks which tools Claude (and, optionally, an OpenAI model) picks for a prompt, given our tool descriptions and server instructions. Nothing in the repo tested this, so changes like #273 (rewording descriptions for the Claude connector review) had no way to show they didn't break routing.
How it works
McpServer, set up the way the hosted deployment does it (publishJsonSchema2020,installTo,buildServerInstructions), then read back overtools/list. The eval sees exactly what a host sees.static_map_image_toolonly for image filesground_location_toolvsreverse_geocode_toolwrite_filehost tool, and must not ask for a Mapbox token or leave a placeholder (these expectexport_map_html_toolfrom feat(tools): add export_map_html_tool for maps in agent-built pages #275 and fail cleanly on servers without it)web_searchandplaces_map_display_v0tools sit alongside ours, so we can see if our descriptions crowd them out. That's the reviewer's concern.ref_ok(everyrender_map_toolcall passes refs it was actually given, unchanged; each stubbed result gets its own ref) andno_token_ask(no request for a token, no placeholder token written to a file).EVAL_PROVIDER=openai(npm run eval:tools:openai -- --model <id>) runs the same cases, stubs and grader through the Responses API, with OpenAI's server-side tool search for the deferred mode. Results go to a separate flow so providers never mix.chat-latestis the closest proxy for ChatGPT but only supports eager mode (EVAL_MODES=eager).EVAL_SERVER_ROOTpoints at another checkout, so two branches can be compared.npm run eval:tools:reportbuilds an HTML report with per-case transcripts.The runner and report builder are vendored from the claude-api skill's eval scaffold. That gets us resume, backoff, a per-case timeout, a failure log and a check on which model actually answered. It calls the Claude API (~$8 per 114-row run on Opus 5.5), so it is not part of
npm test.First results (Opus 5.5, 3 reps each, on the original 17 prompts)
#273 doesn't regress anything. Most of the remaining unrequested maps come from the hint data tools append to their results. Making that hint conditional fixes most of them; I've suggested that change on #273.
ChatGPT proxy (
chat-latest, eager only, 3 reps, on the original 17 prompts)Same direction as Claude: #273 and the hint change both help, nothing regresses. Those runs used one shared ref across all stubs, which is why ref checks are now per result; they aren't rerun on the new harness yet.
Site-building cases (main vs #275, 3 reps)
On main, both models hand-write GL JS and ask for a token or leave
YOUR_MAPBOX_ACCESS_TOKENin the file, which is what Codex did in the report. The one flagged row per provider on #275 comes from the stub returning a placeholder page, not from the tool.Testing
npm test: 980/980 pass.tsc --noEmitpasses withevals/included. The build only compilessrc/(tshy usestsconfig.src.json).chat-latest(57/57 each), no errors.Notes
@anthropic-ai/sdk,openaiandtsxare new dev dependencies. The lockfile still resolves everything from registry.npmjs.org..claude/hillclimb/*/is gitignored._state.jsonis committed: it lists the metrics, and the harness hash the runner checks before running.Checklist
npm testpasses): no unit tests, since the eval is a separate paid runnpm run lint)CHANGELOG.mdupdated underUnreleasedevals/tool-selection/README.md)🤖 Generated with Claude Code