Skip to content

fix: resolve $ref pointers from recursive Zod schemas#1054

Open
4444J99 wants to merge 1 commit intocoinbase:mainfrom
4444J99:fix/815-resolve-recursive-zod-schema-refs
Open

fix: resolve $ref pointers from recursive Zod schemas#1054
4444J99 wants to merge 1 commit intocoinbase:mainfrom
4444J99:fix/815-resolve-recursive-zod-schema-refs

Conversation

@4444J99
Copy link
Copy Markdown

@4444J99 4444J99 commented Mar 28, 2026

Description

Fixes #815

zodToJsonSchema() and z.toJSONSchema() emit $ref pointers when a Zod type is referenced more than once (shared sub-schemas) or uses z.lazy() for recursion. OpenAI's function-calling API rejects schemas containing $ref with BadRequestError: 400 Invalid schema ... object schema missing properties.

This PR adds a resolveJsonSchemaRefs() utility that inlines $ref definitions up to a configurable depth (default: 5), replacing deeper levels with a permissive empty schema ({}), and strips the $defs/definitions block from the output.

Integration approach:

  • MCP extension: Integrated directly into getMcpTools() where AgentKit controls the z.toJSONSchema() conversion, so ref resolution happens transparently for all MCP users.
  • LangChain / Vercel AI SDK: These extensions pass raw Zod schemas to their respective tool() functions, which handle JSON Schema conversion internally. The utility is exported from @coinbase/agentkit so users of these extensions can apply it to their own zodToJsonSchema() output when needed.

Usage for LangChain/Vercel AI SDK users with recursive schemas:

import { resolveJsonSchemaRefs } from "@coinbase/agentkit";
import { zodToJsonSchema } from "zod-to-json-schema";

const jsonSchema = zodToJsonSchema(myRecursiveZodSchema);
const resolved = resolveJsonSchemaRefs(jsonSchema);

Tests

10 unit tests for the core utility covering:

  • Simple $ref from $defs and definitions
  • Recursive schemas with depth limiting
  • Shared sub-schemas in unions (exact reproduction of issue Recursive Zod Schemas lead to Invalid Schema in OpenAI #815)
  • Output stripping of definition blocks
  • Primitive passthrough, unrecognized $ref paths, null values
  • Default max depth behavior

2 integration tests for the MCP extension:

  • Existing tool/handler contract preserved
  • Shared sub-schemas produce no $ref or $defs in output
PASS src/resolveJsonSchemaRefs.test.ts (10 tests)
PASS src/index.test.ts (2 tests)

Lint and format checks pass.

Checklist

  • Added a changelog entry
  • Added documentation to all relevant README.md files

zodToJsonSchema() and z.toJSONSchema() emit $ref pointers for recursive
(z.lazy) and shared Zod types. OpenAI's function-calling API rejects
schemas containing $ref with "object schema missing properties".

Add resolveJsonSchemaRefs() utility that inlines $ref definitions up to
a configurable depth. Integrate it into the MCP framework extension
where AgentKit controls the JSON Schema conversion. Export from
@coinbase/agentkit for LangChain and Vercel AI SDK users.

- 10 unit tests for the core utility
- 2 tests for MCP extension integration (including shared sub-schema case)
- Lint and format clean

Closes coinbase#815
@4444J99 4444J99 requested a review from murrlincoln as a code owner March 28, 2026 14:49
@cb-heimdall
Copy link
Copy Markdown

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@github-actions github-actions bot added documentation Improvements or additions to documentation framework extension New framework extension typescript labels Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation framework extension New framework extension typescript

Development

Successfully merging this pull request may close these issues.

Recursive Zod Schemas lead to Invalid Schema in OpenAI

2 participants