Skip to main content
Glama
itsocialist

Claude Code Connector MCP

by itsocialist
mcp_server.integration.test.ts14.3 kB
/** * Integration tests for Claude Code Connector MCP Server * * These tests spawn the actual MCP server and send JSON-RPC requests * to verify end-to-end functionality. */ import { spawn, ChildProcess } from 'child_process'; import { join } from 'path'; import { mkdir, rm, writeFile, readFile } from 'fs/promises'; import { existsSync } from 'fs'; import * as path from 'path'; // Server path - use path.resolve for compatibility const SERVER_PATH = path.resolve(process.cwd(), 'dist', 'index.js'); // Base test directory - each test gets a unique subdirectory const TEST_BASE_DIR = '/tmp/mcp-integration-tests'; const PROJECTS_JSON_PATH = join(process.env.HOME || '', '.claude', 'projects.json'); // Helper to send JSON-RPC request to server async function sendRequest( child: ChildProcess, method: string, params: Record<string, any>, id: number = 1 ): Promise<any> { return new Promise((resolve, reject) => { const request = { jsonrpc: '2.0', id, method, params }; let responseData = ''; const onData = (data: Buffer) => { responseData += data.toString(); try { const parsed = JSON.parse(responseData); child.stdout?.off('data', onData); resolve(parsed); } catch { // Wait for more data } }; child.stdout?.on('data', onData); setTimeout(() => { child.stdout?.off('data', onData); reject(new Error('Request timeout')); }, 5000); child.stdin?.write(JSON.stringify(request) + '\n'); }); } // Helper to spawn server function spawnServer(): ChildProcess { return spawn('node', [SERVER_PATH], { stdio: ['pipe', 'pipe', 'pipe'] }); } // Helper to clean test projects from projects.json async function cleanTestProjects(): Promise<void> { if (existsSync(PROJECTS_JSON_PATH)) { try { const data = JSON.parse(await readFile(PROJECTS_JSON_PATH, 'utf-8')); data.projects = data.projects.filter((p: any) => !p.id.startsWith('integration-test')); await writeFile(PROJECTS_JSON_PATH, JSON.stringify(data, null, 2), 'utf-8'); } catch { // Ignore cleanup errors } } } // Generate unique test directory let testCounter = 0; function getUniqueTestDir(): string { testCounter++; return join(TEST_BASE_DIR, `test-${Date.now()}-${testCounter}`); } describe('MCP Server Integration Tests', () => { let server: ChildProcess; beforeAll(async () => { // Clean up any leftover test projects await cleanTestProjects(); // Create base test directory await mkdir(TEST_BASE_DIR, { recursive: true }); }); afterAll(async () => { // Clean up test data await cleanTestProjects(); // Clean up test directory await rm(TEST_BASE_DIR, { recursive: true, force: true }); }); beforeEach(() => { server = spawnServer(); }); afterEach(() => { if (server && !server.killed) { server.kill(); } }); describe('register_project', () => { it('should register a new project successfully', async () => { const testDir = getUniqueTestDir(); await mkdir(testDir, { recursive: true }); const response = await sendRequest(server, 'tools/call', { name: 'register_project', arguments: { name: 'Integration Test Project', rootPath: testDir, id: 'integration-test-reg-1' } }); expect(response.result).toBeDefined(); const content = JSON.parse(response.result.content[0].text); expect(content.success).toBe(true); expect(content.projectId).toBe('integration-test-reg-1'); }); it('should reject non-existent path', async () => { const response = await sendRequest(server, 'tools/call', { name: 'register_project', arguments: { name: 'Bad Project', rootPath: '/nonexistent/path/12345' } }); expect(response.result.isError).toBe(true); const content = JSON.parse(response.result.content[0].text); expect(content.error).toBe('INVALID_PATH'); }); it('should reject missing required fields', async () => { const response = await sendRequest(server, 'tools/call', { name: 'register_project', arguments: { name: 'No Path Project' // Missing rootPath } }); expect(response.result.isError).toBe(true); const content = JSON.parse(response.result.content[0].text); expect(content.error).toBe('INVALID_ARGUMENTS'); }); }); describe('list_projects', () => { it('should list registered projects', async () => { const testDir = getUniqueTestDir(); await mkdir(testDir, { recursive: true }); // First register a project in this server session await sendRequest(server, 'tools/call', { name: 'register_project', arguments: { name: 'List Test Project', rootPath: testDir, id: 'integration-test-list' } }); // Query in same server session const response = await sendRequest(server, 'tools/call', { name: 'list_projects', arguments: { includeInactive: true } }); expect(response.result).toBeDefined(); const content = JSON.parse(response.result.content[0].text); expect(content.projects).toBeDefined(); expect(Array.isArray(content.projects)).toBe(true); expect(content.projects.length).toBeGreaterThan(0); }); it('should filter inactive projects by default', async () => { const response = await sendRequest(server, 'tools/call', { name: 'list_projects', arguments: {} }); expect(response.result).toBeDefined(); const content = JSON.parse(response.result.content[0].text); // All returned projects should be active content.projects.forEach((p: any) => { expect(p.active).toBe(true); }); }); }); describe('write_to_project and read_from_project', () => { it('should write a file to project', async () => { const testDir = getUniqueTestDir(); await mkdir(testDir, { recursive: true }); // Register project in same session await sendRequest(server, 'tools/call', { name: 'register_project', arguments: { name: 'Write Test', rootPath: testDir, id: 'integration-test-write' } }); const response = await sendRequest(server, 'tools/call', { name: 'write_to_project', arguments: { projectId: 'integration-test-write', filePath: 'test-file.txt', content: 'Hello Integration Test!' } }); expect(response.result).toBeDefined(); expect(response.result.isError).toBeFalsy(); const content = JSON.parse(response.result.content[0].text); expect(content.success).toBe(true); expect(content.action).toBe('created'); }); it('should read a file from project', async () => { const testDir = getUniqueTestDir(); await mkdir(testDir, { recursive: true }); // Register project in same session await sendRequest(server, 'tools/call', { name: 'register_project', arguments: { name: 'Read Test', rootPath: testDir, id: 'integration-test-read' } }); // Write a file await sendRequest(server, 'tools/call', { name: 'write_to_project', arguments: { projectId: 'integration-test-read', filePath: 'read-test.txt', content: 'Content to read back' } }); // Read it back const response = await sendRequest(server, 'tools/call', { name: 'read_from_project', arguments: { projectId: 'integration-test-read', filePath: 'read-test.txt' } }); expect(response.result).toBeDefined(); const content = JSON.parse(response.result.content[0].text); expect(content.content).toBe('Content to read back'); }); it('should reject directory traversal attempts', async () => { const testDir = getUniqueTestDir(); await mkdir(testDir, { recursive: true }); // Register project in same session await sendRequest(server, 'tools/call', { name: 'register_project', arguments: { name: 'Traversal Test', rootPath: testDir, id: 'integration-test-traversal' } }); const response = await sendRequest(server, 'tools/call', { name: 'write_to_project', arguments: { projectId: 'integration-test-traversal', filePath: '../../../etc/passwd', content: 'malicious' } }); expect(response.result.isError).toBe(true); const content = JSON.parse(response.result.content[0].text); expect(content.error).toBe('INVALID_PATH'); }); it('should reject writing to non-existent project', async () => { const response = await sendRequest(server, 'tools/call', { name: 'write_to_project', arguments: { projectId: 'nonexistent-project-id', filePath: 'test.txt', content: 'test' } }); expect(response.result.isError).toBe(true); const content = JSON.parse(response.result.content[0].text); expect(content.error).toBe('PROJECT_NOT_FOUND'); }); it('should create nested directories when writing', async () => { const testDir = getUniqueTestDir(); await mkdir(testDir, { recursive: true }); // Register project in same session await sendRequest(server, 'tools/call', { name: 'register_project', arguments: { name: 'Nested Test', rootPath: testDir, id: 'integration-test-nested' } }); const response = await sendRequest(server, 'tools/call', { name: 'write_to_project', arguments: { projectId: 'integration-test-nested', filePath: 'deep/nested/path/file.txt', content: 'Nested content', createDirs: true } }); expect(response.result).toBeDefined(); const content = JSON.parse(response.result.content[0].text); expect(content.success).toBe(true); }); }); describe('resources', () => { it('should list project files resource', async () => { const testDir = getUniqueTestDir(); await mkdir(testDir, { recursive: true }); // Register a project first await sendRequest(server, 'tools/call', { name: 'register_project', arguments: { name: 'Resource Test', rootPath: testDir, id: 'integration-test-resource' } }); // List resources const response = await sendRequest(server, 'resources/list', {}); expect(response.result).toBeDefined(); expect(response.result.resources).toBeDefined(); // Should include our test project const hasTestResource = response.result.resources.some( (r: any) => r.uri.includes('integration-test') ); expect(hasTestResource).toBe(true); }); it('should read project files resource', async () => { const testDir = getUniqueTestDir(); await mkdir(testDir, { recursive: true }); // Register project and create a file await sendRequest(server, 'tools/call', { name: 'register_project', arguments: { name: 'Resource Read Test', rootPath: testDir, id: 'integration-test-resource-read' } }); await sendRequest(server, 'tools/call', { name: 'write_to_project', arguments: { projectId: 'integration-test-resource-read', filePath: 'resource-test.txt', content: 'For resource test' } }); const response = await sendRequest(server, 'resources/read', { uri: 'claude-code://integration-test-resource-read/files' }); expect(response.result).toBeDefined(); expect(response.result.contents).toBeDefined(); const content = JSON.parse(response.result.contents[0].text); expect(content.tree).toBeDefined(); expect(Array.isArray(content.tree)).toBe(true); }); }); });

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/itsocialist/claude-code-connector-mcp'

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