Skip to content

MCP fixes

MCP: SSE timeout or unexpected disconnect

Why long-lived MCP SSE connections drop after a minute or two, when to switch to Streamable HTTP, and the proxy, keep-alive, and Copilot Studio settings that fix it.

The symptom

The MCP server connects fine and answers the first request. After a minute or two of idle, the next request times out or returns a connection error. The client logs SSE connection closed, stream ended, a generic ECONNRESET, or UpstreamConnectFailure.

In Copilot Studio specifically, the failure looks like the tool returning "Could not reach the MCP server" after the first successful invocation. In VS Code Copilot Chat, the bottom-right status flips back to "Disconnected" between agent steps. In Claude Desktop, the tools disappear from the hammer menu until you toggle the connector off and on.

Why this happens

SSE keeps a single HTTP response open and trickles bytes through it. That works locally but fights every layer of a normal cloud stack: load balancers, reverse proxies, CDNs, and serverless gateways all assume an idle response is a hung response and close the socket after their own timeout (often 30 to 120 seconds). The MCP client then sees the close as a transport error.

Streamable HTTP, the transport in the current MCP spec, opens a stream only when the server has data to push and closes it cleanly when the chunk is delivered. It looks like a normal HTTP request to every proxy, so the timeout problem mostly goes away.

Fix 1: move to Streamable HTTP

The current MCP spec defines Streamable HTTP as the HTTP-based transport. The older HTTP+SSE transport is kept for backwards compatibility but is marked deprecated. Streamable HTTP opens streams on demand and survives the network layer better. Most servers (including Hjarni at https://hjarni.com/mcp) accept it at the same URL.

If your client lets you pick a transport explicitly (VS Code's "type": "http", Goose's type: streamable_http, Cline's transportType: "streamableHttp"), pick Streamable HTTP. Most timeout problems disappear there.

Microsoft Copilot Studio only supports the Streamable transport for MCP and dropped SSE after August 2025. If you add the server through the MCP onboarding wizard (Tools, then Add a tool, then New tool, then Model Context Protocol), it speaks Streamable HTTP for you. An older Power Apps custom connector whose OpenAPI schema still declares SSE will not work: recreate it on the current path before chasing proxy settings.

Fix 2: turn off proxy buffering

Reverse proxies between the client and the MCP server can buffer responses. SSE depends on bytes flowing out as soon as they exist. Any buffering breaks the stream.

nginx:

location /mcp {
  proxy_pass http://upstream;
  proxy_buffering off;
  proxy_set_header X-Accel-Buffering "no";
  proxy_read_timeout 3600s;
  proxy_send_timeout 3600s;
}

Cloudflare:

Cloudflare proxies SSE natively, so the orange cloud is not the problem. The thing that bites is the proxy read timeout (100 seconds by default on Free/Pro/Business, configurable up to 6000 seconds on Enterprise). If your stream needs to stay idle longer, raise the timeout, switch to Streamable HTTP, or front the endpoint through a Cloudflare Tunnel.

Azure Front Door / Application Gateway (Copilot Studio):

Front Door defaults the response timeout to 60 seconds. Raise sendReceiveTimeoutInSeconds on the routing rule, or move the MCP server behind a setup that accepts long responses (App Service with WebSockets on, Container Apps with the HTTP scaler). Application Gateway has a similar RequestTimeout on the backend HTTP settings.

Fix 3: raise idle timeouts on every hop

Every layer between the client and the server has its own idle timeout. The connection drops at the tightest one.

  • AWS ALB: default 60s. Raise via the load balancer attribute idle_timeout.timeout_seconds.
  • GCP Load Balancer: default 30s for backend service. Raise timeoutSec.
  • Azure Front Door: default 60s response timeout. Raise sendReceiveTimeoutInSeconds on the routing rule.
  • Cloudflare: proxy read timeout defaults to 100s and is configurable on Enterprise. Switch transports if long-lived SSE drops are a recurring issue.
  • Vercel / Netlify Edge: serverless functions cap streaming responses at 25s (Hobby) or 300s (Pro). Move the MCP route to a long-running runtime, or switch to Streamable HTTP.
  • Puma/Unicorn: default 60s worker timeout. Raise worker_timeout for the MCP route.

Fix 4: send a keep-alive heartbeat

If you cannot raise every timeout in the chain, push bytes through the connection regularly so intermediaries see it as active. The SSE spec lets you send a comment line, which clients ignore but proxies count as traffic. Emit one every 15 seconds.

# Rails example (Rack hijack or ActionController::Live)
stream.write(": keep-alive\n\n")

15 seconds is well under every default timeout in the previous section, and the payload is two bytes. Set a higher cadence if you are also hitting a frame-rate cap (some CDNs treat very chatty streams as a DDoS pattern).

Fix 5: confirm the client retries

Some MCP clients reconnect transparently on drop. Others give up after one error. In that case, restart the app or remove and re-add the server. Check the client's logs for retry behaviour. If retries are absent, that is your workaround until the server-side timeout is fixed.

If Copilot Studio surfaces a dropped stream as a tool failure rather than retrying it, a single mid-conversation drop becomes visible to the user. Get the server-side fix in place rather than leaning on client retries.

Per-client checklist

Microsoft Copilot Studio

Copilot Studio only speaks the Streamable transport now (SSE was dropped after August 2025), so add the server through the MCP onboarding wizard and check the Azure Front Door / Application Gateway timeouts in front of it. A custom connector still declaring SSE in its OpenAPI schema needs recreating. See how to connect Copilot Studio to an MCP server for the full setup, or how to connect GitHub Copilot if you mean Copilot in VS Code.

GitHub Copilot in VS Code

Use "type": "http" in .vscode/mcp.json to pin Streamable HTTP. The legacy "type": "sse" is what trips most of the long-idle disconnects. Hjarni for GitHub Copilot has the exact mcp.json shape.

Claude Desktop / Claude.ai

Connectors auto-reconnect, but if the tools vanish between turns, remove the connector and add it again to force a fresh OAuth handshake.

ChatGPT (developer mode)

Disable and re-enable the app for the current chat. ChatGPT's connector layer caches the SSE handle aggressively; toggling the app is faster than waiting for it to renegotiate.

Cursor

Cursor's MCP client is sensitive to gzip-encoded SSE streams. If your server compresses responses, exclude text/event-stream from the compression filter.

Verifying the fix

From the same network the client uses, run:

curl -N -H "Accept: text/event-stream" https://your-server/mcp

Leave it open. If the stream stays alive for more than two minutes and prints periodic keep-alive lines, the server-side fix is in. If curl exits at exactly 60, 100, or 120 seconds, that is the timeout you still need to raise.