Skip to content

fix(mcp): drop placeholder arguments the tool schema would reject - #1568

Merged
TheGreatAxios merged 1 commit into
mainfrom
cl-11344-strip-empty-placeholder-arguments-from-mcp-tool-calls
Oct 25, 2026
Merged

TheGreatAxios merged 1 commit into
mainfrom
cl-11344-strip-empty-placeholder-arguments-from-mcp-tool-calls

Conversation

@bwachman

@bwachman bwachman commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Before an MCP tool call is forwarded, an optional argument is dropped only when the property's own schema would reject it. An empty or whitespace-only string is dropped when the schema rejects "" (minLength of 1 or more, an enum or const without "", a pattern that "" does not match, or a non-string type); an empty array under minItems of 1 or more or a non-array type; and null where the schema does not allow it. For anyOf and oneOf, a value is dropped only when every branch rejects it. Some models fill every optional field with placeholders, and a server rejects the ones its schema forbids (for example Linear's list_issues with customView: " " fails minLength: 1).
  • Values the schema permits, including empty strings on unconstrained properties and keys the schema does not declare, pass through unchanged, as do required arguments, 0, false, and null on a property that allows it. A missing or non-object arguments value is passed through without throwing. The cleanup runs for every provider; it changes nothing for a model that omits optional fields.

Verification

  • bun run check passes (lint, typecheck, dead exports, build, 9465 tests)
  • Unit tests cover each rule, including minLength: 0, whitespace against minLength: 1, enum, const, pattern (including one that does not compile), minItems, nullable and non-nullable null, composite schemas, and null or undefined arguments
  • A hermetic e2e test replays the padded mcp__linear__list_issues call recorded from gpt-5.6-terra and asserts the fake Linear client receives every recorded argument except customView
  • The live check on gpt-5.6-terra and an Anthropic-native model is still outstanding, so this does not close the issue

Part of CL-11344

@linear-code

linear-code Bot commented Oct 24, 2026

Copy link
Copy Markdown

CL-11344

@bwachman

Copy link
Copy Markdown
Contributor Author

No live evidence yet that this helps. The tests cover the code path: the e2e test replays the recorded customView: " " call. No live session has shown the fix preventing a failure.

The 2026-10-24 stack session that looked like evidence isn't. Its list_issues calls carried 14 empty values with no error, but a direct control call to Linear sending 12 of those same fields as "" (cursor, query, team, state, cycle, label, delegate, project, release, parentId, updatedAt, triagedAt) was also accepted. Those values are harmless with or without this change.

What does fail without it: customView: " " is rejected (Too small: expected string to have >=1 characters). That session sent customView: "." instead, which is not empty and is outside this PR's scope.

A live pass needs a run on a build with this change where the model sends customView (or another field with a minimum length) empty or whitespace and Linear returns no validation error, plus the same padding failing on the release build. Details are on CL-11344.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Fleet review verdict: REQUEST CHANGES

PR: #1568 — fix(mcp): strip empty placeholder arguments before forwarding (CL-11344)

Convergence: swarm → defect / quality / solve convergence → defect lens converged request-changes (must-fix).

Blocker — must fix (confirmed)

Optional string arguments that are empty / whitespace-only are stripped as placeholders without consulting property-schema constraints (e.g. minLength). Legitimate optional empty strings that the schema permits are dropped before forwarding.

  • Confirmed at PR head via temporary test: an optional string with minLength: 0 was still stripped.

Follow-up — should fix (confirmed)

stripPlaceholderArguments throws on null / undefined arguments; surfaced via handler catch.

Required change to merge

Consult property schema constraints (minLength / pattern / enum / const) before treating an empty string as a placeholder.

Why it matters

Post-fix, schema-permitted values are dropped — the fix over-applies.

Verification status

  • Unit + hermetic e2e strip proofs green; live model E2E outstanding.

@bwachman
bwachman force-pushed the cl-11344-strip-empty-placeholder-arguments-from-mcp-tool-calls branch from c4b9f64 to d10a8fd Compare October 24, 2026 20:44
@bwachman

Copy link
Copy Markdown
Contributor Author

Consult the property schema before treating an empty value as a placeholder (fixed). stripPlaceholderArguments now drops an optional argument only when the property's own schema would reject it. An empty or whitespace-only string is dropped only if the schema rejects "" (minLength of 1 or more, an enum or const that does not list "", a pattern that "" does not match, or a type that is not string). Whitespace is judged as empty because servers trim before checking length. An empty array is dropped only under minItems of 1 or more or a non-array type, and null only where the schema does not allow it. For anyOf and oneOf, a value is dropped only when every branch rejects it. Empty values on unconstrained properties, and keys the schema does not declare, are now forwarded unchanged, since the server accepts them. An uncompilable pattern is treated as not rejecting. The e2e case now expects every padded argument except customView to reach the server, and the comment at the call site in src/mcp/plugin.ts no longer claims servers read empty values as filters.

stripPlaceholderArguments throws on null or undefined arguments (fixed). A null, undefined, or non-object arguments value (including an array) is now returned as-is instead of throwing. Unit tests cover each case.

@bwachman bwachman changed the title fix(mcp): strip empty placeholder arguments before forwarding fix(mcp): drop placeholder arguments the tool schema would reject Oct 24, 2026
@bwachman

Copy link
Copy Markdown
Contributor Author

Resolved findings at d10a8fd0, with line citations

Finding Status Where
Blocker: empty strings stripped without consulting the property's constraints Resolved admits checks anyOf/oneOf, enum, const, type, minLength, pattern and minItems (src/mcp/placeholder-args.ts:48-66). A placeholder is dropped only when the property schema rejects it (:67-73). Tests: placeholder-args.test.ts:33, :38, :45, :120. E2E: e2e/mcp-late-dispatch.test.ts:373-435.
Follow-up: throws on null/undefined arguments Resolved Arguments that are not a plain object are returned as-is (src/mcp/placeholder-args.ts:93-95). Test: placeholder-args.test.ts:126.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-reviewed at d10a8fd. Empty strings are stripped only when the schema rejects them, and null or undefined arguments no longer throw. Approving.

@corbits-triage

Copy link
Copy Markdown
Contributor

Triage: Pass

No changes needed from you. A maintainer will review this pull request.

@TheGreatAxios
TheGreatAxios merged commit ade5be1 into main Oct 25, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants