import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import http from 'node:http'; import type { AddressInfo } from 'node:net'; import { describe, expect, it, vi } from 'vitest'; import { createWecomPluginMcpServer, WecomPluginMcpFactory } from './wecom-plugin-mcp.factory'; import type { WecomPluginQueryService } from './wecom-plugin-query.service'; import type { WecomPluginRuntime } from './wecom-plugin.types'; const plugin: WecomPluginRuntime = { id: '9', name: 'ops-readonly', permissions: ['order.read', 'metrics.read'], }; async function listen(server: http.Server): Promise { await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const { port } = server.address() as AddressInfo; return `http://127.0.0.1:${port}/mcp`; } describe('WecomPluginMcpFactory Streamable HTTP', () => { it('lists only permitted tools and calls query_orders', async () => { const query = { queryOrders: vi.fn().mockResolvedValue({ total: 1, items: [{ orderNo: 'DK1' }] }), } as unknown as WecomPluginQueryService; const factory = new WecomPluginMcpFactory(query); const httpServer = http.createServer((req, res) => { const chunks: Buffer[] = []; req.on('data', (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))); req.on('end', () => { const raw = Buffer.concat(chunks).toString('utf8'); (req as http.IncomingMessage & { body?: unknown }).body = raw ? JSON.parse(raw) : undefined; void factory.handle(req, res, plugin); }); }); const url = await listen(httpServer); const client = new Client({ name: 'wecom-plugin-test', version: '1.0.0' }); const transport = new StreamableHTTPClientTransport(new URL(url)); try { await client.connect(transport); const listed = await client.listTools(); expect(listed.tools.map((t) => t.name).sort()).toEqual(['query_metrics', 'query_orders']); const called = await client.callTool({ name: 'query_orders', arguments: { q: 'DK' } }); expect(called.isError).toBeFalsy(); const text = called.content.find((c) => c.type === 'text'); expect(text && 'text' in text ? JSON.parse(text.text) : null).toEqual({ total: 1, items: [{ orderNo: 'DK1' }], }); } finally { await client.close().catch(() => undefined); await new Promise((resolve, reject) => httpServer.close((err) => (err ? reject(err) : resolve()))); } }); it('createWecomPluginMcpServer does not register unauthorized tools', () => { const query = {} as WecomPluginQueryService; const server = createWecomPluginMcpServer(query, plugin, 'alice'); expect(server).toBeTruthy(); }); });