autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
82 lines (81 loc) • 3.09 kB
JavaScript
/**
* zodToMcpSchema.ts — Zod v4 → MCP-compatible JSON Schema 转换器
*
* 将 Zod schema 转为 MCP SDK 所需的 inputSchema 格式:
* 1. 移除 $schema(MCP 不需要 meta-schema 声明)
* 2. 带 default 值的字段从 required 中移除(Agent 可省略,Zod parse 自动填充)
* 3. 移除 additionalProperties: false(允许前向兼容的额外字段)
* 4. 清理 integer 的冗余 min/max 边界(Zod v4 自动加的 ±MAX_SAFE_INTEGER)
*
* @module external/mcp/zodToMcpSchema
*/
import { z } from 'zod';
/** 递归清理 JSON Schema 对象 */
function cleanJsonSchema(obj) {
const cleaned = { ...obj };
// 移除顶层 $schema
delete cleaned['$schema'];
// 移除 additionalProperties: false
if (cleaned['additionalProperties'] === false) {
delete cleaned['additionalProperties'];
}
// 清理 integer 的冗余边界
if (cleaned['type'] === 'integer') {
if (cleaned['minimum'] === -9007199254740991) {
delete cleaned['minimum'];
}
if (cleaned['maximum'] === 9007199254740991) {
delete cleaned['maximum'];
}
}
// 带 default 的字段从 required 中移除
const properties = cleaned['properties'];
if (properties && Array.isArray(cleaned['required'])) {
const required = cleaned['required'].filter((key) => {
const prop = properties[key];
return prop && !('default' in prop);
});
cleaned['required'] = required.length > 0 ? required : [];
}
// 递归处理 properties 内部的 object/array 类型
if (properties) {
const cleanedProps = {};
for (const [key, propSchema] of Object.entries(properties)) {
cleanedProps[key] = cleanJsonSchema(propSchema);
}
cleaned['properties'] = cleanedProps;
}
// 递归处理 items(数组元素)
if (cleaned['items'] && typeof cleaned['items'] === 'object') {
cleaned['items'] = cleanJsonSchema(cleaned['items']);
}
// 递归处理 anyOf / oneOf / allOf(用于 union / discriminatedUnion / intersection)
for (const keyword of ['anyOf', 'oneOf', 'allOf']) {
if (Array.isArray(cleaned[keyword])) {
cleaned[keyword] = cleaned[keyword].map(cleanJsonSchema);
}
}
return cleaned;
}
/**
* 将 Zod schema 转换为 MCP 兼容的 JSON Schema
*
* @param schema Zod 类型定义
* @returns MCP inputSchema 格式的 JSON Schema 对象
*
* @example
* import { SearchInput } from '#shared/schemas/mcp-tools.js';
* const inputSchema = zodToMcpSchema(SearchInput);
* // → { type: 'object', properties: { query: { type: 'string', minLength: 1 }, ... }, required: ['query'] }
*/
export function zodToMcpSchema(schema) {
const raw = z.toJSONSchema(schema);
const cleaned = cleanJsonSchema(raw);
// 保证 type/properties/required 字段存在
return {
type: 'object',
properties: cleaned['properties'] || {},
required: cleaned['required'] || [],
...cleaned,
};
}