anthropics / anthropics/claude-code

MCP tool-call validation rejects omission of any optional/defaulted parameter, regardless of type

Open
#95,212 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

## Summary
When calling an MCP tool whose JSON Schema declares an optional parameter (one with a `default` value), Claude Code's tool-call argument validator rejects the call if that parameter is omitted — even though the server's schema correctly marks it optional. The error is client-side, before the request reaches the MCP server:
```
Invalid arguments for tool : [{"code":"invalid_type","expected":"nonoptional","path":[""],"message":"Invalid input: expected nonoptional, received undefined"}]
```

## Reproduction
A self-hosted FastMCP-based MCP server (Python, streamable-http) exposes:
```python
def search(query: str, filter_type: str | None = None, page_size: int = 10, start_cursor: str | None = None) -> str: ...
```
FastMCP advertises `page_size` as `{"type": "number", "default": 10}` (plain, non-nullable) and `filter_type`/`start_cursor` as `{"anyOf": [{"type": "string"}, {"type": "null"}], "default": null}`. None appear in the top-level `required` array.

Calling the tool with only `query` set (letting the rest take their declared defaults) fails client-side with the error above for **every** omitted param — `page_size` included.

**`page_size` is the key data point**: it is not nullable and carries no `anyOf` union, so "the client strips `anyOf` unions" cannot explain this. It fails identically to the nullable string params, which points instead at every parameter carrying a `default` being validated as if `.optional()` was never applied to it — a `.nullable()`-without-`.optional()`-shaped gap — regardless of whether the field is actually nullable.

## What I inspected
The tool definition loaded inside my session (surfaced via the tool-search/deferred-tool mechanism) has **no `required` array at all**, e.g. for a different tool on the same server:
```json
{"properties": {"markdown": {"default": null}, "page_id": {"type": "string"}, "text": {"default": null}}, "type": "object"}
```
The upstream server's own advertised schema (verified independently via a local `tools/list` probe against the same server) correctly has `"required": ["page_id"]` and full type info (`anyOf: [string, null]`) on `text`/`markdown`. Something between the server's schema and whatever validates my tool calls drops the `required` array entirely and, for nullable fields, collapses the type info down to bare `{"default": null}`.

## Impact
Any MCP server with an optional/defaulted parameter is affected, not just this one. A caller has no way to omit such a parameter — it must be supplied explicitly, with a real value, on every call, even when the intent is "just use the default." This defeats the purpose of an optional parameter and makes tool schemas misleading: they advertise optional, the runtime enforces required.

## Workaround
None client-side. Server implementers can mitigate for **string** params by treating an empty string `""` as "not provided," but this doesn't work for `bool`/`int` params (no safe empty sentinel without risking a real `0`/`false`), and it's a per-server patch for a client-side defect.

## Environment
- Claude Code (desktop app)
- MCP server: custom FastMCP-based server (Python), streamable-http transport
- Observed 2026-09-17

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by tracing the schema from the MCP tools/list response through the tool-search/deferred-tool mechanism into the tool-call argument validator. Use the reported FastMCP schema and the omitted-parameter reproduction to verify that defaulted optional parameters are accepted while fields listed as required remain rejected when omitted.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.