1. What's the Issue
Browser-based MCP clients (such as MCP Inspector) fail to receive responses from gopher-mcp servers because HTTP responses lack CORS (Cross-Origin Resource Sharing) headers.
When a browser makes a cross-origin request, it checks the response for CORS headers to determine if the response should be allowed. Without these headers, the browser blocks the response even though the server processed the request successfully.
Error observed:
Access to fetch at 'http://localhost:3001/mcp' from origin 'http://localhost:6274'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
on the requested resource.
This affects all HTTP responses from the server:
- JSON-RPC responses (initialize, tools/list, etc.)
- SSE event streams
2. How to Reproduce
Prerequisites
- Build gopher-mcp with HTTP/SSE transport enabled
- Run an MCP server using HTTP/SSE transport
Steps to Reproduce
-
Start the MCP server:
./mcp_example_server # or any gopher-mcp server with HTTP/SSE transport
-
Send a request from a browser context (or simulate with curl showing headers):
curl -s -i -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Origin: http://localhost:6274" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
-
Expected: Response includes CORS headers:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, Mcp-Session-Id, Mcp-Protocol-Version
...
-
Actual (before fix): Response has no CORS headers:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: ...
Using MCP Inspector
- Open MCP Inspector at http://localhost:6274
- Select "Streamable HTTP" transport
- Enter URL: http://localhost:3001/mcp
- Click Connect
- Before fix: Browser console shows CORS error, responses are blocked
3. How to Fix It
The fix adds CORS headers to all HTTP responses in src/filter/http_codec_filter.cc.
Fix 1: Add CORS Headers to JSON Responses
In the onWrite method, add CORS headers to standard JSON responses:
// In the JSON response section (non-SSE)
response << "Cache-Control: no-cache\r\n";
// CORS headers for browser-based clients (e.g., MCP Inspector)
response << "Access-Control-Allow-Origin: *\r\n";
response << "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n";
response << "Access-Control-Allow-Headers: Content-Type, Authorization, Accept, Mcp-Session-Id, Mcp-Protocol-Version\r\n";
Fix 2: Add CORS Headers to SSE Responses
Add the same CORS headers to Server-Sent Events responses:
// In the SSE response section
response << "Cache-Control: no-cache\r\n";
response << "Connection: keep-alive\r\n";
response << "X-Accel-Buffering: no\r\n"; // Disable proxy buffering
// CORS headers for browser-based clients (e.g., MCP Inspector)
response << "Access-Control-Allow-Origin: *\r\n";
response << "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n";
response << "Access-Control-Allow-Headers: Content-Type, Authorization, Accept, Mcp-Session-Id, Mcp-Protocol-Version\r\n";
Fix 3: Skip Double HTTP Framing
Add a check to pass through responses that are already HTTP-formatted (e.g., from the routing filter):
// Check if data is already HTTP-formatted (from routing filter)
// If so, pass through without adding more HTTP framing
if (body_data.length() >= 5 &&
body_data.compare(0, 5, "HTTP/") == 0) {
GOPHER_LOG_DEBUG(
"HttpCodecFilter::onWrite - data already HTTP formatted, "
"passing through");
return network::FilterStatus::Continue;
}
This prevents double HTTP headers when other filters (like the routing filter for OPTIONS) have already built the HTTP response.
Verification
After the fix:
$ curl -s -i -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Origin: http://localhost:6274" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}'
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 234
Cache-Control: no-cache
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, Mcp-Session-Id, Mcp-Protocol-Version
Connection: keep-alive
{"jsonrpc":"2.0","id":1,"result":{...}}
Browser-based MCP clients can now receive and process responses from the server.
1. What's the Issue
Browser-based MCP clients (such as MCP Inspector) fail to receive responses from gopher-mcp servers because HTTP responses lack CORS (Cross-Origin Resource Sharing) headers.
When a browser makes a cross-origin request, it checks the response for CORS headers to determine if the response should be allowed. Without these headers, the browser blocks the response even though the server processed the request successfully.
Error observed:
This affects all HTTP responses from the server:
2. How to Reproduce
Prerequisites
Steps to Reproduce
Start the MCP server:
./mcp_example_server # or any gopher-mcp server with HTTP/SSE transportSend a request from a browser context (or simulate with curl showing headers):
Expected: Response includes CORS headers:
Actual (before fix): Response has no CORS headers:
Using MCP Inspector
3. How to Fix It
The fix adds CORS headers to all HTTP responses in
src/filter/http_codec_filter.cc.Fix 1: Add CORS Headers to JSON Responses
In the
onWritemethod, add CORS headers to standard JSON responses:Fix 2: Add CORS Headers to SSE Responses
Add the same CORS headers to Server-Sent Events responses:
Fix 3: Skip Double HTTP Framing
Add a check to pass through responses that are already HTTP-formatted (e.g., from the routing filter):
This prevents double HTTP headers when other filters (like the routing filter for OPTIONS) have already built the HTTP response.
Verification
After the fix:
$ curl -s -i -X POST http://localhost:3001/mcp \ -H "Content-Type: application/json" \ -H "Origin: http://localhost:6274" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}' HTTP/1.1 200 OK Content-Type: application/json Content-Length: 234 Cache-Control: no-cache Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization, Accept, Mcp-Session-Id, Mcp-Protocol-Version Connection: keep-alive {"jsonrpc":"2.0","id":1,"result":{...}}Browser-based MCP clients can now receive and process responses from the server.