UNPKG

mcpdog

Version:

MCPDog - Universal MCP Server Manager with Web Interface

113 lines 4.77 kB
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { ToolRouter } from './tool-router'; import { EventEmitter } from 'events'; // Mock ServerAdapter class MockAdapter extends EventEmitter { name; config; isConnected = false; tools; constructor(name, tools) { super(); this.name = name; this.tools = tools; this.config = {}; } connect = vi.fn(async () => { this.isConnected = true; this.emit('connected'); }); disconnect = vi.fn(async () => { this.isConnected = false; this.emit('disconnected'); }); getTools = vi.fn(async () => { if (!this.isConnected) throw new Error('Not connected'); return this.tools; }); callTool = vi.fn(async (toolName, args) => { if (!this.isConnected) throw new Error('Not connected'); return { jsonrpc: '2.0', id: 1, result: { content: `Result from ${this.name}.${toolName}` }, }; }); // Added to satisfy ServerAdapter interface sendRequest = vi.fn(async (request) => { return { jsonrpc: '2.0', id: request.id, result: { message: 'Mocked sendRequest' } }; }); } describe('ToolRouter', () => { let toolRouter; beforeEach(() => { toolRouter = new ToolRouter(); }); it('should add an adapter and refresh routes on connect', async () => { const mockTools = [{ name: 'tool1', description: 'desc1', inputSchema: { type: 'object' } }]; const adapter = new MockAdapter('server1', mockTools); toolRouter.addAdapter(adapter); await adapter.connect(); const allTools = await toolRouter.getAllTools(); expect(allTools).toHaveLength(1); expect(allTools[0].name).toBe('tool1'); expect(toolRouter.getAdapter('server1')).toBe(adapter); }); it('should handle tool name conflicts by prefixing', async () => { const tools1 = [{ name: 'shared_tool', description: 'desc1', inputSchema: { type: 'object' } }]; const tools2 = [{ name: 'shared_tool', description: 'desc2', inputSchema: { type: 'object' } }]; const adapter1 = new MockAdapter('server1', tools1); const adapter2 = new MockAdapter('server2', tools2); toolRouter.addAdapter(adapter1); toolRouter.addAdapter(adapter2); await adapter1.connect(); await adapter2.connect(); const allTools = await toolRouter.getAllTools(); expect(allTools).toHaveLength(2); const toolNames = allTools.map(t => t.name).sort(); expect(toolNames).toEqual(['server1-shared_tool', 'server2-shared_tool']); }); it('should route a tool call to the correct adapter', async () => { const tools1 = [{ name: 'tool1', description: 'desc1', inputSchema: { type: 'object' } }]; const tools2 = [{ name: 'tool2', description: 'desc2', inputSchema: { type: 'object' } }]; const adapter1 = new MockAdapter('server1', tools1); const adapter2 = new MockAdapter('server2', tools2); toolRouter.addAdapter(adapter1); toolRouter.addAdapter(adapter2); await adapter1.connect(); await adapter2.connect(); // Need to manually trigger route creation for testing since it happens on 'connect' event // @ts-ignore - access private method for test await toolRouter.refreshToolRoutes('server1'); // @ts-ignore await toolRouter.refreshToolRoutes('server2'); const response = await toolRouter.callTool('tool2', {}); expect(response.result.content).toBe('Result from server2.tool2'); expect(adapter2.callTool).toHaveBeenCalledWith('tool2', {}); expect(adapter1.callTool).not.toHaveBeenCalled(); }); it('should return an error if a tool is not found', async () => { const response = await toolRouter.callTool('non_existent_tool', {}); expect(response.error).toBeDefined(); expect(response.error?.code).toBe(-32601); expect(response.error?.message).toContain('Tool not found'); }); it('should remove routes when an adapter disconnects', async () => { const mockTools = [{ name: 'tool1', description: 'desc1', inputSchema: { type: 'object' } }]; const adapter = new MockAdapter('server1', mockTools); toolRouter.addAdapter(adapter); await adapter.connect(); let allTools = await toolRouter.getAllTools(); expect(allTools).toHaveLength(1); adapter.emit('disconnected'); const route = toolRouter.findToolRoute('tool1'); expect(route).toBeUndefined(); }); }); //# sourceMappingURL=tool-router.test.js.map