MCP JSON Schema: additionalProperties `{}` normalized to `false`, breaking z.record() fields

Status Open
Maintainer reply None cached
Activity 1 comment · opened Jul 17, 2026

Description

When Claude Code loads tools from an MCP server, it normalizes JSON Schema "additionalProperties": {} (empty schema = accept everything) to "additionalProperties": false (reject everything). These mean opposite things in JSON Schema.

Reproduction

  1. Register an MCP tool with a z.record(z.string(), z.any()) field:
server.tool("my_tool", "description", {
  goal: z.string(),
  input: z.record(z.string(), z.any()).describe("Key-value pairs"),
}, handler);
  1. The MCP SDK correctly generates JSON Schema:
"input": { "type": "object", "additionalProperties": {} }
  1. But Claude Code presents this to the LLM as:
"input": { "type": "object", "additionalProperties": false, "properties": {} }
  1. The LLM sees "no properties, no additional properties" and never populates the field — it sends {} or omits it entirely.

Expected behavior

"additionalProperties": {} should be preserved as-is (or normalized to "additionalProperties": true, which is semantically equivalent). It should NOT be converted to false.

JSON Schema spec reference

Per JSON Schema draft-07:

  • "additionalProperties": {} — empty schema, matches any value (accept all additional properties)
  • "additionalProperties": true — equivalent to empty schema
  • "additionalProperties": false — reject all additional properties

Impact

Any MCP server using z.record() for dynamic key-value inputs (common for form data, configuration objects, metadata) has those fields silently broken. The LLM can see the field description but the schema tells it the object accepts nothing.

Workaround

Using z.object({}).passthrough() instead of z.record() produces "additionalProperties": true (explicit boolean) which Claude Code preserves correctly.

Environment

  • Claude Code version: latest as of 2026-07-17
  • MCP SDK: @modelcontextprotocol/sdk@1.29.0
  • zod-to-json-schema@3.25.1

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