fix(samples): propagate handler isError to MCP response; add content-text defense-in-depth#40060
Conversation
…nt check in apply_samples Co-authored-by: dsyme <7204669+dsyme@users.noreply.github.com>
|
✅ Test Quality Sentinel completed test quality analysis. |
|
✅ PR Code Quality Reviewer completed the code quality review. |
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. No ADR enforcement needed: PR #40060 does not have the 'implementation' label (has_implementation_label=false) and has 0 new lines of code in business logic directories (default_business_additions=0, threshold=100). |
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅ |
There was a problem hiding this comment.
⚠️ Not ready to approve
The newly added mcp_server_core.test.cjs test redundantly re-spies on process.stderr.write despite an existing spy in the enclosing beforeEach, which can cause Vitest to throw or behave inconsistently.
Pull request overview
This PR fixes error propagation in the sample replay path by preserving the isError flag from MCP tool handlers (instead of hardcoding false), and adds a defense-in-depth check in the samples driver to detect handler-encoded {"result":"error"} payloads even under protocol/version skew.
Changes:
- Propagate
isErrorfrom handler results through both MCP dispatch layers (mcp_server_core.cjs+safe_outputs_mcp_server_http.cjs). - Add
sampleResultIsError()toapply_samples.cjsand use it in the replay loop as a fallback detector. - Add unit tests covering
isErrorpropagation and the newsampleResultIsError()helper.
File summaries
| File | Description |
|---|---|
| actions/setup/js/safe_outputs_mcp_server_http.cjs | Preserves handler-provided isError when normalizing tool results for MCP responses. |
| actions/setup/js/mcp_server_core.cjs | Preserves handler-provided isError in both HTTP (handleRequest) and stdio (handleMessage) dispatch paths. |
| actions/setup/js/apply_samples.cjs | Adds sampleResultIsError() fallback detection and uses it to mark sample replays as failed. |
| actions/setup/js/mcp_server_core.test.cjs | Adds a regression test asserting isError:true is forwarded into the JSON-RPC tools/call result. |
| actions/setup/js/apply_samples.test.cjs | Adds unit tests for sampleResultIsError() covering multiple input shapes and fallbacks. |
Copilot's findings
- Files reviewed: 5/5 changed files
- Comments generated: 1
Note
Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
🧪 Test Quality Sentinel Report✅ Test Quality Score: 86/100 — Excellent
📊 Metrics & Test Classification (7 tests analyzed)
Go: 0 ( Inflation note: The +44 / +6 ratio for Mocking note: Verdict
|
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /diagnose and /tdd — approving with non-blocking suggestions on test coverage.
📋 Key Themes & Highlights
Key Themes
- Test coverage gaps:
handleRequestand the HTTP server fix sites lack regression tests — the three fixes are structurally identical, but onlyhandleMessageis covered by the new test - Test signal isolation: the
isError: trueunit test inapply_samples.test.cjsconflates two detection paths; cleaner with success content
Positive Highlights
- ✅ Root cause correctly identified and fixed at all three dispatch sites (
handleRequest,handleMessage, HTTP ×2) — no half-measures - ✅
sampleResultIsError()is a well-designed defense layer: fast-path onisError, graceful fallback on content text, safe JSON parse withtry/catch, and clean early-return structure - ✅ The pre-fix scenario (content-text only,
isError: false) is explicitly tested and commented — this is excellent as documentation of the original failure mode - ✅ All six unit tests for
sampleResultIsErrorcover meaningful boundaries (null, empty, non-JSON, success, flag, fallback) - ✅ Export of
sampleResultIsErroris clearly flagged in the export comment as test-only
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer
There was a problem hiding this comment.
The core logic fix is correct and minimal — isError is now properly forwarded at all three dispatch sites, and the sampleResultIsError defense-in-depth helper is well-reasoned and well-tested.
🔍 Review themes
Test coverage gaps
Two of the three fixed code paths lack tests:
handleRequestinmcp_server_core.cjshas no unit test at all (onlyhandleMessageis covered by the new test).safe_outputs_mcp_server_http.cjshas no test file; neither the predefined-tool nor the dynamic safe-jobisErrorfix is verified.
The sampleResultIsError and handleMessage tests are thorough; extending coverage to the other two paths would complete the picture.
Existing concern (already reviewed)
The new handleMessage test redundantly re-creates the server and calls vi.spyOn on process.stderr.write a second time, which the earlier review comment already flagged.
🔎 Code quality review by PR Code Quality Reviewer
- Simplify handleMessage isError test to reuse beforeEach server/stderr spy - Add handleRequest isError propagation regression tests - Isolate flag path in sampleResultIsError flag-precedence test (success content) - Extract normalizeMcpToolResult helper in HTTP server + unit-test both reg paths
In samples mode,
apply_samplesreported every replay as successful even when the safe-output handler returned{"result":"error", ...}. The handler correctly setsisError: trueon its return value, but both MCP dispatch layers discarded it and hardcodedisError: false, making handler failures invisible to the replay driver and producing green runs with nooutputs.jsonl.Root cause
mcp_server_core.cjs(handleRequest/handleMessage) andsafe_outputs_mcp_server_http.cjsboth did:The handlers in
safe_outputs_handlers.cjsalready returned{ content, isError: true }on failure — that flag was never forwarded.Changes
mcp_server_core.cjs—handleRequestandhandleMessagenow preserveisErrorfrom the handler result instead of hardcodingfalse.safe_outputs_mcp_server_http.cjs— same fix for both predefined-tool and dynamic safe-job registrations.apply_samples.cjs— addedsampleResultIsError(result)helper as a defense-in-depth fallback: detects{"result":"error"}in the content text even if an older server emitsisError: false, keeping the driver resilient to protocol-version skew. The replay loop now uses this helper.Tests
mcp_server_core.test.cjs: new case verifying a handler returningisError: trueproducesisError: truein the JSON-RPC response.apply_samples.test.cjs: 6 unit tests forsampleResultIsError()— covers the success path, theisErrorflag, the content-text fallback (the pre-fix scenario), non-JSON content,null, and empty content array.