Skip to content

emulate tool schema uses anyOf, rejected by Claude API #918

Description

@ioku24

Problem

The emulate tool fails when used with Claude Code (and the Claude API in general) because the tool's input schema contains anyOf, which the Claude API does not support.

Error:

tools.N.custom.input_schema: input_schema does not support oneOf, allOf, or anyOf

Root Cause

In build/src/tools/emulation.js, the geolocation and viewport fields use .nullable().optional() on Zod objects:

geolocation: zod
    .object({
        latitude: zod.number().min(-90).max(90),
        longitude: zod.number().min(-180).max(180),
    })
    .nullable()   // <-- produces anyOf
    .optional()

viewport: zod
    .object({
        width: zod.number().int().min(0),
        height: zod.number().int().min(0),
        deviceScaleFactor: zod.number().min(0).optional(),
        isMobile: zod.boolean().optional(),
        hasTouch: zod.boolean().optional(),
        isLandscape: zod.boolean().optional(),
    })
    .nullable()   // <-- produces anyOf
    .optional()

The bundled zod-to-json-schema converter in third_party/index.js (line ~22174) translates .nullable() on objects to:

{
  "anyOf": [
    { "type": "object", "properties": { ... } },
    { "type": "null" }
  ]
}

Note: .nullable() on primitives (like userAgent: zod.string().nullable()) is fine — the converter uses type: ["string", "null"] for those. The issue only affects .nullable() on object schemas.

Impact

  • Version affected: 0.16.0 (latest via npx)
  • Tool affected: emulate only (the other 25+ tools work fine)
  • Who's affected: Anyone using this MCP server with Claude Code or the Claude API

Suggested Fix

Replace .nullable().optional() with .optional() and handle null clearing logic at the tool handler level. For example:

// Before:
geolocation: zod.object({ ... }).nullable().optional()

// After:
geolocation: zod.object({ ... }).optional()
// Handle "clear geolocation" via a separate boolean flag or empty object convention

Alternatively, post-process the generated JSON Schema to replace anyOf: [{object}, {type: "null"}] with just the object schema marked as optional.

Environment

  • chrome-devtools-mcp v0.16.0 (via npx)
  • Claude Code CLI
  • macOS (Apple Silicon)

Activity

  1. OrKoN commented on Feb 9, 2026

    @OrKoN
    Collaborator

    @ioku24 do you have an upstream issue for claude? this does sounds like Claude API fails to support JSON schema according to the MCP spec.

  2. OrKoN commented on Feb 9, 2026

    @OrKoN
    Collaborator

    Looks like this is the bug on the claude side anthropics/claude-code#4886

  3. ioku24 commented on Feb 9, 2026

    @ioku24
    Author

    Thats fair. this is ultimately a Claude API limitation, and I've upvoted the upstream issue. That said, since Claude Code is a significant MCP client, would you consider dropping .nullable() on the geolocation and viewport objects? .optional() alone covers the "not provided" case, and it would make the schema compatible without any functional loss. Happy to submit a PR if that's welcome.

  4. OrKoN commented on Feb 9, 2026

    @OrKoN
    Collaborator

    Actually, I just tested with 2.1.37 (Claude Code) and I am not able to reproduce the issue.

  5. OrKoN commented on Feb 9, 2026

    @OrKoN
    Collaborator

    Is the Claude API some other configuration for Claude Code (not the default one with a subscription)?

  6. OrKoN commented on Feb 9, 2026

    @OrKoN
    Collaborator

    @ioku24 without nullable there it would not be possible to reset the emulation, as undefined leaves it as-is and null clears it. Having separate commands or attributes just for clearing the value seems wasteful.

  7. added theissue type on Feb 9, 2026
  8. OrKoN commented on Feb 26, 2026

    @OrKoN
    Collaborator

    As I was not able to repro with 2.1.37 (Claude Code) I suspect it might have been been fixed on their side. cc @natorion

  9. OrKoN commented on Mar 2, 2026

    @OrKoN
    Collaborator

    I think the following would be actually a good workaround for this as well #1073

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions