Skip to content

fix(ai): keep tool schemas within Meta's limits - #51040

Closed
rekram1-node wants to merge 1 commit into
devfrom
muse-sanitizer
Closed

rekram1-node wants to merge 1 commit into
devfrom
muse-sanitizer

Conversation

@rekram1-node

Copy link
Copy Markdown
Collaborator

V2 fix for #50297, #45800, and #48151.

Problem

Meta's API rejects the whole request when any tool schema:

This hits Muse Spark on Meta's API and through Go, Zen, and gateways that forward to Meta.

Changes

  • New protocols/utils/meta-json-schema.ts (MetaJsonSchema.normalize), selected with sanitizer: "meta".
    • It measures depth the way Meta does, confirmed with live probes: one level for every properties or items key holding an object anywhere in the schema, including property names and values such as default. It follows $ref and ignores unreferenced $defs.
    • Schemas within the limits are sent unchanged.
    • Otherwise, local $refs are inlined and a recursive $ref becomes {} with its sibling keywords. At level 10, properties and items are removed (along with additionalProperties, patternProperties, and unevaluated*), so that part accepts any object or array. const, default, enum, and examples values that would exceed the limit are dropped. $ref siblings merge into the target unless they overlap one of its constraints, in which case both go under allOf.
  • A model name containing muse-spark selects it, like gemini and kimi. The match is not just muse because Muse Glimmer 30B is an open-weight model on Fireworks, Together, DeepInfra, and NVIDIA, which don't run Meta's validator.
  • The Meta provider's Responses, Chat, and Messages models set sanitizer: "meta" for every model, since the limits come from Meta's API.
  • Add "meta" to LanguageModelSanitizerCompatibility.

Verification

  • bun test in packages/ai: 1512 pass. bun run check: 35/35.
  • Offline, over 235 schemas (Vercel's OpenAPI request bodies, published Vercel MCP schemas, and a synthetic corpus), exactly 11 change: the 5 Vercel flag schemas over 10 levels, 3 recursive schemas, and 3 synthetic deep chains. Every output is within 10 levels and has no $ref left.
  • Live through V2:
    • Meta Responses accepts all 11 changed schemas.
    • A recursive schema and MCP create_flag are accepted on Meta Chat, on Meta Messages, and on Go with muse-spark-1.3-contributor, which gets the sanitizer by name.
    • With sanitizer: "none", the same two are rejected with Meta's recursion and depth errors.
  • Two live create_flag calls from muse-spark-1.3 using the trimmed schema both validate against the original, untrimmed schema.

References that aren't plain local JSON pointers (percent-encoded names, $anchor) are not inlined, the same as before this change.

Meta rejects tool schemas nested more than 10 levels deep and any recursive
$ref. Add a meta sanitizer that sends schemas within those limits unchanged
and otherwise inlines references, cuts recursive ones, and leaves structure
past the limit unconstrained. Meta routes apply it to every model, and
Muse Spark models get it by name on other hosts.
@thdxr
thdxr changed the base branch from v2 to dev October 10, 2026 19:16
@satyalyadav

Copy link
Copy Markdown

Verification on v2.0.26: the native-tool path still reproduces, and the muse-spark matcher this PR adds covers it.

Minimal repro:

  • A local stdio MCP server publishing one tool whose inputSchema is an object chain nested 12 levels deep (level_0 … level_11).
  • The server exposed natively so the schema reaches the provider:
{
  "mcp": {
    "servers": {
      "deepschema": { "type": "local", "command": ["node", "deep-mcp.mjs"], "codemode": false }
    }
  }
}
$ opencode run --standalone -m opencode-go/muse-spark-1.3-contributor "Reply with exactly: ok"
Error: JSON schema exceeds the maximum nesting depth of 10 levels

With Code Mode left at its default the same server passes unchanged, because the deep schema never reaches Meta. Worth knowing when retesting: without codemode: false it looks fixed.

Note: the branch currently conflicts with dev (mergeable_state: dirty), so it needs a rebase before it can land.

@github-actions

Copy link
Copy Markdown
Contributor

Automated PR Cleanup

Thank you for contributing to opencode.

Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions.

This PR was closed because it matched the following cleanup criteria:

  • The PR was created more than 1 month ago
  • The PR had fewer than 2 positive reactions
  • Positive reactions are counted as thumbs-up, heart, celebration, or rocket reactions on the PR

PRs created within the last month are not affected by this cleanup.

If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate.

Thanks again for taking the time to contribute.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants