MCP SDK自定义工具Zod参数验证兼容问题:Cursor正常Flowise报“Expected object, received string”错误
MCP SDK自定义工具Zod参数验证兼容问题:Cursor正常Flowise报“Expected object, received string”错误
我最近基于Model Context Protocol (MCP) SDK开发了带Zod参数验证的自定义工具,在Cursor AI里调用完全正常,但在Flowise中遇到了参数格式的矛盾错误——有时候提示“Expected object, received string”,手动转成对象后又提示“Expected string, received object”,折腾了好一阵子都没解决。
报错信息
McpError: MCP error -32602: Invalid arguments for tool getUserData: [ { "code": "invalid_type", "expected": "object", "received": "string", "path": ["parameters"], "message": "Expected object, received string" }]
我的工具代码
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; // Zod schema definition const UserFilterSchema = z.object({ userId: z.string().describe("User ID to filter by"), page: z.number().int().positive().optional().default(1), limit: z.number().int().positive().optional().default(10), includeDetails: z.boolean().optional().default(false) }); type UserFilterInput = z.infer<typeof UserFilterSchema>; // Tool registration server.tool( "getUserData", { parameters: UserFilterSchema, token: z.string().describe("Authentication token"), }, async (args: { parameters: UserFilterInput, token: string }) => { const { userId, page = 1, limit = 10, includeDetails = false } = args.parameters; try { // Mock data processing logic const mockData = { userId, userData: `Sample data for user ${userId}`, page, limit, includeDetails }; return { content: [{ type: "text", text: JSON.stringify({ success: true, data: mockData }) }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ success: false, error: error.message }) }], isError: true }; } } );
环境差异与尝试
| 环境 | 状态 | 说明 |
|---|---|---|
| Cursor AI | ✅ 正常工作 | 参数验证和解析完全正常 |
| Flowise | ❌ 报错 | 参数格式矛盾验证失败 |
我已经试过两种参数传递方式,但都触发相反的错误:
- 传入对象格式:
{ userId: "user123", page: 1, limit: 10 }→ 提示“Expected string, received object” - 传入字符串格式:
"user123"→ 提示“Expected object, received string”
可能的解决方案
1. 用Zod Union兼容两种参数格式
修改Schema,让它同时支持对象和字符串输入,自动转换为统一结构:
// 兼容对象和字符串的UserFilterSchema const UserFilterSchema = z.union([ // 原始对象格式 z.object({ userId: z.string().describe("User ID to filter by"), page: z.number().int().positive().optional(), limit: z.number().int().positive().optional(), includeDetails: z.boolean().optional() }), // 直接传入userId字符串,自动转为对象 z.string().transform(userId => ({ userId })) ]).pipe( // 统一应用默认值 z.object({ userId: z.string(), page: z.number().int().positive().default(1), limit: z.number().int().positive().default(10), includeDetails: z.boolean().default(false) }) );
2. 在工具函数中显式处理参数格式
如果不想修改Schema,可以在工具逻辑里先做格式兼容:
async (args: { parameters: unknown, token: string }) => { let parsedParams: UserFilterInput; // 处理字符串格式的参数 if (typeof args.parameters === 'string') { try { // 尝试解析为JSON对象 parsedParams = UserFilterSchema.parse(JSON.parse(args.parameters)); } catch { // 解析失败则用字符串作为userId parsedParams = UserFilterSchema.parse({ userId: args.parameters }); } } else { // 处理对象格式的参数 parsedParams = UserFilterSchema.parse(args.parameters); } const { userId, page = 1, limit = 10, includeDetails = false } = parsedParams; // 后续业务逻辑... }
3. 检查Flowise的参数配置
有些低代码平台会自动序列化参数,检查Flowise中调用MCP工具的设置:
- 确认是否开启了“自动JSON序列化”选项,尝试关闭它
- 在Flowise中用表达式手动处理参数,比如
{{ JSON.stringify(参数对象) }}或{{ 参数对象.userId }}
这种问题本质是不同MCP客户端对协议细节的实现差异,通过兼容输入格式或者显式转换,就能解决跨平台的调用问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

