Troubleshooting

HTTP transport issues

Diagnose SSE disconnects, missing session IDs, port conflicts, and 404s from the HTTP transport.

Last updated

HTTP transport issues

When the HTTP + SSE transport misbehaves, the cause is usually one of four things.

404 Not Found on every request

The server only responds on /mcp. Any other path returns 404.

  • Wrong path. Double-check the URL. http://host:9090/mcp — not /mcp/tools/call, not /api/mcp, not /.
  • Wrong host or port. MCP_HTTP_PORT=9090 is the default; confirm with lsof -i :9090 or ss -lnt.

Port already in use

Error: listen EADDRINUSE: address already in use 0.0.0.0:9090

Another process is bound to :9090. Either stop it or pick a different port:

MCP_HTTP_PORT=9091 MCP_TRANSPORT=http node apps/mcp/dist/index.js

Session ID is missing or stale

The transport is session-aware. Every request after the first initialize must include the mcp-session-id header returned on that initialize. Common failures:

  • The header is not echoed. Each subsequent request starts a fresh session, which works but loses tool capability context and forces a re-initialize.
  • The session expired. If the transport closed (network blip, TCP reset, explicit DELETE /mcp), the server drops the session. A stale ID is ignored; the next request mints a new session.
  • Header name is wrong. It's mcp-session-id, lowercase, hyphens. Most HTTP clients normalize header casing, but some (notably Windows PowerShell) don't.

See Transports → Sessions for the full contract.

SSE connection closes immediately

A GET /mcp stream should stay open indefinitely. If it closes in under a second:

  • Proxy buffering. If the server sits behind a reverse proxy, confirm SSE is passed through without buffering (proxy_buffering off in nginx, disable_chunked_encoding=true in some CDN configs).
  • Client timeout. Many HTTP clients default to a short read timeout. Set a long or unlimited timeout for SSE reads (curl -N, axios.timeout=0).
  • The server crashed. The Plainwork MCP server writes fatal errors to stderr. Run it in the foreground and watch for [plainwork-mcp] fatal: … lines.

Starting works, requests hang forever

The HTTP transport does not kill partial requests — if your client sends a POST with a never-ending body, the server waits. Symptoms:

  • Client issues POST /mcp, server never responds.
  • Content-Length is omitted and the request body stream is not closed.

Always send Content-Length with a complete body, or use Transfer- Encoding: chunked and close the request stream.

Docker-specific

  • docker run without -p 9090:9090 — the server is listening inside the container but the port is not published. Add the flag.
  • Host-network mode on Linux--network=host is an alternative to -p, but means every container using that mode shares the port namespace. Prefer -p unless you have a reason not to.