fix: inline $ref pointers in schemaToJson for self-contained tool schemas#1671
Open
Vadaski wants to merge 2 commits intomodelcontextprotocol:mainfrom
Open
fix: inline $ref pointers in schemaToJson for self-contained tool schemas#1671Vadaski wants to merge 2 commits intomodelcontextprotocol:mainfrom
Vadaski wants to merge 2 commits intomodelcontextprotocol:mainfrom
Conversation
…emas (modelcontextprotocol#1562) `z.toJSONSchema()` can produce `$ref`/`$defs` in two ways: - Reused sub-schemas: controlled by `reused: 'ref'|'inline'` - Schemas registered in `z.globalRegistry` with an `id`: extracted to `$defs` regardless of the `reused` setting Tool `inputSchema` and `outputSchema` objects sent to LLMs must be fully self-contained — LLMs and most downstream validators cannot resolve `$ref` pointers that point into `$defs` within the same document. Fix `schemaToJson()` to: 1. Pass `reused: 'inline'` to prevent multiply-referenced sub-schemas from becoming `$ref` pointers. 2. Pass a proxy metadata registry that wraps `z.globalRegistry` but strips the `id` field from returned metadata and exposes an empty `_idmap`, so schemas registered with an `id` are inlined rather than extracted to `$defs`. Non-id metadata (e.g. `.describe()` descriptions) is preserved. Add `packages/core/test/util/schemaToJson.test.ts` with five tests covering: - Shared schemas inlined instead of producing `$ref`/`$defs` - No `$ref` for basic schemas - Correct output for a plain `z.object()` - `io: 'input'` option respected - `.describe()` metadata preserved after id-stripping Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@modelcontextprotocol/client
@modelcontextprotocol/server
@modelcontextprotocol/express
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Fixes #1562.
z.toJSONSchema()can emit$ref/$defsin two independent ways:$defsand emits$refpointers. Controlled by thereusedoption.z.globalRegistrywith anid– Zod reads the_idmapof the metadata registry and extracts any schema with anidto$defs, regardless ofreused. This fires whenever a user callsz.globalRegistry.add(schema, { id: 'Foo' })or.meta({ id: 'Foo' }).Tool
inputSchema/outputSchemaobjects sent over MCP must be fully self-contained JSON Schema objects. LLMs and most downstream validators cannot resolve$refpointers – especially$ref: "#/$defs/Foo"references that point into a sibling$defsblock.Fix
Update
schemaToJson()inpackages/core/src/util/schema.ts:reused: 'inline'to prevent multiply-referenced sub-schemas from becoming$refpointers.z.globalRegistrybut:idfield from returned metadata (so the serialiser skips the id-based$defsextraction pass)_idmap.describe()descriptions,.meta()annotations) unchangedThis means schemas annotated with
.describe('some description')still emit adescriptionfield in the JSON Schema output, while schemas registered with an explicitidare inlined instead of becoming$refpointers.Tests
Added
packages/core/test/util/schemaToJson.test.tswith five tests:$ref/$defs$reffor plain schemasz.object()io: 'input'option is respected.describe()metadata is preserved after id-strippingChecklist
pnpm test:all)pnpm typecheck:all)