Skip to main content
Glama

Knowledge MCP Server

by sven-borkert
test-client.tsโ€ข5.56 kB
import { spawn } from 'child_process'; import type { ChildProcess } from 'child_process'; import type { TestRunner, MCPCallData } from './test-helpers.js'; interface MCPRequest { jsonrpc: '2.0'; id: number; method: string; params: { name: string; arguments: Record<string, unknown>; }; } interface MCPResponse { jsonrpc: '2.0'; id: number; result?: { content: Array<{ type: string; text: string; }>; }; error?: { code: number; message: string; }; } interface TestClientOptions { serverPath: string; storagePath?: string; logLevel?: string; timeout?: number; } export class MCPTestClient { private server: ChildProcess | null = null; private requestId = 1; private responses: Map<number, MCPResponse> = new Map(); private isConnected = false; private options: Required<TestClientOptions>; private testRunner: TestRunner | null = null; constructor(options: TestClientOptions) { this.options = { serverPath: options.serverPath, storagePath: options.storagePath ?? process.env.KNOWLEDGE_MCP_HOME ?? '', logLevel: options.logLevel ?? 'ERROR', timeout: options.timeout ?? 5000, }; } async connect(): Promise<void> { return new Promise((resolve, reject) => { const env: Record<string, string> = { ...process.env, KNOWLEDGE_MCP_LOG_LEVEL: this.options.logLevel, }; if (this.options.storagePath) { env.KNOWLEDGE_MCP_HOME = this.options.storagePath; } this.server = spawn('node', [this.options.serverPath], { stdio: ['pipe', 'pipe', 'pipe'], env, }); this.server.stdout?.on('data', (data: Buffer) => { const lines = data .toString() .split('\n') .filter((line: string) => line.trim()); for (const line of lines) { try { const response = JSON.parse(line) as { id?: number }; if (response.id) { this.responses.set(response.id, response as MCPResponse); } } catch { // Ignore non-JSON lines (logs, etc.) } } }); this.server.stderr?.on('data', (_data: Buffer) => { // Ignore server logs in tests unless debugging if (this.options.logLevel === 'DEBUG') { console.error(_data.toString()); } }); this.server.on('error', (error) => { reject(new Error(`Server failed to start: ${error.message}`)); }); // Wait for server to initialize setTimeout(() => { this.isConnected = true; resolve(); }, 1000); }); } async disconnect(): Promise<void> { if (this.server) { this.server.kill(); this.server = null; this.isConnected = false; } // Add actual async operation if needed await Promise.resolve(); } setTestRunner(runner: TestRunner): void { this.testRunner = runner; } async callTool(toolName: string, arguments_: Record<string, unknown>): Promise<MCPResponse> { if (!this.isConnected) { throw new Error('Client not connected'); } const startTime = Date.now(); const id = this.requestId++; const request: MCPRequest = { jsonrpc: '2.0', id, method: 'tools/call', params: { name: toolName, arguments: arguments_, }, }; this.server?.stdin?.write(JSON.stringify(request) + '\n'); // Wait for response return new Promise<MCPResponse>((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error(`Timeout waiting for response to ${toolName}`)); }, this.options.timeout); const checkResponse = (): void => { if (this.responses.has(id)) { clearTimeout(timeout); const response = this.responses.get(id); this.responses.delete(id); const duration = Date.now() - startTime; // Track the MCP call if we have a test runner if (this.testRunner && response) { const callData: MCPCallData = { method: toolName, input: arguments_, output: response.error ? { error: response.error } : response.result?.content?.[0]?.text ? JSON.parse(response.result.content[0].text) : {}, duration, timestamp: new Date().toISOString(), }; this.testRunner.trackMCPCall(callData); } if (response?.error) { reject(new Error(`Tool error: ${JSON.stringify(response.error)}`)); } else if (response?.result?.content?.[0]?.text) { resolve(response); } else { reject(new Error('Invalid response format')); } } else { setTimeout(checkResponse, 100); } }; checkResponse(); }); } // Convenience method for tool calls that returns parsed result async callToolAndParse( toolName: string, arguments_: Record<string, unknown> ): Promise<Record<string, unknown>> { const response = await this.callTool(toolName, arguments_); return this.extractResult(response); } // Helper method to extract result content private extractResult(response: MCPResponse): Record<string, unknown> { if (!response.result?.content?.[0]?.text) { throw new Error('Invalid response format'); } return JSON.parse(response.result.content[0].text) as Record<string, unknown>; } }

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/sven-borkert/knowledge-mcp'

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