import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { createCanvasCapabilityClientCommand } from "@/lib/canvas/capabilities/client-command"; import { CANVAS_CAPABILITY_REGISTRY } from "@/lib/canvas/capabilities/registry"; const requestIdSchema = z.string().trim().min(1).max(180).optional(); const nodeIdsSchema = z .array(z.string().trim().min(1).max(180)) .min(1) .max(50) .describe("要操作的画布图片节点 ID;多步调用可传入上一步返回的 createdNodeIds"); export async function createCanvasCapabilityMcpServer(params: { filterDisabled?: boolean; projectId: string; requestHeaders: Headers; requestUrl: string; userId: string; }) { const server = new McpServer({ name: "buyi-canvas-capabilities", version: "1.0.0", }); const enabledCapabilityIds = params.filterDisabled === false ? new Set(CANVAS_CAPABILITY_REGISTRY.map((capability) => capability.id)) : await import("@/lib/canvas/capabilities/availability").then( ({ getEnabledCanvasCapabilityIds }) => getEnabledCanvasCapabilityIds(CANVAS_CAPABILITY_REGISTRY) ); for (const capability of CANVAS_CAPABILITY_REGISTRY) { if (!enabledCapabilityIds.has(capability.id)) continue; if (!(capability.mcp.exposed && capability.mcp.toolName)) continue; const inputSchema = z.object({ nodeIds: nodeIdsSchema, args: capability.inputSchema.optional().describe("能力参数"), requestId: requestIdSchema.describe("幂等请求号;重试时必须复用同一个值"), confirmed: z .boolean() .optional() .describe("危险操作的明确确认;删除等能力必须为 true"), }); server.registerTool( capability.mcp.toolName, { title: capability.title, description: capability.description, inputSchema, annotations: { destructiveHint: capability.risk === "destructive", idempotentHint: true, openWorldHint: capability.risk === "external", readOnlyHint: capability.risk === "read", }, }, async (input) => { try { const command = createCanvasCapabilityClientCommand({ capabilityId: capability.id, nodeIds: input.nodeIds, projectId: params.projectId, requestId: input.requestId, args: input.args, source: "mcp", confirmed: input.confirmed, }); const { executeCanvasCapabilityRuntime } = await import( "@/lib/canvas/capabilities/runtime" ); const result = await executeCanvasCapabilityRuntime({ command, projectId: params.projectId, requestHeaders: params.requestHeaders, requestUrl: params.requestUrl, userId: params.userId, }); const createdNodeIds = result.operation?.createdNodeIds ?? []; const summary = result.mode === "interactive" ? `已创建客户端交互命令:${capability.title}` : `已执行:${capability.title}${ createdNodeIds.length > 0 ? `,创建 ${createdNodeIds.length} 个节点` : "" }`; return { content: [{ type: "text" as const, text: summary }], structuredContent: { capabilityId: capability.id, mode: result.mode, task: result.task, ...(result.operation ? { operation: result.operation } : {}), ...(result.outputs ? { outputs: result.outputs } : {}), ...(result.clientCommand ? { clientCommand: result.clientCommand } : {}), }, }; } catch (error) { const message = error instanceof Error ? error.message : `执行${capability.title}失败`; return { isError: true, content: [{ type: "text" as const, text: message }], }; } } ); } return server; }