Skip to content

fix(llm): Anthropic rejects top-level anyOf/oneOf/allOf in tool input_schema #38616

Description

@ajhochy

Summary

Anthropic's Messages API rejects tool definitions whose input_schema has anyOf, oneOf, or allOf at the top level of the object, returning:

Error: tools.2.custom.input_schema: input_schema does not support oneOf, allOf, or anyOf at the top level

This crashes the entire session — no response is returned, and the agent errors out immediately.

Root Cause

ProviderTransform.schema() in packages/opencode/src/provider/transform.ts has per-provider schema sanitization for Moonshot/Kimi and Google/Gemini, but no sanitization for Anthropic. When an MCP tool produces a JSON Schema with a top-level combiner (e.g. { anyOf: [...] }), it is passed unmodified to the Anthropic Messages API, which rejects it.

PR #1100 (fix(llm): emit anyOf as sole schema key for Vertex tool defs) fixed the same class of problem for Vertex/Gemini but did not cover @ai-sdk/anthropic.

Note: json-schema.ts's normalize() already collapses single-element anyOf arrays and flattens other common cases — but this runs at the tool-definition build time for opencode-native tools. MCP tools that arrive with their schemas pre-formed (tool.inputSchema set directly, not derived via ToolJsonSchema.fromTool()) can bypass that normalization and still carry a top-level combiner into lowerTool()input_schema: tool.inputSchema.

Affected Code

packages/opencode/src/provider/transform.ts — the schema() export function (currently ~line 1339). The Gemini branch (lines ~1377–1453) rewrites integer enums and enforces items on arrays but does not strip top-level combiners. There is no Anthropic branch at all.

Proposed Fix

Add an Anthropic-specific pass in schema() that:

  1. Detects @ai-sdk/anthropic (and @ai-sdk/google-vertex/anthropic) via model.api.npm
  2. Recursively walks the schema and, at any level where anyOf/oneOf/allOf is the only key (or the sole meaningful key), unwraps the single-element case to the element itself — mirroring what normalize() in json-schema.ts already does
  3. For multi-element top-level combiners that cannot be safely collapsed (e.g. a true union of two different object shapes), falls back to { type: "object" } to avoid the API rejection — a lossy but non-crashing result

The fix should be consistent with the existing normalize() logic in json-schema.ts lines 56–76.

Repro

  1. Configure an agent with @ai-sdk/anthropic as the provider
  2. Connect an MCP server whose tools expose a schema with a top-level anyOf (e.g. an optional/nullable parameter encoded as { anyOf: [{ type: "string" }, { type: "null" }] } at the root of input_schema)
  3. Send any message — the first API call immediately fails with the error above

Environment

  • opencode fork (Rhythm), observed on claude-opus-4-8 and claude-sonnet-4-6 via @ai-sdk/anthropic
  • The error is provider-side validation, not a transient failure — it reproduces on every call

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions