Skip to main content
Glama
hablapro

Google Search Console MCP Server

by hablapro
sseStdioHandler.ts5.71 kB
import { toolRegistry } from "./tools/index"; interface MCPMessage { jsonrpc: string; id?: number | string; method?: string; params?: any; } interface MCPResponse { jsonrpc: string; id?: number | string; result?: any; error?: { code: number; message: string; data?: any; }; } export class SSEStdioHandler { private env: Env; private tools: Map<string, any> = new Map(); constructor(env: Env) { this.env = env; this.init(); } async init() { // Register all tools const params = { env: this.env }; toolRegistry.forEach(tool => { this.tools.set(tool.name, { definition: tool, execute: (args: any) => tool.execute(params, args) }); }); } async processMessage(message: MCPMessage, writer: WritableStreamDefaultWriter): Promise<void> { const response: MCPResponse = { jsonrpc: "2.0", id: message.id }; try { switch (message.method) { case "initialize": response.result = { protocolVersion: "2024-11-05", serverInfo: { name: "gsc-mcp-cloud", version: "1.0.0" }, capabilities: { tools: {} } }; break; case "tools/list": response.result = { tools: Array.from(this.tools.values()).map(tool => ({ name: tool.definition.name, description: tool.definition.description, inputSchema: { type: "object", properties: this.schemaToProperties(tool.definition.schema), required: this.getRequiredFields(tool.definition.schema) } })) }; break; case "tools/call": const toolName = message.params?.name; const toolArgs = message.params?.arguments || {}; const tool = this.tools.get(toolName); if (!tool) { throw new Error(`Tool not found: ${toolName}`); } // Validate arguments against schema const validatedArgs = tool.definition.schema.parse(toolArgs); // Execute tool const result = await tool.execute(validatedArgs); response.result = result; break; default: throw new Error(`Unknown method: ${message.method}`); } } catch (error: any) { response.error = { code: -32603, message: error.message || "Internal error", data: { type: error.name || "Error", stack: error.stack, details: error.toString() } }; } // Write response to SSE stream const encoder = new TextEncoder(); await writer.write(encoder.encode(`data: ${JSON.stringify(response)}\n\n`)); } private schemaToProperties(schema: any): any { const shape = schema._def.shape(); const properties: any = {}; for (const [key, value] of Object.entries(shape)) { const zodType = value as any; properties[key] = { type: this.getZodType(zodType), description: zodType._def.description || "" }; // Add enum values if present if (zodType._def.values) { properties[key].enum = Array.from(zodType._def.values); } } return properties; } private getZodType(zodType: any): string { const typeName = zodType._def.typeName; switch (typeName) { case "ZodString": return "string"; case "ZodNumber": return "number"; case "ZodBoolean": return "boolean"; case "ZodArray": return "array"; case "ZodObject": return "object"; default: return "string"; } } private getRequiredFields(schema: any): string[] { const shape = schema._def.shape(); const required: string[] = []; for (const [key, value] of Object.entries(shape)) { const zodType = value as any; if (!zodType.isOptional()) { required.push(key); } } return required; } async handleSSE(request: Request): Promise<Response> { const { readable, writable } = new TransformStream(); const writer = writable.getWriter(); const encoder = new TextEncoder(); // Handle SSE connection (async () => { try { // Send initial connection message await writer.write(encoder.encode(": connection established\n\n")); // Handle incoming messages if (request.method === "POST") { const body = await request.json() as MCPMessage; await this.processMessage(body, writer); } else { // For GET requests, send tools list const listMessage: MCPMessage = { jsonrpc: "2.0", id: 1, method: "tools/list" }; await this.processMessage(listMessage, writer); } } catch (error: any) { await writer.write(encoder.encode(`data: ${JSON.stringify({ jsonrpc: "2.0", error: { code: -32603, message: error.message || "Internal error", data: { type: error.name || "Error", details: error.toString() } } })}\n\n`)); } finally { await writer.close(); } })(); return new Response(readable, { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "Connection": "keep-alive", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type" } }); } }

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/hablapro/mcp-gsc'

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