Skip to main content
Glama
mcp-protocol.test.ts7.93 kB
/** * MCP 프로토콜 준수 테스트 * MCP SDK Client를 사용하여 서버의 프로토콜 표준 준수 여부 검증 */ import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; import * as path from 'path'; import * as fs from 'fs'; import * as os from 'os'; describe('MCP Protocol Compliance', () => { let client: Client; let transport: StdioClientTransport; let testVaultPath: string; let testIndexPath: string; beforeAll(async () => { // 테스트용 임시 디렉토리 생성 testVaultPath = fs.mkdtempSync(path.join(os.tmpdir(), 'mcp-test-vault-')); testIndexPath = path.join(os.tmpdir(), `mcp-test-index-${Date.now()}.db`); // 서버 경로 // __dirname: packages/mcp-server/__tests__/e2e // serverPath: packages/mcp-server/dist/cli.js const serverPath = path.join(__dirname, '..', '..', 'dist', 'cli.js'); // Transport 및 Client 초기화 // Transport가 서버 프로세스를 자동으로 관리 transport = new StdioClientTransport({ command: 'node', args: [ serverPath, '--vault', testVaultPath, '--index', testIndexPath ] }); client = new Client({ name: 'mcp-test-client', version: '1.0.0' }, { capabilities: {} }); await client.connect(transport); }, 30000); // 30초 타임아웃 afterAll(async () => { // Client 종료 (transport도 함께 정리됨) if (client) { try { await client.close(); } catch (error) { console.error('Error closing client:', error); } } // Transport 명시적 종료 (서버 프로세스 종료 포함) if (transport) { try { await transport.close(); } catch (error) { console.error('Error closing transport:', error); } } // 약간의 대기 시간 (프로세스 종료 완료 대기) await new Promise(resolve => setTimeout(resolve, 500)); // 테스트 파일 정리 if (fs.existsSync(testVaultPath)) { fs.rmSync(testVaultPath, { recursive: true, force: true }); } if (fs.existsSync(testIndexPath)) { fs.unlinkSync(testIndexPath); } }, 10000); describe('서버 초기화 및 연결', () => { test('서버에 성공적으로 연결', () => { expect(client).toBeDefined(); }); test('서버 정보 조회 가능', async () => { const serverInfo = await client.getServerVersion(); expect(serverInfo).toBeDefined(); }); }); describe('Tool 목록 조회', () => { test('Tool 목록을 반환', async () => { const result = await client.listTools(); expect(result).toBeDefined(); expect(result.tools).toBeInstanceOf(Array); expect(result.tools.length).toBeGreaterThan(0); }); test('각 Tool이 유효한 스키마를 가짐', async () => { const result = await client.listTools(); result.tools.forEach(tool => { // 필수 필드 검증 expect(tool.name).toBeDefined(); expect(typeof tool.name).toBe('string'); expect(tool.description).toBeDefined(); expect(typeof tool.description).toBe('string'); // inputSchema는 JSON Schema 형식이어야 함 if (tool.inputSchema) { expect(tool.inputSchema.type).toBeDefined(); } }); }); test('create_note 툴이 존재', async () => { const result = await client.listTools(); const createNoteTool = result.tools.find(t => t.name === 'create_note'); expect(createNoteTool).toBeDefined(); expect(createNoteTool?.description).toBeTruthy(); expect(createNoteTool?.inputSchema).toBeDefined(); }); test('search_memory 툴이 존재', async () => { const result = await client.listTools(); const searchMemoryTool = result.tools.find(t => t.name === 'search_memory'); expect(searchMemoryTool).toBeDefined(); expect(searchMemoryTool?.description).toBeTruthy(); expect(searchMemoryTool?.inputSchema).toBeDefined(); }); }); describe('Tool 실행', () => { test('create_note 툴 실행 가능', async () => { const result = await client.callTool({ name: 'create_note', arguments: { title: 'Test Note', content: 'This is a test note for MCP protocol compliance.', category: 'Resources' } }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); expect(Array.isArray(result.content)).toBe(true); // 결과가 TextContent 타입이어야 함 if (result.content.length > 0) { const firstContent = result.content[0]; expect(firstContent.type).toBe('text'); } }, 15000); test('잘못된 Tool 이름으로 호출 시 적절한 에러 반환', async () => { await expect( client.callTool({ name: 'non_existent_tool', arguments: {} }) ).rejects.toThrow(); }); test('잘못된 인자로 Tool 호출 시 적절한 에러 반환', async () => { await expect( client.callTool({ name: 'create_note', arguments: { // title이 누락됨 content: 'Invalid note' } }) ).rejects.toThrow(); }); }); describe('에러 응답 형식', () => { test('에러가 MCP 표준 형식을 따름', async () => { try { await client.callTool({ name: 'create_note', arguments: { title: 'Test', category: 'InvalidCategory' // 잘못된 category } }); fail('에러가 발생해야 함'); } catch (error: any) { // MCP 에러는 message 속성을 가져야 함 expect(error.message).toBeDefined(); expect(typeof error.message).toBe('string'); } }); }); describe('Resource 목록 조회 (선택사항)', () => { test('Resource 목록 조회 시도', async () => { try { const result = await client.listResources(); expect(result).toBeDefined(); } catch (error) { // Resource를 지원하지 않는 경우 에러 발생 가능 // 이는 선택사항이므로 테스트 실패로 간주하지 않음 console.log('Resources not supported:', error); } }); }); describe('Prompt 목록 조회 (선택사항)', () => { test('Prompt 목록 조회 시도', async () => { try { const result = await client.listPrompts(); expect(result).toBeDefined(); } catch (error) { // Prompt를 지원하지 않는 경우 에러 발생 가능 console.log('Prompts not supported:', error); } }); }); describe('통합 워크플로우', () => { test('노트 작성 → 검색 워크플로우', async () => { // 1. 노트 작성 const uniqueTitle = `Integration Test Note ${Date.now()}`; const createResult = await client.callTool({ name: 'create_note', arguments: { title: uniqueTitle, content: 'Integration test content for MCP compliance.', category: 'Resources', tags: ['test', 'mcp'] } }); expect(createResult).toBeDefined(); // 2. 약간의 대기 (인덱싱 시간) await new Promise(resolve => setTimeout(resolve, 1000)); // 3. 검색으로 찾기 const searchResult = await client.callTool({ name: 'search_memory', arguments: { query: uniqueTitle } }); expect(searchResult).toBeDefined(); expect(searchResult.content).toBeDefined(); // 검색 결과에 작성한 노트가 포함되어야 함 const resultText = JSON.stringify(searchResult.content); expect(resultText).toContain(uniqueTitle); }, 20000); }); });

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/inchan/memory-mcp'

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