Skip to content

Conversation

@felixweinberger
Copy link
Contributor

@felixweinberger felixweinberger commented Dec 15, 2025

Zod v4 stores descriptions in z.globalRegistry, not in ._zod.def.description as the original implementation assumed. Both v3 and v4 expose a .description getter, so this simplifies getSchemaDescription() to just use that.

Motivation and Context

Users registering prompts with described arguments were not seeing the descriptions in the output:

server.prompt("test", { arg: z.string().describe("My description") }, ...)
// Expected: argument has description: "My description"
// Actual: description was missing

Root cause: The original v4 implementation looked for descriptions in ._zod.def.description, but this property doesn't exist in Zod v4.

See: https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/classic/schemas.ts shows the .description getter reads from the global registry:

Object.defineProperty(inst, "description", {
    get() {
      return core.globalRegistry.get(inst)?.description;
    },
});

The https://zod.dev/metadata confirm this architecture:

"The .describe() method is a shorthand for registering a schema in z.globalRegistry"

So the correct way to access descriptions is via the .description getter (which internally reads from the registry), not ._zod.def.description (which doesn't exist).

How Has This Been Tested?

  • Added regression test in new test/issues/ folder (following Python SDK pattern)
  • Test runs for both Zod v3 and v4 via zodTestMatrix
  • All existing tests pass

Breaking Changes

None - this is a bug fix.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the https://modelcontextprotocol.io
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Fixes #1277

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 15, 2025

Open in StackBlitz

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/sdk@1296

commit: 03a6d09

@felixweinberger felixweinberger added the bug Something isn't working label Dec 15, 2025
@felixweinberger felixweinberger marked this pull request as ready for review December 15, 2025 11:31
@felixweinberger felixweinberger requested a review from a team as a code owner December 15, 2025 11:31
@felixweinberger felixweinberger marked this pull request as draft December 15, 2025 11:33
Zod v4 stores descriptions in `z.globalRegistry`, not in `._zod.def.description`.
Both v3 and v4 expose a `.description` getter, so simplify to just use that.

Also removes the incorrect `description` field from `ZodV4Internal` interface.

Fixes #1277
@felixweinberger felixweinberger force-pushed the fweinberger/zod-compat-v4-detection branch from 9f891fb to 1a0a906 Compare December 15, 2025 15:14
}
const v3Schema = schema as unknown as ZodV3Internal;
// v3 may have description on the schema itself or in _def
return (schema as { description?: string }).description ?? v3Schema._def?.description;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ?? v3Schema._def?.description is unnecessary because ZodType on v3 already has this getter: https://github.com/colinhacks/zod/blob/main/packages/zod/src/v3/types.ts#L164-L166

get description(): string | undefined {
  return this._def.description;
}

Comment on lines -225 to -228
if (isZ4Schema(schema)) {
const v4Schema = schema as unknown as ZodV4Internal;
return v4Schema._zod?.def?.description;
}
Copy link
Contributor Author

@felixweinberger felixweinberger Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In v4 .describe() stores in globalRegistry with the .description getter reading from it directly via v4Schema.description rather than on _def so we don't need this.

https://github.com/colinhacks/zod/blob/main/packages/zod/src/v4/classic/schemas.ts#L223-L230

@felixweinberger felixweinberger marked this pull request as ready for review December 15, 2025 15:32
@felixweinberger
Copy link
Contributor Author

@dclark27 @colinhacks @mattzcarey in case I'm missing any edge cases you were thinking of here with this helper - but it looks to me like branching on zod version might have been unnecessary here?

Copy link
Contributor

@mattzcarey mattzcarey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@KKonstantinov KKonstantinov merged commit 9941294 into main Dec 15, 2025
10 checks passed
@KKonstantinov KKonstantinov deleted the fweinberger/zod-compat-v4-detection branch December 15, 2025 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

zod-compat fails to detect standard Zod v4 schemas (missing _zod property), causing metadata loss

4 participants