Fix Zod object detection logic
Previously, this check was `value instanceof ZodType`, which breaks if the user is using a different version of Zod than the library. Instead, do the check using the structure of the input object rather than its prototype chain. Additionally, this adds a vendored version of Zod to use for tests, which ensures that regressions like this would be caught next time.
This commit is contained in:
parent
35fe98a0d6
commit
b6617dc44c
4 changed files with 35 additions and 11 deletions
5
src/server/__tests__/vendor/zod@3.24.4.umd.d.ts
vendored
Normal file
5
src/server/__tests__/vendor/zod@3.24.4.umd.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import * as _zod from 'zod';
|
||||
|
||||
// This makes the vendored zod use the same types as the actual zod package
|
||||
export declare const z: typeof _zod.z;
|
||||
export default _zod.default;
|
||||
8
src/server/__tests__/vendor/zod@3.24.4.umd.js
vendored
Normal file
8
src/server/__tests__/vendor/zod@3.24.4.umd.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,6 @@
|
|||
import { McpServer } from "./mcp.js";
|
||||
import { Client } from "../client/index.js";
|
||||
import { InMemoryTransport } from "../inMemory.js";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
ListToolsResultSchema,
|
||||
CallToolResultSchema,
|
||||
|
|
@ -17,6 +16,9 @@ import {
|
|||
import { ResourceTemplate } from "./mcp.js";
|
||||
import { completable } from "./completable.js";
|
||||
import { UriTemplate } from "../shared/uriTemplate.js";
|
||||
// Note: deliberately using a different Zod version to the one bundled
|
||||
// with the SDK as that's what our users are likely to be doing
|
||||
import { z } from "./__tests__/vendor/zod@3.24.4.umd.js";
|
||||
|
||||
describe("McpServer", () => {
|
||||
test("should expose underlying Server instance", () => {
|
||||
|
|
|
|||
|
|
@ -659,16 +659,6 @@ export class McpServer {
|
|||
if (this._registeredTools[name]) {
|
||||
throw new Error(`Tool ${name} is already registered`);
|
||||
}
|
||||
|
||||
// Helper to check if an object is a Zod schema (ZodRawShape)
|
||||
const isZodRawShape = (obj: unknown): obj is ZodRawShape => {
|
||||
if (typeof obj !== "object" || obj === null) return false;
|
||||
|
||||
const isEmptyObject = z.object({}).strict().safeParse(obj).success;
|
||||
|
||||
// Check if object is empty or at least one property is a ZodType instance
|
||||
return isEmptyObject || Object.values(obj as object).some(v => v instanceof ZodType);
|
||||
};
|
||||
|
||||
let description: string | undefined;
|
||||
if (typeof rest[0] === "string") {
|
||||
|
|
@ -931,6 +921,25 @@ const EMPTY_OBJECT_JSON_SCHEMA = {
|
|||
type: "object" as const,
|
||||
};
|
||||
|
||||
// Helper to check if an object is a Zod schema (ZodRawShape)
|
||||
function isZodRawShape(obj: unknown): obj is ZodRawShape {
|
||||
if (typeof obj !== "object" || obj === null) return false;
|
||||
|
||||
const isEmptyObject = z.object({}).strict().safeParse(obj).success;
|
||||
|
||||
// Check if object is empty or at least one property is a ZodType instance
|
||||
return isEmptyObject || Object.values(obj as object).some(isZodType);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function isZodType(value: any): value is ZodType {
|
||||
return value !== null &&
|
||||
typeof value === 'object' &&
|
||||
typeof value.parse === 'function' &&
|
||||
typeof value.safeParse === 'function' &&
|
||||
typeof value._def === 'object';
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional, optional information for annotating a resource.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue