Bug Description
When an MCP server (e.g. on Cloud Run) scales to zero and back, the server's in-memory sessions are lost. The agent's @retry_on_errors decorator retries the failed call, but MCPSessionManager.create_session() returns the same cached dead session because _is_session_disconnected() only checks transport-level stream closure - the transport is still alive, only the server-side session is gone.
This means every retry reuses the stale session and fails again, until all retries are exhausted.
Steps to Reproduce
- Deploy an MCP server on Cloud Run (or any auto-scaling platform) with scale-to-zero enabled
- Connect an ADK agent using
McpToolset with a Streamable HTTP connection
- Make a successful tool call (session is created and cached)
- Wait for the MCP server to scale to zero (~15 minutes idle)
- Make another tool call - the server scales back up with fresh state
Expected Behavior
The agent should detect the server-side session loss and create a fresh session on retry.
Actual Behavior
@retry_on_errors catches the error and retries
create_session() finds the cached session
_is_session_disconnected() returns False (transport streams are still open)
- The same dead session is reused → retry fails with the same error
- All retries exhausted → permanent failure
Root Cause
_is_session_disconnected() only checks _read_stream._closed and _write_stream._closed. In a scale-to-zero scenario, the HTTP transport is fine - the server responds normally with
a 404/error for the unknown session ID. The check needs a way to detect **servtion, not just transport failure.
The existing _MCP_GRACEFUL_ERROR_HANDLING flag handles the case where the asyncio task dies, but not this scenario where the task is alive and the server responds normally.
Environment
- ADK version: 2.7.1
- MCP server: Cloud Run with Streamable HTTP transport
- Python: 3.12+
Suggested Fix
Add an invalidate_session() method to MCPSessionManager that marks a session key as stale. Have _execute_with_session() call it on any exception before re-raising, so the next @retry_on_errors attempt builds a fresh session. PR incoming.
Bug Description
When an MCP server (e.g. on Cloud Run) scales to zero and back, the server's in-memory sessions are lost. The agent's
@retry_on_errorsdecorator retries the failed call, butMCPSessionManager.create_session()returns the same cached dead session because_is_session_disconnected()only checks transport-level stream closure - the transport is still alive, only the server-side session is gone.This means every retry reuses the stale session and fails again, until all retries are exhausted.
Steps to Reproduce
McpToolsetwith a Streamable HTTP connectionExpected Behavior
The agent should detect the server-side session loss and create a fresh session on retry.
Actual Behavior
@retry_on_errorscatches the error and retriescreate_session()finds the cached session_is_session_disconnected()returnsFalse(transport streams are still open)Root Cause
_is_session_disconnected()only checks_read_stream._closedand_write_stream._closed. In a scale-to-zero scenario, the HTTP transport is fine - the server responds normally witha 404/error for the unknown session ID. The check needs a way to detect **servtion, not just transport failure.
The existing
_MCP_GRACEFUL_ERROR_HANDLINGflag handles the case where the asyncio task dies, but not this scenario where the task is alive and the server responds normally.Environment
Suggested Fix
Add an
invalidate_session()method toMCPSessionManagerthat marks a session key as stale. Have_execute_with_session()call it on any exception before re-raising, so the next@retry_on_errorsattempt builds a fresh session. PR incoming.