cloudflare / cloudflare/playwright-mcp
@cloudflare/playwright-mcp ships invalid tool inputSchemas when zod@4 is hoisted
- Dominant language
- TypeScript
- Stars
- 258
- Forks
- 50
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
`@cloudflare/playwright-mcp@0.0.5` does not declare `zod` in its `dependencies` and uses `zod-to-json-schema@^3.24.6`, which is [deprecated as of Nov 2025](https://github.com/StefanTerdell/zod-to-json-schema#notice-of-deprecation) and only converts zod-v3 schemas. As soon as zod@4 wins hoisting in a downstream tree, every tool's `inputSchema` collapses to `{"$schema": "..."}` — no `type: "object"`, no `properties`. Strict MCP clients like MCP Playground then reject every tool.
This is the same root cause as #28, which was closed without a library-side fix. The acknowledgement there was that pinning `zod@3` in the library's own `package.json` would prevent it; that pin was never added to the published `@cloudflare/playwright-mcp` package.
## Reproduction
In a downstream consumer that uses `@cloudflare/playwright-mcp@0.0.5`, after `@modelcontextprotocol/sdk` is bumped to a version with peer `zod: ^3.25 || ^4.0` (e.g. 1.29.0), `npm install` resolves `zod@4` at the top level. The library imports `zod` and `zod-to-json-schema` from there (no nested copy after dedupe).
```js
// inside the library's connection.js
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
zodToJsonSchema(z.object({ url: z.string() }));
// → { "$schema": "http://json-schema.org/draft-07/schema#" } ❌ no type, no properties
```
Strict MCP clients then emit, for every tool:
```
{ "code": "invalid_value", "values": ["object"], "path": ["tools", N, "inputSchema", "type"], "message": "Invalid input: expected \"object\"" }
```
## Proposed fixes (either is sufficient)
1. **Pin zod in the published package** — add `"zod": "^3"` to `dependencies` in `cloudflare/package.json`. Smallest possible change; what was discussed in #28.
2. **Migrate to `z.toJSONSchema`** — Zod 4 has [native JSON Schema support](https://zod.dev/json-schema). Replace the `zod-to-json-schema` import in `connection.js` with `z.toJSONSchema(tool.schema.inputSchema)` and bump the zod dep to `^4`. Drops the deprecated dependency entirely.
Option (2) is the durable fix; option (1) unblocks consumers immediately.
## Workaround for downstreams (today)
```jsonc
// downstream package.json
"overrides": {
"zod": "^3.25.0"
}
```
This forces zod@3 across the dep tree and restores correct schema generation. Verified end-to-end with MCP Inspector against a deployed Worker.
## Why this keeps biting
- Library's `package.json` doesn't pin `zod`, so it inherits whatever a consumer's tree resolves.
- `zod-to-json-schema` v3.25 lets you list zod@4 as a peer **but only converts v3 schemas** ([README](https://github.com/StefanTerdell/zod-to-json-schema#notice-of-deprecation)). When the downstream `zod` is v4, schemas pass through but are no-ops — silent failure.
- Lenient MCP clients accept the broken JSON Schema, so the bug only surfaces on stricter clients, making it look client-specific.
Contributor guide
Assessment
This issue has not been assessed yet.