fix: include schema guidance in empty-arguments error to prevent missing-tool reports#40542
Conversation
…ool reports When an agent calls a safeoutputs tool with empty arguments (as a schema probe), the server now includes the required parameters and a usage example in the error response alongside the existing discovery-probe warning. This allows models like o4-mini (AOAI/Entra) that don't follow up with a tools/list call to immediately retry create_issue with correct arguments, instead of reporting it as a missing tool. Fixes smoke-copilot-aoai-entra failure: create_issue reported missing. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR improves MCP tool-call validation errors when an agent sends empty {} arguments as a schema probe, by embedding schema guidance (required fields + example payload) directly in the probe-detection error so models can retry without a tools/list roundtrip.
Changes:
- Appends
generateEnhancedErrorMessage(...)output to the existing “write-once, not a discovery probe” error for empty-arguments tool calls (in both request- and message-handling paths). - Updates the probe-detection unit test to assert the presence of
Required parameterguidance and anExample:block in the error message.
Show a summary per file
| File | Description |
|---|---|
| actions/setup/js/mcp_server_core.cjs | Enhances empty-arguments probe-detection errors by appending schema guidance for immediate retry. |
| actions/setup/js/mcp_server_core.test.cjs | Extends the probe-detection test to verify schema guidance is included in the error message. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 1
| // Schema guidance should be included so the model can retry without calling tools/list | ||
| expect(results[0].error.message).toContain("Example:"); | ||
| expect(results[0].error.message).toContain("Required parameter"); |
|
✅ PR Code Quality Reviewer completed the code quality review. |
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅ |
|
✅ Test Quality Sentinel completed test quality analysis. |
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. No ADR enforcement needed: PR #40542 does not have the 'implementation' label (has_implementation_label=false) and has 0 new lines of code in business logic directories (requires_adr_by_default_volume=false, threshold=100). Neither Design Decision Gate condition is met. |
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /diagnose and /tdd — approving with minor suggestions.
📋 Key Themes & Highlights
Key Themes
- Root cause well-addressed: The fix correctly identifies that
o4-miniwon't follow up on atools/listredirect, and enriches the probe-detection error in-place with schema guidance so the model can retry immediately. Minimal blast radius. - Symmetric patching: Both
handleRequest(throws) andhandleMessage(replyError) are updated consistently — no risk of one path regressing silently. - Test gap: New assertions land only in the
handleMessagedescribe block; thehandleRequestpath lacks a probe-detection test (see inline comment). - Minor consistency:
tool.inputSchema.requiredis passed togenerateEnhancedErrorMessagein the empty-args branch whilemissingis used in the sibling partial-args branch — functionally equivalent but slightly inconsistent (see inline comment).
Positive Highlights
- ✅ Reuses the existing
generateEnhancedErrorMessagehelper — no logic duplication - ✅ PR description clearly traces the regression from symptom → model behaviour → root cause → fix
- ✅ Test comment (
// Schema guidance should be included so the model can retry without calling tools/list) serves as good inline documentation of the intent
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 66.3 AIC · ⌖ 11.5 AIC · ⊞ 6.9K
| expect(results[0].error.message).toContain("noop"); | ||
| // Schema guidance should be included so the model can retry without calling tools/list | ||
| expect(results[0].error.message).toContain("Example:"); | ||
| expect(results[0].error.message).toContain("Required parameter"); |
There was a problem hiding this comment.
[/tdd] These new assertions cover only the handleMessage probe-detection path. The handleRequest describe block (line 476) has no equivalent probe-detection test — a future regression there would go undetected.
💡 Suggested addition in the handleRequest describe block
it("should return probe-detection error with schema guidance for empty arguments", async () => {
const { createServer, registerTool, handleRequest } = await import("./mcp_server_core.cjs");
const server = createServer({ name: "safe-outputs", version: "1.0.0" });
registerTool(server, {
name: "create_issue",
description: "Create an issue",
inputSchema: {
type: "object",
properties: {
title: { type: "string", description: "Issue title" },
body: { type: "string", description: "Issue body" },
},
required: ["title", "body"],
},
handler: () => ({ content: [] }),
});
const response = await handleRequest(server, {
jsonrpc: "2.0", id: 10, method: "tools/call",
params: { name: "create_issue", arguments: {} },
});
expect(response.error.code).toBe(-32602);
expect(response.error.message).toContain("write-once, not a discovery probe");
expect(response.error.message).toContain("Required parameter");
expect(response.error.message).toContain("Example:");
});| if (missing.length) { | ||
| const hasRequiredFields = tool.inputSchema && Array.isArray(tool.inputSchema.required) && tool.inputSchema.required.length > 0; | ||
| if (hasRequiredFields && Object.keys(args).length === 0) { | ||
| const schemaGuidance = generateEnhancedErrorMessage(tool.inputSchema.required, name, tool.inputSchema); |
There was a problem hiding this comment.
[/diagnose] Minor consistency nit: in the empty-args branch missing already equals tool.inputSchema.required (all fields are missing), so passing tool.inputSchema.required to generateEnhancedErrorMessage is equivalent but slightly diverges from the partial-args path just below (line 783) which uses missing.
💡 Suggested change
// Before
const schemaGuidance = generateEnhancedErrorMessage(tool.inputSchema.required, name, tool.inputSchema);
// After (matches the partial-args path)
const schemaGuidance = generateEnhancedErrorMessage(missing, name, tool.inputSchema);Same change applies to line 945 in the handleMessage path. This makes both branches read consistently and avoids the impression that a different field list is intentional.
There was a problem hiding this comment.
No blocking issues found
The fix is correct and well-targeted. Both handleRequest and handleMessage correctly append generateEnhancedErrorMessage output to the probe-detection error, giving models like o4-mini immediate schema guidance without a tools/list roundtrip.
Analysis notes
- Correctness:
tool.inputSchema.requiredis passed instead of the already-computedmissing, but whenObject.keys(args).length === 0,validateRequiredFieldsreturns all required fields — they are guaranteed equal. No divergence. - Error safety:
generateEnhancedErrorMessageis fully guarded internally and is already called without try/catch in the surrounding non-probe paths — consistent with established pattern. - Test update: Assertions for
"Required parameter"and"Example:"correctly validate the new behavior on thehandleMessagepath. - Missing
handleRequesttest: Already flagged in the prior review comment — not duplicated here.
No new issues found beyond what is already noted.
🔎 Code quality review by PR Code Quality Reviewer · 78.7 AIC · ⌖ 7.12 AIC · ⊞ 5.1K
🧪 Test Quality Sentinel Report✅ Test Quality Score: 100/100 — Excellent
📊 Metrics & Test Classification (1 test analyzed)
Go: 0; JavaScript: 1 ( Test inflation check: test file +3 lines, production file +4 lines (ratio 0.75 — within 2:1 threshold). Verdict
|
|
@copilot run pr-finisher skill |
…idance Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Added a |
When an agent (specifically
o4-minivia AOAI/Entra) calls a safeoutputs tool with empty arguments{}as a schema probe, the server previously returned only the discovery-probe warning pointing totools/list. Theo4-minimodel would not follow up withtools/listand instead emit amissing_toolsafe-output — causingcreate_issueto be reported as unavailable in the Smoke Copilot - AOAI (Entra) run.Changes
mcp_server_core.cjs— Both empty-argument detection code paths now appendgenerateEnhancedErrorMessageoutput (required fields + usage example) to the existing probe-detection error, giving the model immediate retry guidance without atools/listroundtrip:mcp_server_core.test.cjs— Updated the probe-detection test to assertRequired parameterandExample:are present in the error response.