Skip to main content
Glama
mcp-test-harness.ts5.47 kB
import { EventEmitter } from 'events'; import dummyProjects from '@/dummy-data/projects.json' assert { type: 'json' }; export interface MCPContentItem { type: string; text: string; } export interface MCPMessage { jsonrpc: '2.0'; id: number; method?: string; params?: Record<string, unknown>; result?: { content?: MCPContentItem[]; capabilities?: Record<string, unknown>; [key: string]: unknown; }; error?: { code: number; message: string; data?: unknown; }; } interface Transport { onMessage: (handler: (message: MCPMessage) => void) => void; send: (message: MCPMessage) => void; } class InMemoryTransport implements Transport { private messageHandler?: (message: MCPMessage) => void; private otherTransport?: InMemoryTransport; onMessage(handler: (message: MCPMessage) => void): void { this.messageHandler = handler; } send(message: MCPMessage): void { if (this.otherTransport?.messageHandler) { this.otherTransport.messageHandler(message); } } static createLinkedPair(): [InMemoryTransport, InMemoryTransport] { const client = new InMemoryTransport(); const server = new InMemoryTransport(); client.otherTransport = server; server.otherTransport = client; return [client, server]; } } export class MCPTestHarness { private clientTransport: InMemoryTransport; private serverTransport: InMemoryTransport; private responseHandlers = new Map<number, (response: MCPMessage) => void>(); private isConnected = false; private nextMessageId = 1; constructor() { [this.clientTransport, this.serverTransport] = InMemoryTransport.createLinkedPair(); // Set up server-side message handling this.serverTransport.onMessage((message: MCPMessage) => { if (message.method === 'initialize') { this.handleInitialize(message); } else if (message.method === 'tool/call') { this.handleToolCall(message); } }); // Set up client-side message handling this.clientTransport.onMessage((message: MCPMessage) => { if (message.id && this.responseHandlers.has(message.id)) { const handler = this.responseHandlers.get(message.id)!; handler(message); } }); } private handleInitialize(message: MCPMessage): void { if (!message.id) { return; } this.serverTransport.send({ jsonrpc: '2.0', id: message.id, result: { capabilities: { sampling: {}, roots: { listChanged: true } } } }); } private handleToolCall(message: MCPMessage): void { if (!message.id || !message.params) { return; } const { name, args } = message.params as { name: string; args: Record<string, unknown> }; if (name === 'claudeus_plane_projects__list') { if (!args.workspace) { this.serverTransport.send({ jsonrpc: '2.0', id: message.id, result: { content: [{ type: 'text', text: 'Error: Missing required workspace parameter' }] } }); return; } if (args.workspace === 'invalid-workspace-id') { this.serverTransport.send({ jsonrpc: '2.0', id: message.id, result: { content: [{ type: 'text', text: 'Error: Invalid workspace ID' }] } }); return; } // Return actual dummy projects for valid workspace this.serverTransport.send({ jsonrpc: '2.0', id: message.id, result: { content: [{ type: 'text', text: JSON.stringify(dummyProjects) }] } }); } } async connect(): Promise<MCPMessage> { if (this.isConnected) { throw new Error('Already connected'); } this.isConnected = true; return this.sendInitialize(); } async sendInitialize(): Promise<MCPMessage> { const initMessage: MCPMessage = { jsonrpc: '2.0', id: this.nextMessageId++, method: 'initialize', params: { capabilities: { sampling: {}, roots: { listChanged: true } } } }; return this.sendMessage(initMessage); } async sendMessage(message: MCPMessage): Promise<MCPMessage> { if (!this.isConnected) { throw new Error('Not connected'); } if (!message.id) { throw new Error('Message must have an ID'); } return new Promise((resolve, reject) => { const timeout = setTimeout(() => { this.responseHandlers.delete(message.id!); reject(new Error('Message timeout')); }, 5000); this.responseHandlers.set(message.id, (response: MCPMessage) => { clearTimeout(timeout); this.responseHandlers.delete(message.id!); resolve(response); }); this.clientTransport.send(message); }); } async callTool(name: string, args: Record<string, unknown>): Promise<MCPMessage> { const message: MCPMessage = { jsonrpc: '2.0', id: this.nextMessageId++, method: 'tool/call', params: { name, args } }; return this.sendMessage(message); } onServerMessage(message: MCPMessage): void { this.serverTransport.send(message); } clearHandlers(): void { this.responseHandlers.clear(); this.isConnected = false; } }

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/deus-h/claudeus-plane-mcp'

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