From 013cf8bfcb4ae82d1ff6f775cfe632591fb52985 Mon Sep 17 00:00:00 2001 From: rafalzawadzki Date: Tue, 24 Feb 2026 00:01:44 +0100 Subject: [PATCH] Fix MCP server authentication and logging issues - Enable JSON response mode to prevent SSE stream termination by server.close() - Handle GET/DELETE requests before transport creation (return 405 instead of confusing errors) - Remove API key prefix/suffix from error logs (security fix) - Change informational traces from console.error to console.log - Simplify error responses without leaking environment status This fixes Claude Code's "Authenticate" button showing up and "Failed to reconnect" errors. Co-Authored-By: Claude Haiku 4.5 --- package.json | 2 +- src/mcp.ts | 2 +- src/worker.ts | 54 +++++++++++++++++++++++++++++++-------------------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 3716ef9..39f3c68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@supadata/mcp", - "version": "1.2.1", + "version": "1.2.2", "description": "MCP server for Supadata video & web scraping integration. Features include YouTube, TikTok, Instagram, Twitter, and file video transcription, web scraping, batch processing and structured data extraction.", "type": "module", "bin": "./dist/index.js", diff --git a/src/mcp.ts b/src/mcp.ts index 1b29e0e..5603315 100644 --- a/src/mcp.ts +++ b/src/mcp.ts @@ -6,7 +6,7 @@ import { import { z } from 'zod'; async function callSupadata(path: string, args: any, apiKey: string, method: 'GET' | 'POST' = 'GET') { - console.error(`[MCP] Calling Supadata: ${method} ${path}, Key length: ${apiKey?.length ?? 0}`); + console.log(`[MCP] Calling Supadata: ${method} ${path}, Key length: ${apiKey?.length ?? 0}`); let url = `https://api.supadata.ai/v1${path}`; const headers: Record = { diff --git a/src/worker.ts b/src/worker.ts index 2e76485..1f8c3d6 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -6,6 +6,35 @@ import type { Server } from '@modelcontextprotocol/sdk/server/index.js'; export default { async fetch(request: Request, env: { SUPADATA_API_KEY: string }, _ctx: any): Promise { + // Health check — no server/transport needed + const url = new URL(request.url); + if (url.pathname === '/' && request.method === 'GET') { + return new Response('Supadata MCP Worker is running.', { + status: 200, + headers: { 'Content-Type': 'text/plain' }, + }); + } + + // In stateless mode, only POST is supported for JSON-RPC requests. + // GET (SSE streams) and DELETE (session termination) don't work + // because server.close() in the finally block would terminate them. + if (request.method !== 'POST') { + return new Response( + JSON.stringify({ + jsonrpc: '2.0', + error: { + code: -32000, + message: 'Method not allowed. Use POST for JSON-RPC requests.', + }, + id: null, + }), + { + status: 405, + headers: { 'Content-Type': 'application/json', Allow: 'POST' }, + }, + ); + } + let server: Server | null = null; try { @@ -24,13 +53,14 @@ export default { } if (!apiKey) { - console.error('CRITICAL: No API key provided via headers (x-api-key) or environment (SUPADATA_API_KEY).'); + console.warn('No API key provided via headers (x-api-key) or environment (SUPADATA_API_KEY).'); } else { console.log(`Received API Key (length: ${apiKey.length})`); } const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined, + enableJsonResponse: true, }); const result = createMcpServer({ @@ -40,35 +70,17 @@ export default { await server.connect(transport); - if (apiKey) { - const prefix = apiKey.substring(0, 4); - const suffix = apiKey.substring(apiKey.length - 4); - console.error(`Received API Key (length: ${apiKey.length}). Debug: ${prefix}...${suffix}`); - } else { - console.error('CRITICAL: API Key is empty!'); - } - - const url = new URL(request.url); - if (url.pathname === '/' && request.method === 'GET') { - return new Response('Supadata MCP Worker is running. Endpoint: /message or /sse', { - status: 200, - headers: { 'Content-Type': 'text/plain' } - }); - } - const response = await transport.handleRequest(request); return response ?? new Response('Not Found', { status: 404 }); } catch (err: any) { console.error('Worker Error:', err); - const actualEnvKey = env.SUPADATA_API_KEY ? 'Present' : 'Missing'; - const debugMsg = `Server Internal Error: ${err?.message}. Env Status: ${actualEnvKey}`; return new Response( JSON.stringify({ - error: debugMsg, + error: `Server Internal Error: ${err?.message}`, }), - { status: 500 } + { status: 500, headers: { 'Content-Type': 'application/json' } }, ); } finally { if (server) {