Skip to main content
Glama
mcp-server.test.ts10.3 kB
/** * Tests for MCP Server Initialization * Verifies that the MCP server starts correctly and handles tools */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { getConfig } from '@/config'; import { createMCPServer } from '@/server'; import { type ToolDefinition, getAvailableTools, getTool } from '../src/tools'; // Mock dependencies jest.mock('@modelcontextprotocol/sdk/server/index.js'); jest.mock('@modelcontextprotocol/sdk/server/stdio.js'); jest.mock('@modelcontextprotocol/sdk/types.js', () => ({ ErrorCode: { MethodNotFound: -32601, InternalError: -32603, }, McpError: class McpError extends Error { constructor( public code: number, message: string ) { super(message); this.name = 'McpError'; } }, ListToolsRequestSchema: { method: 'tools/list' }, CallToolRequestSchema: { method: 'tools/call' }, })); jest.mock('@/config'); jest.mock('../src/tools', () => ({ getAvailableTools: jest.fn(), getTool: jest.fn(), getMCPMode: jest.fn(() => 'dev'), })); jest.mock('@/utils/logger'); describe('MCP Server Initialization', () => { beforeEach(() => { jest.clearAllMocks(); // Setup default mocks (getConfig as jest.Mock).mockReturnValue({ mcp: { name: 'teamcity-mcp', version: '0.1.0', capabilities: { tools: true, prompts: false, resources: false, }, }, server: { mode: 'dev', }, teamcity: { url: 'https://teamcity.example.com', token: 'test-token', }, }); const defaultTools = [ { name: 'ping', description: 'Test connectivity', inputSchema: { type: 'object', properties: { message: { type: 'string' }, }, }, handler: jest.fn(), }, ]; (getAvailableTools as jest.Mock).mockReturnValue(defaultTools); // Mock getTool to find tools from the available tools (getTool as jest.Mock).mockImplementation((name: string) => { const tools = (getAvailableTools as jest.Mock)() as ToolDefinition[]; return tools.find((tool: ToolDefinition) => tool.name === name); }); }); describe('createMCPServer', () => { it('should create an MCP server instance', () => { const mockServer = { setRequestHandler: jest.fn(), }; (Server as jest.Mock).mockImplementation(() => mockServer); const server = createMCPServer(); expect(server).toBeDefined(); }); // Removed implementation-detail assertions about handler registration and tool loading it('should handle ListToolsRequest', async () => { const mockSetRequestHandler = jest.fn(); const mockServer = { setRequestHandler: mockSetRequestHandler, }; (Server as jest.Mock).mockImplementation(() => mockServer); createMCPServer(); // Get the list tools handler const listToolsHandler = mockSetRequestHandler.mock.calls[0][1]; // Call the handler const result = await listToolsHandler(); expect(result).toEqual({ tools: [ { name: 'ping', description: 'Test connectivity', inputSchema: { type: 'object', properties: { message: { type: 'string' }, }, }, }, ], }); }); it('should handle CallToolRequest for existing tool', async () => { const mockHandler = jest.fn().mockResolvedValue({ success: true, data: { result: 'pong' } }); const toolMock = { name: 'ping', description: 'Test connectivity', inputSchema: {}, handler: mockHandler, }; (getAvailableTools as jest.Mock).mockReturnValue([toolMock]); (getTool as jest.Mock).mockImplementation((name: string) => { return name === 'ping' ? toolMock : undefined; }); const mockSetRequestHandler = jest.fn(); const mockServer = { setRequestHandler: mockSetRequestHandler, }; (Server as jest.Mock).mockImplementation(() => mockServer); createMCPServer(); // Get the call tool handler const callToolHandler = mockSetRequestHandler.mock.calls[1][1]; // Call the handler const result = await callToolHandler({ params: { name: 'ping', arguments: { message: 'test' }, }, }); expect(mockHandler).toHaveBeenCalledWith({ message: 'test' }); expect(result).toEqual({ content: [ { type: 'text', text: 'Tool executed successfully', }, ], }); }); it('should handle CallToolRequest for non-existing tool', async () => { const mockSetRequestHandler = jest.fn(); const mockServer = { setRequestHandler: mockSetRequestHandler, }; (Server as jest.Mock).mockImplementation(() => mockServer); createMCPServer(); // Get the call tool handler const callToolHandler = mockSetRequestHandler.mock.calls[1][1]; // Call the handler for non-existing tool await expect( callToolHandler({ params: { name: 'non-existing-tool', arguments: {}, }, }) ).rejects.toThrow('Unknown tool: non-existing-tool'); }); }); describe('Server Configuration', () => { it('should initialize using configuration without errors', () => { const mockServer = { setRequestHandler: jest.fn(), }; (Server as jest.Mock).mockImplementation(() => mockServer); expect(() => createMCPServer()).not.toThrow(); }); it('should handle disabled capabilities', () => { (getConfig as jest.Mock).mockReturnValue({ mcp: { name: 'teamcity-mcp', version: '0.1.0', capabilities: { tools: false, prompts: false, resources: false, }, }, server: { mode: 'dev', }, }); const mockServer = { setRequestHandler: jest.fn(), }; (Server as jest.Mock).mockImplementation(() => mockServer); expect(() => createMCPServer()).not.toThrow(); }); }); describe('Error Handling', () => { it('should handle errors in tool execution', async () => { const mockHandler = jest.fn().mockResolvedValue({ success: false, error: 'Tool error' }); const toolMock = { name: 'failing-tool', description: 'A tool that fails', inputSchema: {}, handler: mockHandler, }; (getAvailableTools as jest.Mock).mockReturnValue([toolMock]); (getTool as jest.Mock).mockImplementation((name: string) => { return name === 'failing-tool' ? toolMock : undefined; }); const mockSetRequestHandler = jest.fn(); const mockServer = { setRequestHandler: mockSetRequestHandler, }; (Server as jest.Mock).mockImplementation(() => mockServer); createMCPServer(); const callToolHandler = mockSetRequestHandler.mock.calls[1][1]; const result = await callToolHandler({ params: { name: 'failing-tool', arguments: {}, }, }); expect(result).toEqual({ content: [ { type: 'text', text: 'Tool error', }, ], }); }); it('should validate tool input against schema', async () => { const mockHandler = jest.fn(); const toolMock = { name: 'validated-tool', description: 'A tool with validation', inputSchema: { type: 'object', properties: { required_field: { type: 'string' }, }, required: ['required_field'], }, handler: mockHandler, }; (getAvailableTools as jest.Mock).mockReturnValue([toolMock]); (getTool as jest.Mock).mockImplementation((name: string) => { return name === 'validated-tool' ? toolMock : undefined; }); const mockSetRequestHandler = jest.fn(); const mockServer = { setRequestHandler: mockSetRequestHandler, }; (Server as jest.Mock).mockImplementation(() => mockServer); createMCPServer(); const callToolHandler = mockSetRequestHandler.mock.calls[1][1]; // Call with invalid input await expect( callToolHandler({ params: { name: 'validated-tool', arguments: { wrong_field: 'value' }, }, }) ).rejects.toThrow(); }); }); describe('Tool Registration', () => { it('should register multiple tools', async () => { (getAvailableTools as jest.Mock).mockReturnValue([ { name: 'tool1', description: 'First tool', inputSchema: {}, handler: jest.fn(), }, { name: 'tool2', description: 'Second tool', inputSchema: {}, handler: jest.fn(), }, { name: 'tool3', description: 'Third tool', inputSchema: {}, handler: jest.fn(), }, ]); const mockSetRequestHandler = jest.fn(); const mockServer = { setRequestHandler: mockSetRequestHandler, }; (Server as jest.Mock).mockImplementation(() => mockServer); createMCPServer(); const listToolsHandler = mockSetRequestHandler.mock.calls[0][1]; const result = await listToolsHandler(); expect(result.tools).toHaveLength(3); expect(result.tools.map((t: { name: string }) => t.name)).toEqual([ 'tool1', 'tool2', 'tool3', ]); }); it('should handle empty tool list', async () => { (getAvailableTools as jest.Mock).mockReturnValue([]); const mockSetRequestHandler = jest.fn(); const mockServer = { setRequestHandler: mockSetRequestHandler, }; (Server as jest.Mock).mockImplementation(() => mockServer); createMCPServer(); const listToolsHandler = mockSetRequestHandler.mock.calls[0][1]; const result = await listToolsHandler(); expect(result.tools).toHaveLength(0); }); }); });

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/Daghis/teamcity-mcp'

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