Skip to main content
Glama

MCPMan

by semistrict
server-states.test.ts7.84 kB
import { test, expect, describe, beforeAll, afterAll } from 'vitest'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; import { ListRootsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import { writeFile, mkdir } from 'node:fs/promises'; const testConfigDir = 'tests/config-server-states'; const testConfigPath = `${testConfigDir}/settings.json`; describe('Server States Test', () => { let client: Client; let transport: StdioClientTransport; beforeAll(async () => { // Create a test config with one autoConnect and one manual server const testConfig = { version: '1.0', servers: { filesystem: { transport: 'stdio', command: 'npx', args: ['@modelcontextprotocol/server-filesystem'], env: {}, autoConnect: true, timeout: 30000, }, 'filesystem-manual': { transport: 'stdio', command: 'npx', args: ['@modelcontextprotocol/server-filesystem'], env: {}, autoConnect: false, timeout: 30000, }, }, logging: { level: 'error', }, }; // Create directory and write config await mkdir(testConfigDir, { recursive: true }); await writeFile(testConfigPath, JSON.stringify(testConfig, null, 2)); // Create client with custom config transport = new StdioClientTransport({ command: 'bun', args: ['index.ts'], env: { ...process.env, MCP_CONFIG_DIR: testConfigDir, }, }); client = new Client( { name: 'server-states-test-client', version: '1.0.0', }, { capabilities: { roots: { listChanged: true }, }, } ); client.setRequestHandler(ListRootsRequestSchema, async () => ({ roots: [ { uri: `file://${process.cwd()}`, name: 'Test Directory', }, ], })); await client.connect(transport); await waitForToolRegistration(client); }); afterAll(async () => { await client.close(); }); test('help tool should error for disabled server', async () => { // This must be the first test to ensure filesystem hasn't been enabled yet const result = await client.callTool({ name: 'help', arguments: { server: 'filesystem' }, }); const content = result.content as Array<{ type: string; text: string }>; const text = content[0]?.text || ''; expect(text).toContain('not enabled'); expect(text).toContain('Use enable_server'); }); test('list_servers should show all servers with their states', async () => { const result = await client.callTool({ name: 'list_servers', arguments: {}, }); const content = result.content as Array<{ type: string; text: string }>; const data = JSON.parse(content[0]?.text || '{}'); expect(data.servers).toBeDefined(); expect(data.servers.filesystem).toBeDefined(); expect(data.servers['filesystem-manual']).toBeDefined(); // filesystem should be connected (autoConnect: true) expect(data.servers.filesystem.state).toBe('connected'); expect(data.servers.filesystem.toolCount).toBe(0); // No tools available when not enabled // filesystem-manual should be not-connected (autoConnect: false) expect(data.servers['filesystem-manual'].state).toBe('not-connected'); }); test('should not be able to call tools on connected but not enabled server', async () => { const result = await client.callTool({ name: 'invoke', arguments: { calls: [ { server: 'filesystem', tool: 'list_directory', parameters: { path: '.' }, }, ], }, }); const content = result.content as Array<{ type: string; text: string }>; const text = content[0]?.text || ''; // Should get an error about server not being enabled expect(text).toContain('not enabled'); expect(text).toContain('state: connected'); }); test('enable_server should enable a connected server', async () => { const result = await client.callTool({ name: 'enable_server', arguments: { name: 'filesystem' }, }); const content = result.content as Array<{ type: string; text: string }>; const text = content[0]?.text || ''; expect(text).toContain("Server 'filesystem' enabled"); expect(text).toContain('tools available'); expect(text).toContain('TypeScript Declarations'); }); test('should be able to call tools after enabling server', async () => { const result = await client.callTool({ name: 'eval', arguments: { function: 'async () => { const files = await filesystem.listDirectory({ path: "." }); return files.length > 0; }', }, }); const content = result.content as Array<{ type: string; text: string }>; const text = content[0]?.text || ''; expect(text).toContain('$results'); expect(text).toContain('true'); }); test('list_servers should show enabled state after enabling', async () => { const result = await client.callTool({ name: 'list_servers', arguments: {}, }); const content = result.content as Array<{ type: string; text: string }>; const data = JSON.parse(content[0]?.text || '{}'); expect(data.servers.filesystem.state).toBe('enabled'); expect(data.servers.filesystem.toolCount).toBeGreaterThan(0); }); test('enable_server should connect and enable a not-connected server', async () => { const result = await client.callTool({ name: 'enable_server', arguments: { name: 'filesystem-manual' }, }); const content = result.content as Array<{ type: string; text: string }>; const text = content[0]?.text || ''; expect(text).toContain("Server 'filesystem-manual' enabled"); expect(text).toContain('tools available'); }); test('list_servers should show manual server as enabled after enable_server', async () => { const result = await client.callTool({ name: 'list_servers', arguments: {}, }); const content = result.content as Array<{ type: string; text: string }>; const data = JSON.parse(content[0]?.text || '{}'); expect(data.servers['filesystem-manual'].state).toBe('enabled'); expect(data.servers['filesystem-manual'].toolCount).toBeGreaterThan(0); }); test('enable_server should return message if already enabled', async () => { const result = await client.callTool({ name: 'enable_server', arguments: { name: 'filesystem' }, }); const content = result.content as Array<{ type: string; text: string }>; const text = content[0]?.text || ''; expect(text).toContain('already enabled'); }); test('enable_server should error for non-existent server', async () => { const result = await client.callTool({ name: 'enable_server', arguments: { name: 'nonexistent' }, }); const content = result.content as Array<{ type: string; text: string }>; const text = content[0]?.text || ''; expect(text).toContain('not found'); expect(text).toContain('Available servers'); }); }); async function waitForToolRegistration( client: Client, maxRetries = 50, delayMs = 100 ): Promise<void> { for (let i = 0; i < maxRetries; i++) { try { const result = await client.listTools(); const evalTool = result.tools?.find((tool) => tool.name === 'eval'); if (evalTool) { return; } } catch (error) { // Ignore errors and keep retrying } await new Promise((resolve) => setTimeout(resolve, delayMs)); } throw new Error('Timeout waiting for tool registration to complete'); }

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/semistrict/mcpman'

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