MCP tool calls with array-typed parameters (list[int]) arrive server-side as stringified text, failing validation
Summary
When calling an MCP tool that has an array-typed parameter (e.g. loc: list[int] | None), the value is transmitted in a way that arrives at the MCP server as a string representation of the array rather than an actual JSON array, causing the server's own parameter validation to reject it.
Reproduction
Using the Windows-MCP extension (CursorTouch/Windows-MCP, installed as a Claude Desktop Extension), its Click tool has this signature:
def click_tool(
loc: list[int] | None = None,
label: int | None = None,
button: Literal["left", "right", "middle"] = "left",
clicks: int = 1,
ctx: Context = None,
) -> str:
Calling Click with loc: [1271, 2106] fails server-side with:
1 validation error for call[click_tool]
loc
Input should be a valid list [type=list_type, input_value='[1271, 2106]', input_type=str]
Note input_type=str — the value arrived as the literal string '[1271, 2106]', not an array.
This is not a server-side bug — I verified this directly by reproducing the exact registered tool function (including its with_analytics wrapper) and running it through FastMCP's actual ParsedFunction.from_function() pipeline in isolation. The generated schema is fully correct:
"loc": {
"anyOf": [
{"items": {"type": "integer"}, "type": "array"},
{"type": "null"}
],
"default": null
}
Proper "type": "array" and "items" are present. So the server advertises the correct schema, and correctly rejects the malformed input it receives — the defect is in how the array value gets serialized into the actual tool call before it reaches the server.
Why this is easy to miss: scalar-typed optional parameters (e.g. label: int | None) on the same tool work fine when passed as plain text, because the server's validation leniently coerces a numeric string into an int. That leniency doesn't extend to list/array types, so this defect is silently masked for every scalar parameter and only surfaces as a hard failure for array-typed ones — every attempt to pass coordinates as [x, y], in any bracket/spacing format, fails identically.
Impact: any MCP tool with a list-typed parameter (coordinates, multi-value selections, etc.) is unusable via this path. In this case it blocked all coordinate-based mouse interaction through the Windows-MCP extension.
What I've ruled out: the target MCP server's schema generation and validation (verified correct via direct reproduction above). I don't have visibility into Claude Code's internal source to point at the exact serialization step, but the failure point is somewhere between tool-call construction and the value reaching the server.