fix(mcp): check_chain variant inputs no longer silently discarded#191
Merged
fix(mcp): check_chain variant inputs no longer silently discarded#191
Conversation
Previously `crates/spar-mcp/src/tools/check_chain.rs` accepted `variant` and `variant_context` in its input schema but bound both to `_variant` / `_unused`, dropping them on the floor. An LLM agent supplying a variant name received unfiltered chain results with no warning — a silent correctness bug that broke the rivet<->spar v1 contract for variant-scoped queries. Wiring full variant scoping through `LatencyAnalysis` is non-trivial (the pass walks the unfiltered `SystemInstance` and chains can cross dropped components in subtle ways), so this PR takes Option B from the task brief: refuse the call with a clear `BAD_INPUT` error pointing the agent at `spar.verify_move` / `spar.enumerate_moves` for variant- scoped use, and document the deferral as a v0.10 enhancement. Also addresses Tier C #50: the variant <-> variant_context mutual- exclusion was previously enforced only at the `verify_pipeline` / `enumerate_pipeline` layer (via `MovesError::VariantArgsConflict`). This PR adds a JSON Schema `not` constraint to all three tools' `inputSchema` so a strict client rejects the conflicting payload before sending it, and applies the same guard up-front in `check_chain::call` so the more specific "mutually exclusive" message wins over the not-yet-supported one. Tests: - `check_chain_rejects_variant_input_with_bad_input` (in-process) - `check_chain_rejects_both_variant_and_variant_context` (in-process) - `verify_rejects_both_variant_and_variant_context` (in-process) - `check_chain_with_variant_returns_bad_input_via_stdio` (stdio) - `check_chain_rejects_both_variant_args_via_stdio` (stdio) - `tools_list_advertises_variant_mutex_in_schema` (stdio) Out of scope: protocolVersion (#21), notifications/initialized (#22), outputSchema (#23) — separate PRs per the task brief. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
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.
Tier A #7 + Tier C #50 from the Wave 3 MCP review. check_chain accepted variant / variant_context but discarded them silently — agents got unfiltered chain results with no warning. Picked Option B per the brief: hard-reject with BAD_INPUT pointing at verify_move/enumerate_moves (Option A requires threading VariantScope through LatencyAnalysis, a v0.10-scale effort). Also added JSON Schema 'not' mutex on all three tools and an up-front mutex guard in check_chain. Tests: 5 new (3 in-process, 3 stdio) covering the BAD_INPUT path, the conflict path, and the schema advertising. Quality gates (cargo test -p spar-mcp, clippy, fmt, rivet validate) all pass. Out of scope per brief: protocolVersion (#21), notifications/initialized (#22), outputSchema (#23).