Skip to main content
Glama
by Rana-X
mcp-server.test.js10.4 kB
import { describe, test, expect, jest, beforeEach, afterEach } from '@jest/globals'; import { EventEmitter } from 'events'; // Mock the MCP SDK jest.mock('@modelcontextprotocol/sdk/server/index.js', () => ({ StdioServerTransport: jest.fn(() => ({ start: jest.fn() })), Server: jest.fn(() => { const emitter = new EventEmitter(); return { setRequestHandler: jest.fn((type, handler) => { emitter.on(type, handler); }), connect: jest.fn(async (transport) => { return Promise.resolve(); }), _emit: (type, request) => { return new Promise((resolve) => { emitter.emit(type, request, { send: resolve }); }); } }; }) })); // Mock Resend jest.mock('resend', () => ({ Resend: jest.fn(() => ({ emails: { send: jest.fn(async () => ({ data: { id: 'mock-email-id' }, error: null })) } })) })); // Mock dotenv jest.mock('dotenv/config', () => ({}), { virtual: true }); describe('MCP Server Tests', () => { let server; let mockConsoleLog; let mockConsoleError; beforeEach(() => { // Clear all mocks jest.clearAllMocks(); // Mock console mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); // Set environment variables process.env.RESEND_API_KEY = 'test_key'; process.env.FROM_EMAIL = 'gwen@irl-concierge.com'; process.env.PARTNER_EMAILS = 'partner@example.com'; }); afterEach(() => { mockConsoleLog.mockRestore(); mockConsoleError.mockRestore(); }); describe('Server Initialization', () => { test('creates MCP server with correct configuration', async () => { // Import the module (this runs the server setup) const { Server } = await import('@modelcontextprotocol/sdk/server/index.js'); // Dynamically import index.js which will use our mocked modules jest.isolateModules(async () => { await import('../index.js'); }); // Verify server was created expect(Server).toHaveBeenCalledWith( expect.objectContaining({ name: 'sf-cleaning', version: '1.0.0' }) ); }); test('registers all required request handlers', async () => { const { Server } = await import('@modelcontextprotocol/sdk/server/index.js'); jest.isolateModules(async () => { await import('../index.js'); }); const serverInstance = Server.mock.results[0].value; // Check that handlers were registered expect(serverInstance.setRequestHandler).toHaveBeenCalledWith( 'tools/list', expect.any(Function) ); expect(serverInstance.setRequestHandler).toHaveBeenCalledWith( 'tools/call', expect.any(Function) ); }); }); describe('Tools List Handler', () => { test('returns correct tool schema', async () => { const { Server } = await import('@modelcontextprotocol/sdk/server/index.js'); jest.isolateModules(async () => { await import('../index.js'); }); const serverInstance = Server.mock.results[0].value; // Simulate tools/list request const response = await serverInstance._emit('tools/list', {}); expect(response).toEqual({ tools: [{ name: 'request_cleaning', description: 'Request a cleaning service for a San Francisco address', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Customer name' }, phone: { type: 'string', description: 'Contact phone number' }, address: { type: 'string', description: 'Service address in San Francisco' } }, required: ['name', 'phone', 'address'] } }] }); }); }); describe('Tools Call Handler', () => { let serverInstance; beforeEach(async () => { const { Server } = await import('@modelcontextprotocol/sdk/server/index.js'); jest.isolateModules(async () => { await import('../index.js'); }); serverInstance = Server.mock.results[0].value; }); test('handles valid SF cleaning request', async () => { const response = await serverInstance._emit('tools/call', { params: { name: 'request_cleaning', arguments: { name: 'John Doe', phone: '4155551234', address: '123 Market St, San Francisco, CA' } } }); expect(response).toEqual({ content: [{ type: 'text', text: expect.stringContaining('✅ Sent!') }] }); }); test('rejects non-SF address', async () => { const response = await serverInstance._emit('tools/call', { params: { name: 'request_cleaning', arguments: { name: 'Jane Doe', phone: '5105551234', address: '456 Broadway, Oakland, CA' } } }); expect(response).toEqual({ content: [{ type: 'text', text: expect.stringContaining('Sorry, we only serve San Francisco') }] }); }); test('validates required fields', async () => { const response = await serverInstance._emit('tools/call', { params: { name: 'request_cleaning', arguments: { name: 'Test User', phone: '4155551234' // missing address } } }); expect(response).toEqual({ content: [{ type: 'text', text: expect.stringContaining('Missing required field') }] }); }); test('handles invalid phone number', async () => { const response = await serverInstance._emit('tools/call', { params: { name: 'request_cleaning', arguments: { name: 'Test User', phone: '123', // Invalid address: '789 Market St, SF' } } }); expect(response).toEqual({ content: [{ type: 'text', text: expect.stringContaining('Invalid phone number') }] }); }); test('handles unknown tool', async () => { const response = await serverInstance._emit('tools/call', { params: { name: 'unknown_tool', arguments: {} } }); expect(response).toEqual({ content: [{ type: 'text', text: expect.stringContaining('Unknown tool') }] }); }); test('handles email sending failure', async () => { // Mock Resend to fail const { Resend } = await import('resend'); Resend.mockImplementation(() => ({ emails: { send: jest.fn(async () => ({ data: null, error: { message: 'Email service down' } })) } })); const response = await serverInstance._emit('tools/call', { params: { name: 'request_cleaning', arguments: { name: 'Test User', phone: '4155551234', address: '123 Market St, SF' } } }); expect(response).toEqual({ content: [{ type: 'text', text: expect.stringContaining('Failed to send') }] }); }); }); describe('Environment Variables', () => { test('handles missing RESEND_API_KEY', async () => { delete process.env.RESEND_API_KEY; const { Server } = await import('@modelcontextprotocol/sdk/server/index.js'); jest.isolateModules(async () => { await import('../index.js'); }); expect(mockConsoleError).toHaveBeenCalledWith( expect.stringContaining('Missing RESEND_API_KEY') ); }); test('handles missing FROM_EMAIL', async () => { delete process.env.FROM_EMAIL; const { Server } = await import('@modelcontextprotocol/sdk/server/index.js'); jest.isolateModules(async () => { await import('../index.js'); }); const serverInstance = Server.mock.results[0].value; const response = await serverInstance._emit('tools/call', { params: { name: 'request_cleaning', arguments: { name: 'Test', phone: '4155551234', address: '123 Market St, SF' } } }); expect(response.content[0].text).toContain('not configured'); }); test('handles missing PARTNER_EMAILS', async () => { delete process.env.PARTNER_EMAILS; const { Server } = await import('@modelcontextprotocol/sdk/server/index.js'); jest.isolateModules(async () => { await import('../index.js'); }); const serverInstance = Server.mock.results[0].value; const response = await serverInstance._emit('tools/call', { params: { name: 'request_cleaning', arguments: { name: 'Test', phone: '4155551234', address: '123 Market St, SF' } } }); expect(response.content[0].text).toContain('not configured'); }); }); describe('Server Lifecycle', () => { test('starts server transport', async () => { const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/index.js'); jest.isolateModules(async () => { await import('../index.js'); }); const transport = StdioServerTransport.mock.results[0].value; expect(transport.start).toHaveBeenCalled(); }); test('logs server startup message', async () => { jest.isolateModules(async () => { await import('../index.js'); }); expect(mockConsoleLog).toHaveBeenCalledWith( expect.stringContaining('MCP Server running') ); }); }); });

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/Rana-X/irl'

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