Skip to content

feat: warn the model about unsupported tool parameters#519

Merged
wonderwhy-er merged 2 commits into
mainfrom
feat/warn-unsupported-params
Jun 18, 2026
Merged

feat: warn the model about unsupported tool parameters#519
wonderwhy-er merged 2 commits into
mainfrom
feat/warn-unsupported-params

Conversation

@wonderwhy-er

@wonderwhy-er wonderwhy-er commented Jun 17, 2026

Copy link
Copy Markdown
Owner

What

Makes the server tell the model when it sends a parameter a tool doesn't support, instead of silently dropping it.

Zod's default object schemas strip unknown keys, so e.g. read_file called with view_range returned a normal-looking response with the defaults applied and no signal the param was ignored. This adds a central, dispatcher-level hook that detects unsupported parameters and prepends a corrective warning to the response, naming the ignored params and listing the supported ones. The call still succeeds — the warning is advisory.

Example warning the model now receives as the first content block:

You sent parameters not supported by this tool, which were ignored: view_range, foo_bar. Supported parameters for read_file: path, isUrl, offset, length, sheet, range, options.

Covers all tools

The hook runs at the single dispatcher choke point against a toolArgSchemas map (name → schema) wired for all 27 tools, so it's not specific to read_file.

How it works / safety

  • src/utils/unsupportedParams.ts — pure helpers. Detection is top-level only, which is safe because no tool schema uses a top-level .passthrough(); free-form fields (options, params, pdfOptions) are named keys, so their contents aren't flagged. Unwraps ZodEffects/optional/default defensively, and never warns when it can't determine the supported set.
  • src/server.ts — the hook is wrapped in try/catch so an advisory warning can never break an otherwise-successful tool call. It only runs on the success path (type errors / missing-required still throw and return isError: true as before).
  • The warning is a separate prepended content block, so structured/image results aren't corrupted.

Tests

test/integration/read-file-unknown-params.js drives the real server over stdio (like an LLM) and asserts on the actual CallToolResult:

  • Case 0 clean call → no warning (guards against over-firing)
  • Case 1 unsupported params → isError falsy, warning prepended naming view_range/foo_bar + supported list, file content still served
  • Case 2 wrong type → isError: true
  • Case 3 missing pathisError: true

Unit-checked the helpers separately (supported-param extraction, detection, named free-form field not flagged, all 27 tools mapped). npm run build passes.

Note on #518

This supersedes #518 — that PR characterized the silent-strip gap; this branch descends from it and flips the test to assert the new warning behavior. Recommend closing #518 in favor of this, or merging #518 first.

Possible follow-ups (not in this PR)

  • Add detection telemetry (capture ignored-key names only, never values) to measure real-world frequency in BigQuery.
  • Optionally gate the prepend behind a feature flag for staged rollout.

Summary by CodeRabbit

  • New Features

    • Tool calls now automatically detect and report unsupported parameters with clear warnings listing ignored parameters and supported alternatives, improving feedback and preventing silent failures.
  • Tests

    • Added integration tests covering parameter validation scenarios and error handling behavior.

Adds an integration test that drives the real MCP server over stdio (like an
LLM client) and asserts on the actual CallToolResult for three cases:

- Unknown/unaccepted params (e.g. view_range, foo_bar) are silently stripped:
  the read succeeds, isError is falsy, and nothing tells the model its param
  was ignored. This documents the current gap. The intended future behavior is
  to keep the normal response but append a warning that params were stripped;
  when that lands, the Case 1 "no warning" assertion flips and acts as the
  regression anchor.
- Wrong type on a known param returns a dispatcher-shaped isError: true.
- Missing required param (path) returns a dispatcher-shaped isError: true.

Test only; no production code changes.
Zod's default object schemas silently strip unknown keys, so a model that
invents a parameter (e.g. read_file with view_range) gets a normal-looking
response with no signal its input was ignored - it just sees defaults applied.

Add a central, dispatcher-level hook that detects parameters the caller sent
which the tool's schema does not accept, and prepends a corrective warning to
the response naming the ignored params and listing the supported ones. The call
still succeeds; the warning is advisory.

- src/utils/unsupportedParams.ts: pure helpers (getSupportedParams,
  detectUnsupportedParams, buildUnsupportedParamsWarning). Top-level detection;
  unwraps ZodEffects/optional/default defensively; never warns when the
  supported set can't be determined.
- src/tools/schemas.ts: toolArgSchemas map (name -> schema) for all 27 tools.
- src/server.ts: hook before the dispatcher's return, wrapped so it can never
  break a successful call. Covers every tool, not just read_file.
