anthropics / anthropics/claude-code
MCP client rejects tool calls omitting optional parameters; deadlocks on tools with mutually-exclusive parameter groups
- Dominant language
- Python
- Stars
- 145k
- Forks
- 23.1k
- PR merge metrics
- PR metrics pending
Description
## Summary
Every MCP tool call that omits an optional parameter is rejected with a Zod-style
`invalid_type` / `expected: "nonoptional", received: "undefined"` error for that
parameter — even though the tool's own JSON Schema declares a `default` for it.
Supplying the parameter explicitly (including as `null`/empty string) works, but
for tools that also enforce "provide exactly one of A or B" / "field X must be
omitted when Y is set" business rules, this creates a genuine deadlock: the outer
layer requires every optional field to be present, while the tool's own logic then
rejects that same field's presence. There is no parameter combination that
satisfies both, so some tool actions (e.g. HACS `update_information`, `ws_command`
calls with `entity_id`, entity-registry `unique_id` lookups) cannot be invoked at
all right now.
This affects an MCP server ("ha-mcp") connected via a local stdio subprocess
(`uvx ha-mcp@latest`), and reproduces identically against two completely
different Home Assistant instances (the user's real instance and an unrelated
public demo instance), and across at least two ha-mcp server versions (8.4.2 and
8.4.3). That rules out the HA instance and the server package version as the
cause and points at the client-side tool-calling/validation layer.
## Environment
- Client: Claude Code, running inside the Claude Desktop app ("Code" tab), macOS
- macOS: updated the day before this bug first appeared, to macOS 27 "Golden
Gate" (exact build/beta number not captured — please fill in from `sw_vers`)
- MCP server: `ha-mcp` (PyPI), launched via:
```
~/.local/bin/uvx --python 3.13 --refresh ha-mcp@latest
```
configured in `claude_desktop_config.json` under `mcpServers.home-assistant`
- ha-mcp versions tested: 8.4.3 (original), 8.4.2 (pinned to rule out a
regression) — bug identical on both
- Home Assistant instances tested: a primary self-hosted instance (HA core
2026.9.2) and an unrelated public demo instance (HA core 2026.6.4) — bug
identical on both, ruling out anything instance-specific
- Companion HACS integration (`ha_mcp_tools` / `homeassistant-ai/ha-mcp-integration`):
updated from stable `v2.1.3` to dev snapshot `v2.2.0-dev.386` mid-investigation,
in case of a client↔server↔component version-skew issue — bug persisted
unchanged afterward
## Steps to reproduce
1. Connect to any MCP server exposing a tool with one or more optional
parameters that have declared JSON Schema `default` values.
2. Call the tool supplying only the required parameters, omitting the optional
ones (i.e. the normal/expected calling convention).
3. Observe the call is rejected before it reaches the server's own business
logic.
## Example 1 — basic optional-field rejection
Call:
```
ha_get_state(entity_id="sun.sun")
```
(`fields` and `attribute_keys` are both declared as `{"default": null}` in the
tool's schema.)
Result:
```json
{
"error": "MCP error -32602: Input validation error: Invalid arguments for tool ha_get_state: [
{\"code\":\"invalid_type\",\"expected\":\"nonoptional\",\"path\":[\"fields\"],\"message\":\"Invalid input: expected nonoptional, received undefined\"},
{\"code\":\"invalid_type\",\"expected\":\"nonoptional\",\"path\":[\"attribute_keys\"],\"message\":\"Invalid input: expected nonoptional, received undefined\"}
]"
}
```
Workaround: explicitly pass `fields` and `attribute_keys` (e.g. as a real list,
or as the literal string `null` for list-typed fields — note this is
*not* JSON `null`, it becomes the one-element list `["null"]` server-side,
which is itself a separate minor bug but harmless for read calls).
## Example 2 — the deadlock (no workaround exists)
Tool `ha_call_service` accepts either a normal `domain`/`service`/`entity_id`
service call, or a `ws_command` one-shot WebSocket command — mutually exclusive,
enforced by the tool's own validation:
```json
{
"error": "These parameters apply only to service calls and must be omitted when ws_command is set: entity_id, result_fields, result_attribute_keys."
}
```
But the outer layer separately requires `ws_command` to be present on every
call:
```json
{
"code": "invalid_type",
"expected": "nonoptional",
"path": ["ws_command"],
"message": "Invalid input: expected nonoptional, received undefined"
}
```
There is no value for `ws_command` (empty string, JSON `null`, omitted) that
satisfies both "must be present" and "must be absent when doing a plain service
call" simultaneously. Plain service calls (domain+service+entity_id, no
ws_command) are therefore currently **impossible** to make without hitting one
error or the other.
The same deadlock shape reproduces on:
- `ha_get_integration` (`entry_id` vs. `domain` query modes)
- `ha_get_entity` ("provide exactly one of entity_id or unique_id")
- `ha_manage_hacs` (each `action` value has its own required/forbidden
parameter subset, e.g. `update_information` forbids `category`/`repository`/
`version`, but the outer layer demands they be present)
## What was ruled out
- **Not the HA instance**: identical failure on two unrelated HA installs.
- **Not the ha-mcp server version**: identical on 8.4.2 and 8.4.3.
- **Not the companion HACS component version**: identical before and after
updating `ha_mcp_tools` from v2.1.3 to v2.2.0-dev.386 (+ HA restart).
- **Not a one-off stale connection**: identical immediately after multiple full
restarts of the local `uvx ha-mcp@latest` subprocess (via killing/relaunching
the Claude Desktop app), and after removing a stray second/duplicate
`mcpServers` entry for the same server (which had inadvertently been the one
reconnected after one restart, briefly pointing the session at the wrong
backend — a separate, secondary finding worth a look: on restart the client
reconnected to a different-but-similarly-named `mcpServers` entry than the one
most recently active, rather than the one used before the restart).
## Suspected cause
Given the failure reproduces across every tested combination of server
version, HA backend, and companion-component version, but is 100% consistent
in *shape* (missing-optional-field rejection, plus the exact-one-of deadlock)
across many different tools from the same server, the most likely location is
the client-side code that takes each tool's declared JSON Schema and constructs
the outbound tool-call arguments / validates them before dispatch — i.e.
something in this session's MCP tool-calling layer is not respecting a
schema property's `default`/optional-ness and is instead requiring literal
presence of every declared key.
## Workaround in use
Passing an explicit, schema-valid value for every declared optional parameter
(never omitting any) avoids the first class of error for tools that don't also
have mutually-exclusive parameter groups. Tools with genuine "exactly one of"
requirements remain unusable via this MCP connection until this is fixed.
## Request
Please advise whether this is a known issue, and if not, what additional
diagnostics (e.g. the raw outbound JSON-RPC `tools/call` payload for a failing
request) would help pin down where the extra/missing parameters are being
introduced.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by capturing the raw outbound JSON-RPC tools/call payload requested in the issue and compare omitted optional fields with the tool's declared JSON Schema. Trace the MCP tool-calling and validation layer to determine where optional parameters become required; done means omitted defaults are accepted and mutually exclusive tool calls reach the server without contradictory validation errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100