From 4f8a4a76b3a23e029f7e4716c32c88346fa951a5 Mon Sep 17 00:00:00 2001 From: Jeremy McSpadden Date: Mon, 16 Mar 2026 14:24:32 -0500 Subject: [PATCH] fix: add compiled MCP server module for --mode mcp support Create src/mcp-server.ts with dynamic imports to bypass TypeScript's static module resolution for @modelcontextprotocol/sdk subpath exports that use wildcard patterns (./*) without matching type declarations. --- src/mcp-server.ts | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/mcp-server.ts diff --git a/src/mcp-server.ts b/src/mcp-server.ts new file mode 100644 index 000000000..7d814b451 --- /dev/null +++ b/src/mcp-server.ts @@ -0,0 +1,79 @@ +interface McpTool { + name: string + description: string + parameters: Record + execute( + toolCallId: string, + params: Record, + signal?: AbortSignal, + onUpdate?: unknown, + ): Promise<{ + content: Array<{ type: string; text?: string; data?: string; mimeType?: string }> + }> +} + +// MCP SDK subpath imports use wildcard exports (./*) that NodeNext resolves +// at runtime but TypeScript cannot statically type-check. We construct the +// specifiers dynamically so tsc treats them as `any`. +const MCP_PKG = '@modelcontextprotocol/sdk' + +export async function startMcpServer(options: { + tools: McpTool[] + version?: string +}): Promise { + const { tools, version = '0.0.0' } = options + + const serverMod = await import(`${MCP_PKG}/server`) + const stdioMod = await import(`${MCP_PKG}/server/stdio`) + const typesMod = await import(`${MCP_PKG}/types`) + + const Server = serverMod.Server + const StdioServerTransport = stdioMod.StdioServerTransport + const { ListToolsRequestSchema, CallToolRequestSchema } = typesMod + + const toolMap = new Map() + for (const tool of tools) { + toolMap.set(tool.name, tool) + } + + const server = new Server( + { name: 'gsd', version }, + { capabilities: { tools: {} } }, + ) + + server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: tools.map((t: McpTool) => ({ + name: t.name, + description: t.description, + inputSchema: t.parameters, + })), + })) + + server.setRequestHandler(CallToolRequestSchema, async (request: any) => { + const { name, arguments: args } = request.params + const tool = toolMap.get(name) + if (!tool) { + return { + isError: true, + content: [{ type: 'text' as const, text: `Unknown tool: ${name}` }], + } + } + + try { + const result = await tool.execute(`mcp-${Date.now()}`, args ?? {}, undefined, undefined) + const content = result.content.map((block: any) => { + if (block.type === 'text') return { type: 'text' as const, text: block.text ?? '' } + if (block.type === 'image') return { type: 'image' as const, data: block.data ?? '', mimeType: block.mimeType ?? 'image/png' } + return { type: 'text' as const, text: JSON.stringify(block) } + }) + return { content } + } catch (err: unknown) { + const message = err instanceof Error ? err.message : String(err) + return { isError: true, content: [{ type: 'text' as const, text: message }] } + } + }) + + const transport = new StdioServerTransport() + await server.connect(transport) + process.stderr.write(`[gsd] MCP server started (v${version})\n`) +}