MCP write tools silently drop bodies when schema is empty
Mirza Iqbal discovered that using `z.unknown().optional()` as a body schema in MCP passthrough tools compiles to an empty JSON schema, causing several MCP clients to silently drop the request body on POST calls.
Score breakdown
Audit every MCP tool that uses `z.unknown()` or an untyped body input — replacing it with a concrete schema prevents clients from silently dropping POST bodies in ways that are nearly impossible to debug from server logs alone.
- 01Using `z.unknown().optional()` as a body schema compiles to an empty JSON schema in Zod.
- 02Several MCP clients silently drop request body properties whose schema is empty.
- 03GET requests are unaffected; the bug only surfaces on POST (write) calls.
Mirza Iqbal ran into a silent failure while building an open-source MCP server for Hetzner: any passthrough tool that typed its body as `z.unknown().optional()` would compile down to an empty JSON schema. Several MCP clients interpret an empty schema as having nothing to validate and silently drop the property entirely. Because GET requests carry no body, they passed through without issue — the bug only manifested on POST calls, where the upstream service received an empty body and returned an error that appeared to originate remotely rather than from the MCP server itself.
The fix is a one-line schema change: replacing `z.unknown().optional()` with `z.union([z.record(z.string(), z.unknown()), z.string()]).optional()`.
The fix is a one-line schema change: replacing `z.unknown().optional()` with `z.union([z.record(z.string(), z.unknown()), z.string()]).optional()`. This gives the body a concrete, non-empty shape. A string body should then be parsed into an object before use. Iqbal also notes that the bug only appeared at the client boundary, not in unit tests, underscoring the need to test write tools through the fully built server against a real service.
Iqbal draws two takeaways: audit any tool input typed as `unknown` or left untyped and replace it with a real schema, and ensure write tools are integration-tested end-to-end rather than relying solely on unit tests. The Hetzner MCP server where this was found is open source under the MIT license and installable via `npx -y hetzner-mcp`.
Key facts
- 01Using `z.unknown().optional()` as a body schema compiles to an empty JSON schema in Zod.
- 02Several MCP clients silently drop request body properties whose schema is empty.
- 03GET requests are unaffected; the bug only surfaces on POST (write) calls.
- 04The error appears to come from the upstream service, not the MCP server, making it hard to diagnose.
- 05The fix is replacing the schema with `z.union([z.record(z.string(), z.unknown()), z.string()]).optional()`.
- 06The bug only appeared at the client boundary, not in unit tests.
- 07The affected Hetzner MCP server is open source under the MIT license, installable via `npx -y hetzner-mcp`.
Topics
Summary and scoring are generated automatically from the original article. We always link back to the publisher and never republish images or paywalled content. Last processed Jun 8, 2026 · 15:36 UTC. How this works →