976bde37cc
与 REST/OpenAPI 并存,按实例权限自动 tools/list,免手填工具。 Co-authored-by: Cursor <cursoragent@cursor.com>
72 lines
2.5 KiB
TypeScript
72 lines
2.5 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
|
import { allowedWecomPluginMcpTools } from '@dukang/shared-types';
|
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
import { WECOM_PLUGIN_MCP_TOOL_DEFS } from './wecom-plugin-mcp.tools';
|
|
import type { WecomPluginQueryService } from './wecom-plugin-query.service';
|
|
import type { WecomPluginRuntime } from './wecom-plugin.types';
|
|
import { wecomPluginCaller } from './wecom-plugin.util';
|
|
|
|
export function createWecomPluginMcpServer(
|
|
query: WecomPluginQueryService,
|
|
plugin: WecomPluginRuntime,
|
|
wecomUserId: string,
|
|
): McpServer {
|
|
const server = new McpServer({
|
|
name: plugin.name || 'dukang-wecom-plugin',
|
|
version: '1.0.0',
|
|
});
|
|
const allowed = new Set(allowedWecomPluginMcpTools(plugin.permissions));
|
|
const ctx = { query, plugin, wecomUserId };
|
|
for (const def of WECOM_PLUGIN_MCP_TOOL_DEFS) {
|
|
if (!allowed.has(def.name)) continue;
|
|
server.registerTool(
|
|
def.name,
|
|
{ title: def.name, description: def.description, inputSchema: def.inputSchema },
|
|
async (args) => def.invoke(ctx, (args ?? {}) as Record<string, unknown>),
|
|
);
|
|
}
|
|
return server;
|
|
}
|
|
|
|
@Injectable()
|
|
export class WecomPluginMcpFactory {
|
|
private readonly logger = new Logger(WecomPluginMcpFactory.name);
|
|
|
|
constructor(private readonly query: WecomPluginQueryService) {}
|
|
|
|
async handle(
|
|
req: IncomingMessage & { body?: unknown; headers: IncomingMessage['headers'] },
|
|
res: ServerResponse,
|
|
plugin: WecomPluginRuntime,
|
|
): Promise<void> {
|
|
const server = createWecomPluginMcpServer(this.query, plugin, wecomPluginCaller(req));
|
|
const transport = new StreamableHTTPServerTransport({
|
|
sessionIdGenerator: undefined,
|
|
});
|
|
res.on('close', () => {
|
|
void transport.close();
|
|
void server.close();
|
|
});
|
|
try {
|
|
await server.connect(transport);
|
|
await transport.handleRequest(req, res, req.body);
|
|
} catch (e) {
|
|
const message = e instanceof Error ? e.message : String(e);
|
|
this.logger.error(`wecom plugin MCP failed: ${message}`);
|
|
if (!res.headersSent) {
|
|
res.statusCode = 500;
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.end(
|
|
JSON.stringify({
|
|
jsonrpc: '2.0',
|
|
error: { code: -32603, message },
|
|
id: null,
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|