feat(wecom): 企微 API 插件增加 MCP Streamable HTTP 端点
与 REST/OpenAPI 并存,按实例权限自动 tools/list,免手填工具。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
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,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user