MCP Streamable HTTP: Cosmo Router rejects requests without Content-Type (400 Bad Request), though MCP spec does not require it
Component(s)
router
Component version
0.250.0
wgc version
0.97.0
controlplane version
0.250.0
router version
0.250.0
What happened?
When connecting to Cosmo Router via MCP Streamable HTTP (spec 2025-06-18), POST requests without a Content-Type header result in 400 Bad Request or silent failure (only ping events, no endpoint).
According to the MCP specification, Content-Type is not required for Streamable HTTP transport. Clients such as langchain4j and others currently omit this header by default. This makes Cosmo Router stricter than the MCP spec, and causes interoperability issues.
Steps to Reproduce
- Request without Content-Type
curl -v --http1.1 -N -X POST http://127.0.0.1:3003/mcp \
-H "Accept: application/json,text/event-stream" \
-H "Mcp-Protocol-Version: 2025-06-18" \
-H "Mcp-Session-Id: test-no-ct" \
--data-binary '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}'
- Result
- Router responds with 400 Bad Request, or only sends keepalive ping events.
- No endpoint event is received.
- Same request with Content-Type works
curl -v --http1.1 -N -X POST http://127.0.0.1:3003/mcp \
-H "Content-Type: application/json+stream" \
-H "Accept: application/json,text/event-stream" \
-H "Mcp-Protocol-Version: 2025-06-18" \
-H "Mcp-Session-Id: test-with-ct" \
--data-binary '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}'
This request succeeds: endpoint is emitted and the session initializes.
Expected Behavior
Per MCP spec (2025-06-18), Content-Type is not mandatory. Cosmo Router should accept requests without this header and correctly parse JSON-RPC bodies.
Environment information
• Cosmo Router: v0.250.0 (and likely newer) • MCP protocol: 2025-06-18 Streamable HTTP • OS: macOS • Client: curl / langchain4j
Router configuration
# yaml-language-server: $schema=../../router/pkg/config/config.schema.json
dev_mode: true
version: "1"
listen_addr: 0.0.0.0:3002
execution_config:
file:
watch: true
path: router.json
headers:
all:
request:
- op: "propagate"
named: Authorization
mcp:
enabled: true
server:
listen_addr: "0.0.0.0:3003"
base_url: "http://localhost:3003/mcp" # Optional: Advertised MCP base URL for SSE clients
graph_name: "my-graph"
exclude_mutations: true
enable_arbitrary_operations: true
expose_schema: true
storage:
provider_id: "mcp" # References a file_system provider defined below
# Configure storage providers
storage_providers:
file_system:
- id: "mcp"
path: "/app/operations" # Relative to the router binary
access_logs:
enabled: true
output:
stdout:
enabled: true
Router execution config
Log output
14:43:44 PM INFO cmd/main.go:220 Config file watching is disabled, you can still trigger reloads by sending SIGHUP to the router process {"hostname": "ebcf0c9d017c", "pid": 1, "service": "@wundergraph/router", "service_version": "0.250.0"}
14:43:44 PM INFO cmd/main.go:115 Config file provided. Values in the config file have higher priority than environment variables {"hostname": "ebcf0c9d017c", "pid": 1, "service": "@wundergraph/router", "service_version": "0.250.0", "config_file": ["config.yaml"]}
14:43:44 PM WARN core/router.go:463 No graph token provided. The following Cosmo Cloud features are disabled. Not recommended for Production. {"hostname": "ebcf0c9d017c", "pid": 1, "service": "@wundergraph/router", "service_version": "0.250.0", "features": ["Schema Usage Tracking", "Persistent operations", "Cosmo Cloud Tracing", "Cosmo Cloud Metrics"]}
14:43:44 PM WARN core/router.go:520 Development mode enabled. This should only be used for testing purposes {"hostname": "ebcf0c9d017c", "pid": 1, "service": "@wundergraph/router", "service_version": "0.250.0"}
14:43:44 PM INFO metric/prometheus_server.go:63 Prometheus metrics enabled {"hostname": "ebcf0c9d017c", "pid": 1, "service": "@wundergraph/router", "service_version": "0.250.0", "listen_addr": "127.0.0.1:8088", "endpoint": "/metrics"}
14:43:44 PM INFO mcpserver/server.go:328 MCP server started {"hostname": "ebcf0c9d017c", "pid": 1, "service": "@wundergraph/router", "service_version": "0.250.0", "storage_provider_id": "mcp", "listen_addr": "0.0.0.0:3003", "path": "/mcp", "operations_dir": "/app/operations", "graph_name": "my-graph", "exclude_mutations": true, "enable_arbitrary_operations": true, "expose_schema": true}
14:43:44 PM INFO core/router.go:930 Serving GraphQL playground {"hostname": "ebcf0c9d017c", "pid": 1, "service": "@wundergraph/router", "service_version": "0.250.0", "url": "http://0.0.0.0:3002/"}
14:43:44 PM INFO core/router.go:1237 Watching config file for changes. Router will hot-reload automatically without downtime {"hostname": "ebcf0c9d017c", "pid": 1, "service": "@wundergraph/router", "service_version": "0.250.0", "path": "router.json"}
14:43:44 PM INFO core/router.go:1181 Server initialized and ready to serve requests {"hostname": "ebcf0c9d017c", "pid": 1, "service": "@wundergraph/router", "service_version": "0.250.0", "listen_addr": "0.0.0.0:3002", "playground": true, "introspection": true, "config_version": "e8760031-a564-4614-ac6b-1b07dbb38078"}
14:43:44 PM INFO core/supervisor.go:152 Router started {"hostname": "ebcf0c9d017c", "pid": 1, "service": "@wundergraph/router", "service_version": "0.250.0", "component": "supervisor"}
Additional context
No response
WunderGraph commits fully to Open Source and we want to make sure that we can help you as fast as possible. The roadmap is driven by our customers and we have to prioritize issues that are important to them. You can influence the priority by becoming a customer. Please contact us here.
Hi @yurikotikov Thanks for raising an issue. We will have a look at it.
I took a look at the spec and in fact that there is no explicit requirement for clients to include the Content-Type header.
However it seems like the Content-Type header has raised a few discussions in different projects. Same goes for the library which we are using (https://github.com/mark3labs/mcp-go).
For mcp-go Content-Type must be application/json https://github.com/mark3labs/mcp-go/blob/dd0058cb6537825c7479a9e00884de998903d861/server/streamable_http.go#L244-L247
I also took a brief look at langchain4j. To me it looks like the client includes the header in the transport:
https://github.com/langchain4j/langchain4j/commit/76c2f505685b832fb072ceb4b96e9808dab931a3#diff-0fc89a9ff441d5fd061b55b508312ecc5bf7a33365d290ad0d0f5517b6429dbaR193-R196
https://github.com/langchain4j/langchain4j/commit/46dcc007de38a3b130782b2b3d5e19e89e8e4971#diff-45d2233b3ad4429abdc71dc5ab2710bd981720fe86c9f5b6d5b541d7d77cf78dR66-R82