Skip to content

Commit aca892b

Browse files
committed
chore: skip custom json schema generation
1 parent 9298723 commit aca892b

File tree

1 file changed

+2
-151
lines changed

1 file changed

+2
-151
lines changed

src/server/mcp.ts

Lines changed: 2 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -46,155 +46,6 @@ import { UriTemplate, Variables } from "../shared/uriTemplate.js";
4646
import { RequestHandlerExtra } from "../shared/protocol.js";
4747
import { Transport } from "../shared/transport.js";
4848

49-
// Minimal JSON Schema generation for Zod v4
50-
type JsonSchema = {
51-
type: "object" | "string" | "number" | "boolean" | "integer";
52-
properties?: Record<string, JsonSchema | { enum: string[]; type?: "string" }>;
53-
required?: string[];
54-
enum?: string[];
55-
};
56-
57-
function zodToJsonSchema(schema: ZodObject<ZodRawShape>): JsonSchema {
58-
const shape = schema.shape;
59-
const properties: NonNullable<JsonSchema["properties"]> = {};
60-
const required: string[] = [];
61-
62-
for (const [key, value] of Object.entries(shape)) {
63-
const [unwrapped, isOptional] = unwrapOptional(value as ZodType);
64-
const propSchema = toJsonForAny(unwrapped);
65-
if (propSchema) {
66-
properties[key] = propSchema;
67-
} else {
68-
// Fallback to empty object schema for unsupported types
69-
properties[key] = {} as unknown as JsonSchema;
70-
}
71-
if (!isOptional) required.push(key);
72-
}
73-
74-
const result: JsonSchema = { type: "object", properties };
75-
if (required.length > 0) result.required = required;
76-
return result;
77-
}
78-
79-
function unwrapOptional(s: ZodType): [ZodType, boolean] {
80-
// Zod v4 Optional has unwrap(); fall back to heuristic if unavailable
81-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
82-
const anySchema = s as any;
83-
if (typeof anySchema.isOptional === "function" && anySchema.isOptional()) {
84-
if (typeof anySchema.unwrap === "function") {
85-
return [anySchema.unwrap(), true];
86-
}
87-
// Best-effort: try _def.innerType if present
88-
if (anySchema._def?.innerType) {
89-
return [anySchema._def.innerType as ZodType, true];
90-
}
91-
return [s, true];
92-
}
93-
return [s, false];
94-
}
95-
96-
function toJsonForAny(
97-
s: ZodType,
98-
): JsonSchema | { enum: string[]; type?: "string" } | undefined {
99-
// Prefer Zod v4 built-in conversion when available, then normalize
100-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
101-
const anySchema = s as any;
102-
if (typeof anySchema.toJsonSchema === "function") {
103-
try {
104-
const js = anySchema.toJsonSchema();
105-
const normalized = normalizeJsonFragment(js);
106-
if (normalized) return normalized;
107-
} catch {
108-
// fall through to manual mapping
109-
}
110-
}
111-
112-
// Manual mapping for common primitives and objects
113-
if (isInstanceOf(anySchema, z.ZodString)) return { type: "string" };
114-
if (isInstanceOf(anySchema, z.ZodBoolean)) return { type: "boolean" };
115-
if (isInstanceOf(anySchema, z.ZodNumber)) return { type: "number" };
116-
if (isInstanceOf(anySchema, z.ZodObject))
117-
return zodToJsonSchema(anySchema as ZodObject<ZodRawShape>);
118-
// Enums
119-
if (isInstanceOf(anySchema, z.ZodEnum)) {
120-
const values = extractEnumValues(anySchema);
121-
if (values) return { type: "string", enum: values };
122-
}
123-
return undefined;
124-
}
125-
126-
function extractEnumValues(s: unknown): string[] | undefined {
127-
// Try well-known locations across Zod versions
128-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
129-
const anySchema = s as any;
130-
if (Array.isArray(anySchema.options)) return anySchema.options as string[];
131-
if (Array.isArray(anySchema.values)) return anySchema.values as string[];
132-
if (Array.isArray(anySchema._def?.values))
133-
return anySchema._def.values as string[];
134-
return undefined;
135-
}
136-
137-
// Avoid `any` in constructor typing for linter compliance
138-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
139-
function isInstanceOf<T>(
140-
value: unknown,
141-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
142-
ctor: new (...args: any[]) => T,
143-
): value is T {
144-
// Avoid crashes across dual bundles by checking name and prototype
145-
return (
146-
typeof value === "object" &&
147-
value !== null &&
148-
(value as object) instanceof ctor
149-
);
150-
}
151-
152-
function normalizeJsonFragment(
153-
js: unknown,
154-
): JsonSchema | { enum: string[]; type?: "string" } | undefined {
155-
if (!js || typeof js !== "object") return undefined;
156-
// If this already looks like a primitive schema
157-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
158-
const frag = js as any;
159-
if (
160-
frag.type === "string" ||
161-
frag.type === "number" ||
162-
frag.type === "boolean" ||
163-
frag.type === "integer"
164-
) {
165-
const res: JsonSchema = { type: frag.type };
166-
if (Array.isArray(frag.enum)) res.enum = frag.enum;
167-
return res;
168-
}
169-
if (Array.isArray(frag.enum)) {
170-
return {
171-
type: typeof frag.type === "string" ? frag.type : "string",
172-
enum: frag.enum,
173-
};
174-
}
175-
if (
176-
frag.type === "object" &&
177-
frag.properties &&
178-
typeof frag.properties === "object"
179-
) {
180-
const properties: Record<
181-
string,
182-
JsonSchema | { enum: string[]; type?: "string" }
183-
> = {};
184-
for (const [k, v] of Object.entries(
185-
frag.properties as Record<string, unknown>,
186-
)) {
187-
const nv = normalizeJsonFragment(v);
188-
if (nv) properties[k] = nv;
189-
else properties[k] = {} as unknown as JsonSchema;
190-
}
191-
const out: JsonSchema = { type: "object", properties };
192-
if (Array.isArray(frag.required) && frag.required.length > 0)
193-
out.required = frag.required.slice();
194-
return out;
195-
}
196-
return undefined;
197-
}
19849

19950
/**
20051
* High-level MCP server that provides a simpler API for working with resources, tools, and prompts.
@@ -265,13 +116,13 @@ export class McpServer {
265116
title: tool.title,
266117
description: tool.description,
267118
inputSchema: tool.inputSchema
268-
? (zodToJsonSchema(tool.inputSchema) as Tool["inputSchema"])
119+
? z.toJSONSchema(tool.inputSchema) as Tool["inputSchema"]
269120
: EMPTY_OBJECT_JSON_SCHEMA,
270121
annotations: tool.annotations,
271122
};
272123

273124
if (tool.outputSchema) {
274-
toolDefinition.outputSchema = zodToJsonSchema(
125+
toolDefinition.outputSchema = z.toJSONSchema(
275126
tool.outputSchema,
276127
) as Tool["outputSchema"];
277128
}

0 commit comments

Comments
 (0)