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.
This commit is contained in:
Jeremy McSpadden 2026-03-16 14:24:32 -05:00
parent ce6e684899
commit 4f8a4a76b3

79
src/mcp-server.ts Normal file
View file

@ -0,0 +1,79 @@
interface McpTool {
name: string
description: string
parameters: Record<string, unknown>
execute(
toolCallId: string,
params: Record<string, unknown>,
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<void> {
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<string, McpTool>()
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`)
}