Describe the bug
We are seeing unstable behavior when symfony/mcp-bundle handles multiple concurrent HTTP MCP requests within the same Mcp-Session-Id.
From our investigation, the underlying cause likely comes from the official PHP MCP SDK session model, where protocol state is stored in a shared session payload and mutated through a read -> modify -> write whole session flow. However, in practice this issue becomes visible at the Symfony bundle integration level, because the bundle exposes HTTP MCP request handling directly and does not serialize same-session concurrent requests.
What we expected:
- multiple concurrent
tools/call requests in the same MCP session should be handled safely.
What actually happens:
- some requests succeed,
- another response can be lost or overwritten,
- the MCP client then reports stale or unknown message IDs,
- and one or more tool calls may hang on the client side.
In our case this happens systematically with Cursor IDE when it sends several read-only MCP calls in parallel after discovery.
To Reproduce
Steps to reproduce the behavior:
- Create a Symfony app using
symfony/mcp-bundle with HTTP transport enabled.
- Configure MCP session storage with a shared store, for example:
mcp:
http:
session:
store: cache
- Expose a few simple read-only MCP tools/resources.
- Connect a client that issues multiple concurrent MCP calls within the same session.
- Trigger 2-3 parallel
tools/call requests for the same Mcp-Session-Id.
- Observe that:
- some calls complete successfully,
- another call may hang,
- the client may log
Received a response for an unknown message ID.
If needed, I can provide a minimal reproducer repository.
Expected behavior
Even if the underlying PHP MCP SDK currently has concurrency limitations, it would be very helpful if symfony/mcp-bundle could mitigate or document this problem more explicitly.
Possible improvements at the bundle level could be:
- serializing HTTP MCP request handling per
Mcp-Session-Id,
- adding an optional lock around the full
server->run($transport) lifecycle,
- documenting that same-session parallel HTTP requests are currently unsafe unless externally serialized.
That would make the bundle much safer for real Symfony deployments.
Logs
Observed client-side symptom in Cursor IDE:
Ignoring stale response (unknown message ID): Received a response for an unknown message ID: {"jsonrpc":"2.0","id":10,"result":{...}}
In our case, several parallel MCP get_record calls were started in the same session. Two completed successfully, while another response appeared only as a stale/unknown-message-id payload on the client side.
Relevant bundle integration points:
The bundle injects the session store into the MCP server builder:
->call('setSession', [service('mcp.session.store')])
When store: cache is used, the bundle wires the SDK's Psr16SessionStore:
$container->register('mcp.session.store', Psr16SessionStore::class)
->setArguments([
new Reference($sessionConfig['cache_pool']),
$sessionConfig['prefix'],
$sessionConfig['ttl'],
]);
The controller processes HTTP MCP requests directly:
public function handle(Request $request): Response
{
$transport = new StreamableHttpTransport(
$this->httpMessageFactory->createRequest($request),
$this->responseFactory,
$this->streamFactory,
logger: $this->logger,
);
$psrResponse = $this->server->run($transport);
$streamed = 'text/event-stream' === $psrResponse->getHeaderLine('Content-Type');
return $this->httpFoundationFactory->createResponse($psrResponse, $streamed);
}
And the underlying SDK mutates outgoing queue state through a session read/modify/write pattern:
$queue = $session->get(self::SESSION_OUTGOING_QUEUE, []);
$queue[] = [
'message' => $encoded,
'context' => $context,
];
$session->set(self::SESSION_OUTGOING_QUEUE, $queue);
Later, the whole session payload is saved:
} finally {
$session->save();
}
This seems consistent with a lost-update race when multiple HTTP requests mutate the same session concurrently.
Additional context
Versions:
symfony/mcp-bundle: v0.6.0
api-platform/mcp: ^4.3
modelcontextprotocol/php-sdk: version installed transitively by the bundle in our project
- Symfony app:
7.4
- Client where we observe the issue: Cursor IDE over HTTP MCP
I understand this may partly belong in the upstream PHP MCP SDK. Still, since symfony/mcp-bundle is the Symfony integration layer and is what application developers configure directly, it seems like a good place either to:
- provide a mitigation,
- expose a safe configuration option,
- or clearly document the limitation.
modelcontextprotocol/php-sdk#275
Describe the bug
We are seeing unstable behavior when
symfony/mcp-bundlehandles multiple concurrent HTTP MCP requests within the sameMcp-Session-Id.From our investigation, the underlying cause likely comes from the official PHP MCP SDK session model, where protocol state is stored in a shared session payload and mutated through a
read -> modify -> write whole sessionflow. However, in practice this issue becomes visible at the Symfony bundle integration level, because the bundle exposes HTTP MCP request handling directly and does not serialize same-session concurrent requests.What we expected:
tools/callrequests in the same MCP session should be handled safely.What actually happens:
In our case this happens systematically with Cursor IDE when it sends several read-only MCP calls in parallel after discovery.
To Reproduce
Steps to reproduce the behavior:
symfony/mcp-bundlewith HTTP transport enabled.tools/callrequests for the sameMcp-Session-Id.Received a response for an unknown message ID.If needed, I can provide a minimal reproducer repository.
Expected behavior
Even if the underlying PHP MCP SDK currently has concurrency limitations, it would be very helpful if
symfony/mcp-bundlecould mitigate or document this problem more explicitly.Possible improvements at the bundle level could be:
Mcp-Session-Id,server->run($transport)lifecycle,That would make the bundle much safer for real Symfony deployments.
Logs
Observed client-side symptom in Cursor IDE:
In our case, several parallel MCP
get_recordcalls were started in the same session. Two completed successfully, while another response appeared only as a stale/unknown-message-id payload on the client side.Relevant bundle integration points:
The bundle injects the session store into the MCP server builder:
When
store: cacheis used, the bundle wires the SDK'sPsr16SessionStore:The controller processes HTTP MCP requests directly:
And the underlying SDK mutates outgoing queue state through a session read/modify/write pattern:
Later, the whole session payload is saved:
This seems consistent with a lost-update race when multiple HTTP requests mutate the same session concurrently.
Additional context
Versions:
symfony/mcp-bundle:v0.6.0api-platform/mcp:^4.3modelcontextprotocol/php-sdk: version installed transitively by the bundle in our project7.4I understand this may partly belong in the upstream PHP MCP SDK. Still, since
symfony/mcp-bundleis the Symfony integration layer and is what application developers configure directly, it seems like a good place either to:modelcontextprotocol/php-sdk#275