diff --git a/api/src/mcp/mcp-server.service.ts b/api/src/mcp/mcp-server.service.ts index cb24be5..7446bb6 100644 --- a/api/src/mcp/mcp-server.service.ts +++ b/api/src/mcp/mcp-server.service.ts @@ -22,15 +22,8 @@ export class McpServerService { } async startHttp(port = 3001): Promise { - const mcpServer = this.createServer(); - const transport = new StreamableHTTPServerTransport({ - sessionIdGenerator: undefined, - }); - - await mcpServer.connect(transport); - const httpServer = createServer((request, response) => { - void this.handleHttpRequest(request, response, transport); + void this.handleHttpRequest(request, response); }); await new Promise((resolve) => { @@ -55,7 +48,6 @@ export class McpServerService { private async handleHttpRequest( request: IncomingMessage, response: ServerResponse, - transport: StreamableHTTPServerTransport, ): Promise { this.setCorsHeaders(response); @@ -80,20 +72,72 @@ export class McpServerService { return; } + if (this.isBrowserHealthRequest(request)) { + response.writeHead(200, { 'content-type': 'application/json' }); + response.end( + JSON.stringify({ + name: 'strava-mcp', + status: 'ok', + transport: 'streamable-http', + endpoint: '/mcp', + note: 'Use an MCP client or POST a JSON-RPC initialize request with Accept: application/json, text/event-stream.', + }), + ); + return; + } + + const mcpServer = this.createServer(); + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + }); + try { + await mcpServer.connect(transport); await transport.handleRequest(request, response); } catch (error) { + process.stderr.write( + `MCP HTTP request failed: ${ + error instanceof Error ? (error.stack ?? error.message) : String(error) + }\n`, + ); if (!response.headersSent) { - response.writeHead(500, { 'content-type': 'application/json' }); + response.writeHead(this.errorStatus(error), { + 'content-type': 'application/json', + }); } response.end( JSON.stringify({ error: error instanceof Error ? error.message : 'MCP request failed', }), ); + } finally { + await mcpServer.close().catch(() => undefined); } } + private isBrowserHealthRequest(request: IncomingMessage): boolean { + if (request.method !== 'GET') { + return false; + } + + const accept = request.headers.accept ?? ''; + return !accept.includes('text/event-stream'); + } + + private errorStatus(error: unknown): number { + const message = error instanceof Error ? error.message : ''; + if (message.includes('Not Acceptable')) { + return 406; + } + if (message.includes('Method not allowed')) { + return 405; + } + if (message.includes('Bad Request')) { + return 400; + } + return 500; + } + private isAuthorized(request: IncomingMessage): boolean { const token = process.env.MCP_AUTH_TOKEN; if (!token) {