Skip to content

[AISAGE-736] fix(ai-gateway): drop top-level schema combinators from Claude tools - #7646

Merged
chrarnoldus merged 2 commits into
mainfrom
kilo/bright-boson-l7q
Oct 16, 2026
Merged

chrarnoldus merged 2 commits into
mainfrom
kilo/bright-boson-l7q

Conversation

@chrarnoldus

@chrarnoldus chrarnoldus commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Anthropic rejects tools whose schema has oneOf, allOf or anyOf at the root:

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

This is hit by e.g. the nteract MCP nteract_create_cell tool, which uses a root oneOf to require either notebook_handle or notebook_id.

Like the existing Muse (recursive $ref) and Gemini ($ref in tool results) workarounds, applyProviderSpecificLogic now rewrites tool schemas for Claude models (isClaudeModel) across Chat Completions, Responses and Messages requests via removeTopLevelToolSchemaCombinators:

  • Root allOf/anyOf/oneOf are removed. The flattened schema accepts every input the original accepted; the tool server still validates its own input.
  • Properties declared only inside a branch are added to the root, so additionalProperties: false does not forbid them. Root property definitions win over branch ones.
  • required follows the combinator semantics, as in fix: remove top-level anyOf/oneOf/allOf from tool input schemas for Anthropic API compatibility zereight/gitlab-mcp#558: the union of all allOf branches, and the intersection of anyOf/oneOf branches (a property only stays required if every branch requires it).
  • If the root had no type, it gets type: "object".
  • The removed constraints are appended to the tool description as compact JSON, e.g. The input must also match exactly one of these JSON schemas: [...]. Without strict mode Anthropic only shows the schema to the model and does not validate inputs, so this keeps the information the model would have seen. Anthropic's tool docs call descriptions the most important factor and ask for caveats to be included. The text is the same for every request, so prompt caching is unaffected, and the total size stays roughly the same since the branches move from the schema to the description.
  • strict is kept. Because the flattened schema is a superset of the original, strict mode still enforces types, required properties and additionalProperties of what remains; dropping strict would only loosen validation further.
  • Nested combinators are left untouched.

The Kilo CLI's agent_manager tool does the same thing by hand: it advertises a flat WireParams object, describes per-operation rules in field descriptions, and validates the real union on execution.

Verification

  • No manual end-to-end test against Anthropic was run from the sandbox. The unit tests cover the reported nteract_create_cell schema, required merging per combinator, description appending (with and without an existing description), strict preservation, each request kind, and the Claude-only gating in applyProviderSpecificLogic.

Visual Changes

N/A

Reviewer Notes

  • The rewrite still loosens validation: the model is told the constraint in the description, but neither Anthropic nor strict mode enforces it.
  • A strict tool whose schema uses keywords outside Anthropic's strict-mode subset could still be rejected after flattening; that is unchanged by this PR.

@chrarnoldus chrarnoldus self-assigned this Oct 15, 2026
@kilo-code-bot

kilo-code-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The follow-up commit f46bef976 refines the Claude tool-schema rewrite (required-set computation, description text, strict preservation); the changed logic is sound and no new defects were found.

Files Reviewed (3 files)
  • packages/web-shared/src/lib/ai-gateway/providers/remove-top-level-tool-schema-combinators.ts
  • packages/web-shared/src/lib/ai-gateway/providers/remove-top-level-tool-schema-combinators.test.ts
  • packages/web-shared/src/lib/ai-gateway/providers/apply-provider-specific-logic.test.ts

This incremental review re-checked the changes on top of de694dbcd. The helper now computes required properties per combinator (union for allOf, intersection for anyOf/oneOf), which is a sound over-approximation and mirrors the documented combinator semantics; it returns the removed constraints as text and the wiring appends them to the tool description per request kind while preserving strict. The malformed-combinator, nested-combinator, missing-description, and multi-combinator edge cases are covered by tests, and the Claude-only gating in apply-provider-specific-logic.ts is unchanged (not part of this increment). removeTopLevelToolSchemaCombinators is idempotent since the combinator is deleted on first pass, so re-processing a reused request body will not duplicate the appended description. No security, correctness, or reliability defects were found in the changed lines. The test suite was not executed (read-only review).

Previous Review Summary (commit de694db)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit de694db)

Status: No Issues Found | Recommendation: Merge

I reviewed all four changed files in this PR: the new removeTopLevelToolSchemaCombinators helper (and its unit tests) and the applyProviderSpecificLogic wiring that applies it for Claude models.

The helper mirrors the established removeRecursiveToolSchemaRefs pattern: it iterates tools per request kind (chat_completions → function.parameters, responses → parameters, messages → input_schema), deletes root-level allOf/anyOf/oneOf, folds branch-declared properties onto the root so additionalProperties: false still permits them, preserves allOf required entries, defaults a missing root type to object, and leaves nested combinators untouched. The mutation is correctly gated by isClaudeModel, is lossy but non-failing by design (documented in the PR), and the tests cover the reported nteract_create_cell schema, every request kind, and the Claude-only gating. No security, correctness, or reliability defects were found in the changed lines. I did not execute the test suite (read-only review).

Files Reviewed (4 files)
  • packages/web-shared/src/lib/ai-gateway/providers/remove-top-level-tool-schema-combinators.ts - new helper, no issues
  • packages/web-shared/src/lib/ai-gateway/providers/remove-top-level-tool-schema-combinators.test.ts - new tests, no issues
  • packages/web-shared/src/lib/ai-gateway/providers/apply-provider-specific-logic.ts - Claude gating wiring, no issues
  • packages/web-shared/src/lib/ai-gateway/providers/apply-provider-specific-logic.test.ts - added coverage, no issues

Reviewed by deepseek-v4.1-flash · Input: 60.2K · Output: 13.4K · Cached: 680.6K

Review guidance: REVIEW.md from base branch main

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants