My MCP Server Dropped One Call in Four

An MCP session lives in the memory of one process. Apify Standby spreads requests across several runs. Those two facts together silently killed 2 of every 9 tool calls, and every health check stayed green.

For two weeks our MCP server answered roughly three out of four tool calls. The rest came back with Session not found. Every health check was green, the actor never crashed, no log line said anything was wrong, and the first hint came from a client that retried and got a different answer.

The cause is a two-line summary that took far longer than two lines to find: an MCP session lives in the memory of one process, and the platform was spreading requests across several processes.

The setup

The server is a FastMCP app exposing 19 data tools. It runs on Apify in Standby mode, which means the platform keeps the container warm and routes incoming HTTP requests to it, starting and stopping runs as load requires. From the outside it looks like one endpoint. Inside, it is a pool.

Streamable HTTP in the MCP spec is session-based by default. The client calls initialize, the server hands back an Mcp-Session-Id, and every following request carries that header. The server keeps the session state in a dictionary in memory.

You can already see it. Run A creates the session. The next request lands on run B. Run B has never heard of that session id and correctly answers Session not found.

Why nothing caught it

This is the part worth writing down, because the failure mode is designed to be invisible.

The health check called /health, which is a plain stateless endpoint. It always passed. It would have passed if every single tool call were failing.

The failures were not errors in any sense the platform recognises. The server returned a valid JSON-RPC error response with HTTP 200. From the platform's point of view the actor was answering requests correctly and quickly. Run status: SUCCEEDED. No exception, no exit code, nothing to alert on.

And it was intermittent by nature. With a small pool, a good fraction of requests happen to land back on the run that owns the session. When we finally measured it properly on 4 August 2026, 7 of 9 calls succeeded. A casual test passes. A demo passes. An agent doing twenty calls in a row does not.

The one signal that existed was on the client side, and MCP clients are quiet about this. They retry, or they drop the tool and answer from the model's own memory, which looks to a user like the agent simply chose not to use your tool.

The fix

Stop keeping session state in memory. FastMCP supports stateless operation, where every request is self-contained: the request carries what the server needs, nothing is stored between calls, and it no longer matters which run answers.

That is the whole change. Since build 0.0.29 the server runs stateless, and the same measurement afterwards gave 40 of 40 calls succeeding across both endpoints, the direct actor URL and the gateway.

The cost is real but small. Stateless means no server-side session context, so anything that would have lived in the session has to travel with the request or be looked up from durable storage. For a tool server that fetches and returns data, there was nothing to lose. For a server that maintains long-lived per-client state, this trade is not free and you need actual shared storage rather than process memory.

What we changed besides the code

Three things, and the second one is the one that generalises.

The health check now exercises a real tool call, not just /health. A liveness probe that only tests the endpoint most likely to work is a probe that tells you nothing.

We treat process memory on Standby as a bug by default. The rule we wrote into the code comments: nothing survives between requests unless it is in shared storage. Standby distributes requests across runs, so any in-memory cache, counter or session is a coin flip. This applies to rate-limit counters and usage ledgers exactly as much as to MCP sessions, and those fail even more quietly.

We measure a fixed number of calls after every deploy of the MCP server. Not one call. One call has a 78 percent chance of passing while a quarter of your traffic is on fire.

If you are running an MCP server behind any autoscaling layer

Ask one question: where does the session live? If the answer is "in the process", and more than one process can receive a request, you already have this bug. You may not have noticed, because the failure produces a valid HTTP 200, a SUCCEEDED run status, and a client that quietly stops using your tools.

The server described here is public and free to connect to. It exposes 19 tools over SEC EDGAR, openFDA, ClinicalTrials.gov, USAspending.gov, House Clerk, NIH RePORTER and a few others, and it runs stateless for the reason above. Details are on the MCP server page.


Want the signal instead of the raw filings? Get a free report preview. Prefer the tool to the write-up? Browse all data feeds or connect the free MCP server.