- test/integration/read-file-unknown-params.js: validates clean call (no
  warning), unsupported params (warning prepended, names + supported list,
  read still served), wrong type and missing-required still return isError.
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds unsupported-parameter advisory warning support to the MCP tool call dispatcher. A new toolArgSchemas registry maps tool names to Zod schemas, a new utility module detects unrecognized top-level args and formats corrective warnings, and the CallTool handler prepends those warnings to result.content without failing the call. An integration test validates all four parameter scenarios.

Changes

Unsupported Params Advisory Warnings

Layer / File(s) Summary
unsupportedParams utility functions
src/utils/unsupportedParams.ts
Introduces getObjectShape (recursive Zod unwrapper), getSupportedParams, detectUnsupportedParams (set-difference against resolved schema shape), and buildUnsupportedParamsWarning (formatted advisory string).
toolArgSchemas registry
src/tools/schemas.ts
Exports a Record<string, z.ZodTypeAny> mapping every supported tool name to its Zod argument schema, centralizing schema references for the dispatcher.
CallTool handler wiring
src/server.ts
Imports toolArgSchemas and the three utility functions, then adds a try/catch advisory block after tool execution that detects unsupported params and prepends a warning text to result.content[0] when any are found.
Integration test
test/integration/read-file-unknown-params.js
End-to-end test over MCP stdio covering: clean call (no warning), unsupported params (warning prepended, isError absent), wrong param type (isError: true), and missing required param (isError: true). Includes temp directory setup and config teardown.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • wonderwhy-er/DesktopCommanderMCP#171: Modifies the same CallToolRequestSchema handler in src/server.ts to inject additional text into result.content after tool execution, making it a direct structural sibling of the advisory warning injection added here.

Suggested reviewers

  • dmitry-ottic-ai

Poem

🐇 A rabbit checks params with glee,
Unknown keys? I'll warn thee!
No crash, no fail, just a note in the text,
"These args aren't valid — here's what comes next."
The schema unwraps, the warnings appear,
Clean tool calls ahead — the path is now clear! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: warn the model about unsupported tool parameters' clearly and concisely describes the main change: adding a warning mechanism for unsupported tool parameters sent to tools by models.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/warn-unsupported-params

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server.ts (1)

1476-1478: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

track_ui_event skips the new advisory hook

Line 1477 returns before the unsupported-params warning block runs, so this tool never gets the new warning behavior even though it is registered in toolArgSchemas.

Suggested fix
-        if (name === 'track_ui_event') {
-            return result;
-        }
-
-        if (result.isError) {
+        if (name !== 'track_ui_event' && result.isError) {
             await usageTracker.trackFailure(name);
             console.log(`[FEEDBACK DEBUG] Tool ${name} failed, not checking feedback`);
-        } else {
+        } else if (name !== 'track_ui_event') {
             await usageTracker.trackSuccess(name);
             console.log(`[FEEDBACK DEBUG] Tool ${name} succeeded, checking feedback...`);
             ...
             result = await processDockerPrompt(result, name);
         }
 
         // advisory warning block remains here so it runs for all tools, including track_ui_event

Also applies to: 1568-1588

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server.ts` around lines 1476 - 1478, The early return for track_ui_event
in the condition checking if name === 'track_ui_event' causes the tool to skip
the advisory hook warning block that should execute for all registered tools
including those in toolArgSchemas. Remove this early return statement so that
track_ui_event flows through the same advisory hook/warning block as other
tools. Apply the same fix to any other similar early return conditions mentioned
in the related code sections (also applies to lines 1568-1588).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/server.ts`:
- Around line 1476-1478: The early return for track_ui_event in the condition
checking if name === 'track_ui_event' causes the tool to skip the advisory hook
warning block that should execute for all registered tools including those in
toolArgSchemas. Remove this early return statement so that track_ui_event flows
through the same advisory hook/warning block as other tools. Apply the same fix
to any other similar early return conditions mentioned in the related code
sections (also applies to lines 1568-1588).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0ea80e6-2ba6-49f2-9aec-a807b829574d

📥 Commits

Reviewing files that changed from the base of the PR and between 7a9b2ff and bf3b557.

📒 Files selected for processing (4)
  • src/server.ts
  • src/tools/schemas.ts
  • src/utils/unsupportedParams.ts
  • test/integration/read-file-unknown-params.js

@wonderwhy-er wonderwhy-er merged commit be82290 into main Jun 18, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant