Skip to main content
Glama
mcp-stdio-e2e.test.ts4.11 kB
/** * E2E test for MCP server running in stdio mode. * * This test spawns the MCP server as a child process, communicates via stdin/stdout * using the MCP protocol, and verifies basic functionality works correctly. */ import path from "node:path"; import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; describe("MCP stdio server E2E", () => { let client: Client | null = null; let transport: StdioClientTransport | null = null; beforeEach(() => { // Reset state before each test client = null; transport = null; }); afterEach(async () => { // Clean up client connection if (client) { try { await client.close(); } catch { // Ignore errors during cleanup } client = null; } // Clean up transport if (transport) { try { await transport.close(); } catch { // Ignore errors during cleanup } transport = null; } }); it("should start, respond to initialize, and list tools", async () => { // Using vite-node to run TypeScript directly const projectRoot = path.resolve(import.meta.dirname, ".."); const entryPoint = path.join(projectRoot, "src", "index.ts"); // Build environment without VITEST_WORKER_ID to ensure proper logging behavior const testEnv = { ...process.env }; delete testEnv.VITEST_WORKER_ID; // Create stdio transport which spawns its own process transport = new StdioClientTransport({ command: "npx", args: ["vite-node", entryPoint], cwd: projectRoot, env: { ...testEnv, DOCS_MCP_STORE_PATH: path.join(projectRoot, "test", ".test-store-stdio"), DOCS_MCP_TELEMETRY: "false", }, }); // Create MCP client client = new Client( { name: "test-client", version: "1.0.0", }, { capabilities: {}, }, ); // Connect client to server via transport await client.connect(transport); // List available tools - this is a basic operation that should work const toolsResult = await client.listTools(); // Verify we got some tools back expect(toolsResult).toBeDefined(); expect(toolsResult.tools).toBeDefined(); expect(Array.isArray(toolsResult.tools)).toBe(true); // The server should have at least some tools registered // Based on the codebase, we expect tools like scrape_docs, search_docs, etc. expect(toolsResult.tools.length).toBeGreaterThan(0); // Verify some expected tool names const toolNames = toolsResult.tools.map((t) => t.name); expect(toolNames).toContain("search_docs"); expect(toolNames).toContain("list_libraries"); }, 30000); // 30 second timeout for this test it("should handle shutdown gracefully", async () => { const projectRoot = path.resolve(import.meta.dirname, ".."); const entryPoint = path.join(projectRoot, "src", "index.ts"); // Create stdio transport which spawns its own process // Build environment without VITEST_WORKER_ID const testEnv = { ...process.env }; delete testEnv.VITEST_WORKER_ID; transport = new StdioClientTransport({ command: "npx", args: ["vite-node", entryPoint], cwd: projectRoot, env: { ...testEnv, DOCS_MCP_STORE_PATH: path.join(projectRoot, "test", ".test-store-stdio"), DOCS_MCP_TELEMETRY: "false", }, }); client = new Client( { name: "test-client", version: "1.0.0", }, { capabilities: {}, }, ); // Connect await client.connect(transport); // Verify connection works const toolsResult = await client.listTools(); expect(toolsResult.tools.length).toBeGreaterThan(0); // Close the client (should send shutdown/exit) await client.close(); client = null; // Close the transport await transport.close(); transport = null; }, 30000); });

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/arabold/docs-mcp-server'

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