Is your feature request related to a specific problem?
Yes. We are running ADK Python with MCP toolsets in long-running service processes, and we need a supported way to bound or evict entries from the MCP client-session pool.
Today, MCPSessionManager pools MCP client sessions by a key derived from the merged MCP connection headers. For SSE / Streamable HTTP connections, those merged headers can include:
- static connection headers,
- headers returned by
McpToolset.header_provider,
- auth headers such as
Authorization.
This makes sense for isolation, because different auth/tenant headers may represent different access contexts. However, in production systems these headers can be high-cardinality:
- per-user OAuth bearer tokens,
- short-lived / rotated access tokens,
- service-account impersonation tokens,
- tenant or company headers,
- dynamic auth scopes,
- request/session/correlation headers accidentally or intentionally propagated through
header_provider.
Because healthy MCP sessions appear to remain in the pool until they are detected as disconnected/different-loop or until the whole McpToolset / MCPSessionManager is closed, a long-running process can accumulate many MCP client sessions over time.
In our case, this became acute when we forwarded a conversation/session id to an MCP server as a dynamic header. Since it participated in the ADK MCP session key, each new request created a distinct pooled MCP session. The pool grew under sustained traffic and contributed to production OOMs.
We recognize that propagating high-cardinality correlation metadata as a connection/session header is not ideal. However, the broader issue remains even for legitimate auth headers like Authorization, because token values can also be high-cardinality or short-lived in real deployments.
Currently, the only targeted workaround we found requires using private ADK internals such as:
_mcp_session_manager
_merge_headers
_generate_session_key
_sessions
_session_lock
_cleanup_session
That is fragile across ADK releases.
Describe the Solution You'd Like
We would like ADK to expose a public, supported MCP session-pool lifecycle API or policy so long-running services can prevent unbounded MCP client-session growth.
Any of the following would help:
- Idle TTL eviction
Allow MCP client sessions to be evicted after they have not been used for some configured duration.
Example concept:
McpToolset(
connection_params=StreamableHTTPConnectionParams(...),
session_pool_policy=McpSessionPoolPolicy(
idle_ttl_seconds=900,
),
)
2. **Max pool size / LRU eviction**
Allow a maximum number of pooled MCP sessions per MCPSessionManager, with least-recently-used or similar eviction.
```McpToolset(
connection_params=...,
session_pool_policy=McpSessionPoolPolicy(
max_sessions=100,
eviction_policy="lru",
),
)
- Public per-session close / eviction
Expose a supported API to close the MCP session corresponding to a given effective header set.
await mcp_toolset.close_session(headers={...})
or
await mcp_session_manager.close_session(headers={...})
- Per-tool-call metadata provider
ADK already propagates trace context through MCP tool-call _meta rather than through session headers. It would be useful to expose a supported hook for application metadata that should be sent per tool call but should not participate in MCP session-pool keying.
Impact on your work
This impacts production stability for long-running ADK-based services using MCP toolsets.
Our services use ADK agents with MCP servers for operational workflows. We need MCP calls to carry auth/tenant context and sometimes correlation context, but we cannot allow MCP client sessions to accumulate indefinitely in memory.
Without a supported eviction or pool policy, we have to choose between:
- risking unbounded memory growth in long-running services or relying on private ADK internals to evict sessions.
We have already seen this pattern contribute to OOMs in production when a high-cardinality dynamic header was included in MCP session headers. We have a local workaround, but it is intentionally defensive and fragile because it depends on private implementation details.
A supported ADK API would let us implement this safely and reduce operational risk.
Timeline-wise, this is important for production hardening. We can keep our workaround temporarily, but we would prefer to move to an upstream-supported API as soon as practical.
Willingness to contribute
Yes, but some guidance would be greatly appreciated. We would especially appreciate guidance on whether the preferred direction is:
- TTL / max-size pool policy,
- public per-session eviction,
- pool_sessions=False,
- session-key header policy,
- per-call metadata provider,
- or some combination of the above.
Describe Alternatives You've Considered
Local sidecar eviction using private internals
We implemented a local workaround that:
- records MCP session keys when our header provider runs,
- tracks last-used timestamps,
- captures sessions created by requests,
- evicts captured ephemeral sessions on stream completion,
- runs an idle TTL sweeper as a backstop.
However, this requires private ADK internals which makes it fragile.
Is your feature request related to a specific problem?
Yes. We are running ADK Python with MCP toolsets in long-running service processes, and we need a supported way to bound or evict entries from the MCP client-session pool.
Today,
MCPSessionManagerpools MCP client sessions by a key derived from the merged MCP connection headers. For SSE / Streamable HTTP connections, those merged headers can include:McpToolset.header_provider,Authorization.This makes sense for isolation, because different auth/tenant headers may represent different access contexts. However, in production systems these headers can be high-cardinality:
header_provider.Because healthy MCP sessions appear to remain in the pool until they are detected as disconnected/different-loop or until the whole
McpToolset/MCPSessionManageris closed, a long-running process can accumulate many MCP client sessions over time.In our case, this became acute when we forwarded a conversation/session id to an MCP server as a dynamic header. Since it participated in the ADK MCP session key, each new request created a distinct pooled MCP session. The pool grew under sustained traffic and contributed to production OOMs.
We recognize that propagating high-cardinality correlation metadata as a connection/session header is not ideal. However, the broader issue remains even for legitimate auth headers like
Authorization, because token values can also be high-cardinality or short-lived in real deployments.Currently, the only targeted workaround we found requires using private ADK internals such as:
_mcp_session_manager_merge_headers_generate_session_key_sessions_session_lock_cleanup_sessionThat is fragile across ADK releases.
Describe the Solution You'd Like
We would like ADK to expose a public, supported MCP session-pool lifecycle API or policy so long-running services can prevent unbounded MCP client-session growth.
Any of the following would help:
Allow MCP client sessions to be evicted after they have not been used for some configured duration.
Example concept:
Expose a supported API to close the MCP session corresponding to a given effective header set.
or
ADK already propagates trace context through MCP tool-call _meta rather than through session headers. It would be useful to expose a supported hook for application metadata that should be sent per tool call but should not participate in MCP session-pool keying.
Impact on your work
This impacts production stability for long-running ADK-based services using MCP toolsets.
Our services use ADK agents with MCP servers for operational workflows. We need MCP calls to carry auth/tenant context and sometimes correlation context, but we cannot allow MCP client sessions to accumulate indefinitely in memory.
Without a supported eviction or pool policy, we have to choose between:
We have already seen this pattern contribute to OOMs in production when a high-cardinality dynamic header was included in MCP session headers. We have a local workaround, but it is intentionally defensive and fragile because it depends on private implementation details.
A supported ADK API would let us implement this safely and reduce operational risk.
Timeline-wise, this is important for production hardening. We can keep our workaround temporarily, but we would prefer to move to an upstream-supported API as soon as practical.
Willingness to contribute
Yes, but some guidance would be greatly appreciated. We would especially appreciate guidance on whether the preferred direction is:
Describe Alternatives You've Considered
Local sidecar eviction using private internals
We implemented a local workaround that:
However, this requires private ADK internals which makes it fragile.