[Bug] MCP tool-call arguments coerced to JSON-string on the wire regardless of schema type

Status Fixed / completed
Maintainer reply None cached
Activity 3 comments · opened May 20, 2026 · closed May 20, 2026

Summary

The Claude Code MCP client appears to coerce non-string scalar inputs (numbers, booleans, arrays, objects) to their JSON-string representation before sending tool-call payloads to the MCP server, regardless of the tool's declared input schema type. This makes schema-side type declarations cosmetic for serialization purposes — they may appear in the catalog view via ToolSearch but don't affect wire behavior.

Reproduction

Tested against AI Engine Pro 3.4.8 (a WordPress MCP server) on 2026-05-10. Two tools registered on the same MCP server:

  • Tool A: set_context_settings with value: { type: "string" }
  • Tool B: set_theme_options with value: { /* no type declared */ }

Test inputs:

  1. Send value: 42 (number literal) to both tools.
  2. Send value: ["a", "b"] (array literal) to both tools.

Observed

Both tools' server-side handlers received string values in every case:

  • 42 → arrived as "42" in PHP, regardless of schema.
  • ["a","b"] → arrived as "[\"a\",\"b\"]" (JSON-encoded string).

The schema-declared type: "string" on Tool A had no effect — same coercion happened on the untyped Tool B.

Expected

Either:

  • A. Validate-and-reject: Tool A receives value: 42 (number) → client rejects the call as schema-violating (42 is not a string), surfacing the schema mismatch to the caller LLM as a clear error.
  • B. Pass-through: Tool A receives value: 42 (number) → client forwards 42 as a JSON-number; server's untyped/lenient handlers see actual numbers, typed handlers can return a clear error from their own validation.

The current silent coercion to string-of-JSON is the worst of both worlds: schema appears to enforce a type but doesn't, and the server-side handler can't distinguish "caller passed a number" from "caller passed a string-shaped number."

Why this matters

I spent 5 rounds of release cycles on a downstream plugin (ws404-thegem-mcp v0.4.6 → v0.4.9, ~3 weeks of audit work) trying to leverage schema-side type declarations to enforce input refusal on a server-side check. The fixes always shipped looking correct (catalog view via ToolSearch showed the type field) but had no behavioral effect. Empirical verification only came from a two-tool discriminator test (described below) that should have been the first step.

Discriminator (proposed regression test for the harness)

If Claude Code's MCP client adopts validate-and-reject behavior in a future version, downstream consumers should run this test to detect the change:

  1. Pick a tool with value: { type: "string" } and a control tool with no type declaration on the comparable param.
  2. Send value: 42 to both.
  3. If both arrive as "42" (string) → current silent-coercion behavior.
  4. If typed tool errors and control accepts 42 → validate-reject behavior.

Related upstream issue

There's an adjacent issue on the AI Engine (server) side: normalize_input_schema appears to silently strip ALL union-array type declarations (e.g., type: ["string","number","boolean"]), not just the documented ["string","object"] / ["string","array"] patterns that "break ChatGPT." Already filed with Meow Apps; this report is specifically about the client-side coercion behavior.

Environment

  • Claude Code (current as of 2026-05-20)
  • AI Engine Pro 3.4.8 (target MCP server)
  • WordPress 6.x on PHP 8.1
  • Single-admin connection over the MCP transport

View original on GitHub ↗

3 Comments

github-actions[bot] · 4 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/32524
  2. https://github.com/anthropics/claude-code/issues/58771
  3. https://github.com/anthropics/claude-code/issues/24597

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

ws404dev · 4 months ago

Duplicate of #32524 — the bot was correct. I've added the M13 two-tool discriminator + AI Engine normalize_input_schema cross-reference as a comment on the original issue (https://github.com/anthropics/claude-code/issues/32524#issuecomment-4502319725) rather than keeping the content isolated in this duplicate. Closing.

github-actions[bot] · 3 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.