Skip to main content
Glama

1MCP Server

stdioProxyTransport.ts5.29 kB
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'; import { MCP_SERVER_VERSION } from '@src/constants.js'; import logger, { debugIf } from '@src/logger/logger.js'; /** * STDIO Proxy Transport Options */ export interface StdioProxyTransportOptions { serverUrl: string; preset?: string; filter?: string; tags?: string[]; timeout?: number; } /** * STDIO Proxy Transport * * Provides a STDIO interface that proxies all requests to a running 1MCP HTTP server. * Acts as a bridge between STDIO-only MCP clients and the centralized HTTP server. * * This implementation uses pure transport-to-transport forwarding without the Client layer, * to avoid conflicts with MCP protocol message handling. */ export class StdioProxyTransport { private stdioTransport: StdioServerTransport; private httpTransport: StreamableHTTPClientTransport; private isConnected = false; constructor(private options: StdioProxyTransportOptions) { // Create STDIO server transport (for client communication) this.stdioTransport = new StdioServerTransport(); // Create Streamable HTTP client transport (for HTTP server communication) const url = new URL(this.options.serverUrl); // Apply priority: preset > filter > tags (only one will be added) if (this.options.preset) { url.searchParams.set('preset', this.options.preset); } else if (this.options.filter) { url.searchParams.set('filter', this.options.filter); } else if (this.options.tags && this.options.tags.length > 0) { url.searchParams.set('tags', this.options.tags.join(',')); } this.httpTransport = new StreamableHTTPClientTransport(url, { requestInit: { headers: { 'User-Agent': `1MCP-Proxy/${MCP_SERVER_VERSION}`, }, }, }); } /** * Start the proxy transport */ async start(): Promise<void> { try { debugIf(() => ({ message: 'Starting STDIO proxy transport', meta: { serverUrl: this.options.serverUrl, tags: this.options.tags, }, })); // CRITICAL: Set up message forwarding BEFORE starting transports // This ensures handlers are ready when messages start flowing this.setupMessageForwarding(); // Start HTTP transport connection await this.httpTransport.start(); this.isConnected = true; logger.info('Connected to 1MCP HTTP server'); // Start STDIO transport await this.stdioTransport.start(); logger.info('STDIO proxy started successfully'); } catch (error) { logger.error(`Failed to start STDIO proxy: ${error}`); throw error; } } /** * Set up bidirectional message forwarding between STDIO and HTTP */ private setupMessageForwarding(): void { // Forward messages from STDIO client to HTTP server this.stdioTransport.onmessage = async (message: JSONRPCMessage) => { try { debugIf(() => ({ message: 'Forwarding message from STDIO to HTTP', meta: { method: (message as any).method, id: (message as any).id }, })); // Forward to HTTP server await this.httpTransport.send(message); } catch (error) { logger.error(`Error forwarding STDIO message to HTTP: ${error}`); } }; // Forward messages from HTTP server to STDIO client this.httpTransport.onmessage = async (message: JSONRPCMessage) => { try { debugIf(() => ({ message: 'Forwarding message from HTTP to STDIO', meta: { method: (message as any).method, id: (message as any).id }, })); // Forward to STDIO client await this.stdioTransport.send(message); } catch (error) { logger.error(`Error forwarding HTTP message to STDIO: ${error}`); } }; // Handle errors from STDIO transport this.stdioTransport.onerror = (error: Error) => { logger.error(`STDIO transport error: ${error.message}`); }; // Handle errors from HTTP transport this.httpTransport.onerror = (error: Error) => { logger.error(`HTTP transport error: ${error.message}`); }; // Handle STDIO transport close this.stdioTransport.onclose = async () => { logger.info('STDIO transport closed'); await this.close(); }; // Handle HTTP transport close this.httpTransport.onclose = async () => { logger.warn('HTTP server connection closed'); await this.close(); }; } /** * Close the proxy transport */ async close(): Promise<void> { if (!this.isConnected) { return; } // Set isConnected to false immediately to prevent re-entry // when transport close handlers trigger onclose events this.isConnected = false; try { debugIf('Closing STDIO proxy transport'); // Close HTTP transport await this.httpTransport.close(); // Close STDIO transport await this.stdioTransport.close(); logger.info('STDIO proxy closed'); } catch (error) { logger.error(`Error closing STDIO proxy: ${error}`); } } }

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/1mcp-app/agent'

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