Skip to main content
Glama
mcp-bridge.test.tsโ€ข9.55 kB
import { jest } from '@jest/globals'; import { ApprovalService } from '../src/services/approval'; import { FunctionBridge } from '../src/services/function-bridge'; import { MCPClientManager } from '../src/services/mcp-client-manager'; describe('MCP Bridge', () => { let approvalService: ApprovalService; let mcpManager: MCPClientManager; let functionBridge: FunctionBridge; beforeEach(() => { approvalService = new ApprovalService(300); // 5 minutes mcpManager = new MCPClientManager([]); // Empty config for testing functionBridge = new FunctionBridge(mcpManager, approvalService, []); }); afterEach(() => { approvalService.shutdown(); }); describe('ApprovalService', () => { it('should create approval requests', () => { const request = approvalService.createApprovalRequest( 'TestDuck', 'filesystem', 'read_file', { path: '/test.txt' } ); expect(request).toBeDefined(); expect(request.duckName).toBe('TestDuck'); expect(request.mcpServer).toBe('filesystem'); expect(request.toolName).toBe('read_file'); expect(request.status).toBe('pending'); }); it('should approve pending requests', () => { const request = approvalService.createApprovalRequest( 'TestDuck', 'filesystem', 'read_file', { path: '/test.txt' } ); const approved = approvalService.approveRequest(request.id); expect(approved).toBe(true); const status = approvalService.getApprovalStatus(request.id); expect(status).toBe('approved'); }); it('should deny requests', () => { const request = approvalService.createApprovalRequest( 'TestDuck', 'filesystem', 'read_file', { path: '/test.txt' } ); const denied = approvalService.denyRequest(request.id, 'Security concern'); expect(denied).toBe(true); const status = approvalService.getApprovalStatus(request.id); expect(status).toBe('denied'); }); it('should handle non-existent requests', () => { const status = approvalService.getApprovalStatus('non-existent'); expect(status).toBeUndefined(); const approved = approvalService.approveRequest('non-existent'); expect(approved).toBe(false); }); it('should get pending approvals', () => { approvalService.createApprovalRequest('Duck1', 'server1', 'tool1', {}); approvalService.createApprovalRequest('Duck2', 'server2', 'tool2', {}); const pending = approvalService.getPendingApprovals(); expect(pending).toHaveLength(2); expect(pending.every(req => req.status === 'pending')).toBe(true); }); }); describe('FunctionBridge', () => { it('should generate function definitions for empty MCP tools', async () => { const functions = await functionBridge.getFunctionDefinitions(); expect(Array.isArray(functions)).toBe(true); // Should be empty since we have no MCP servers configured expect(functions).toHaveLength(0); }); it('should handle function calls requiring approval', async () => { const result = await functionBridge.handleFunctionCall( 'TestDuck', 'mcp__filesystem__read_file', { path: '/test.txt', _mcp_server: 'filesystem', _mcp_tool: 'read_file' } ); expect(result.success).toBe(false); expect(result.needsApproval).toBe(true); expect(result.approvalId).toBeDefined(); }); it('should handle invalid function names', async () => { const result = await functionBridge.handleFunctionCall( 'TestDuck', 'invalid_function', {} ); expect(result.success).toBe(false); expect(result.error).toContain('Invalid function name'); }); it('should validate tool arguments', async () => { // Mock a tool with schema in the functionBridge const mockTool = { serverName: 'test_server', name: 'test_tool', description: 'Test tool', inputSchema: { type: 'object', properties: { required_param: { type: 'string' } }, required: ['required_param'] } }; // Add the tool schema manually for testing (functionBridge as any).toolSchemas.set('test_server:test_tool', mockTool.inputSchema); const result = await functionBridge.handleFunctionCall( 'TestDuck', 'mcp__test_server__test_tool', { _mcp_server: 'test_server', _mcp_tool: 'test_tool', // Missing required_param } ); expect(result.success).toBe(false); expect(result.error).toContain('Invalid arguments'); }); it('should handle underscored tool names correctly', async () => { // Test that tool names with underscores are extracted correctly const serverName = (functionBridge as any).extractServerFromFunctionName('mcp__file_system__read_file'); const toolName = (functionBridge as any).extractToolFromFunctionName('mcp__file_system__read_file'); expect(serverName).toBe('file_system'); expect(toolName).toBe('read_file'); }); it('should handle complex server and tool names', async () => { // Test more complex names const serverName = (functionBridge as any).extractServerFromFunctionName('mcp__complex_server_name__complex_tool_name'); const toolName = (functionBridge as any).extractToolFromFunctionName('mcp__complex_server_name__complex_tool_name'); expect(serverName).toBe('complex_server_name'); expect(toolName).toBe('complex_tool_name'); }); }); describe('MCPClientManager', () => { it('should initialize with empty config', async () => { expect(mcpManager.getConnectedServers()).toEqual([]); expect(mcpManager.getConnectionStatus('nonexistent')).toBe('unknown'); }); it('should handle health check with no servers', async () => { const health = await mcpManager.healthCheck(); expect(health).toEqual({}); }); it('should get status of all servers', () => { const status = mcpManager.getStatus(); expect(typeof status).toBe('object'); }); }); describe('Integration Tests', () => { it('should handle complete approval workflow', async () => { // Create approval request const request = approvalService.createApprovalRequest( 'TestDuck', 'filesystem', 'read_file', { path: '/test.txt' } ); expect(request.status).toBe('pending'); // Try function call without approval const result1 = await functionBridge.handleFunctionCall( 'TestDuck', 'mcp__filesystem__read_file', { path: '/test.txt', _mcp_server: 'filesystem', _mcp_tool: 'read_file' } ); expect(result1.success).toBe(false); expect(result1.needsApproval).toBe(true); // Approve the request const approved = approvalService.approveRequest(request.id); expect(approved).toBe(true); // Try function call with approval (would still fail due to no MCP server, but approval logic works) const result2 = await functionBridge.handleFunctionCall( 'TestDuck', 'mcp__filesystem__read_file', { path: '/test.txt', _mcp_server: 'filesystem', _mcp_tool: 'read_file', _approval_id: request.id } ); // Should pass approval but fail on MCP execution expect(result2.error).toContain('MCP server filesystem not connected'); }); it('should handle expired approvals', (done) => { // Create approval service with very short timeout const shortApprovalService = new ApprovalService(1); // 1 second const request = shortApprovalService.createApprovalRequest( 'TestDuck', 'filesystem', 'read_file', { path: '/test.txt' } ); // Wait for expiration setTimeout(() => { const status = shortApprovalService.getApprovalStatus(request.id); expect(status).toBe('expired'); // Try to approve expired request const approved = shortApprovalService.approveRequest(request.id); expect(approved).toBe(false); shortApprovalService.shutdown(); done(); }, 1100); }); it('should handle function definition generation', async () => { // Mock MCP tools const mockTools = [ { serverName: 'filesystem', name: 'read_file', description: 'Read a file', inputSchema: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] } }, { serverName: 'web', name: 'fetch_url', description: 'Fetch URL content', inputSchema: { type: 'object', properties: { url: { type: 'string' } }, required: ['url'] } } ]; // Mock the listAllTools method jest.spyOn(mcpManager, 'listAllTools').mockResolvedValue(mockTools); const functions = await functionBridge.getFunctionDefinitions(); expect(functions).toHaveLength(2); expect(functions[0].name).toBe('mcp__filesystem__read_file'); expect(functions[1].name).toBe('mcp__web__fetch_url'); expect(functions[0].description).toBe('[filesystem] Read a file'); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nesquikm/mcp-rubber-duck'

If you have feedback or need assistance with the MCP directory API, please join our Discord server