Is your feature request related to a problem?
There is no supported way to have McpToolset / MCPSessionManager react to server-initiated MCP notifications — in particular notifications/tools/list_changed.
The underlying mcp SDK ClientSession accepts a message_handler constructor parameter, and invokes it for every incoming message (_handle_incoming → await self._message_handler(req)). But in ADK the ClientSession is constructed internally inside SessionContext._run (google/adk/tools/mcp_tool/session_context.py) with only sampling_callback / sampling_capabilities forwarded — no message_handler. So the SDK falls back to _default_message_handler, which silently discards all server notifications, including ToolListChangedNotification.
Neither MCPSessionManager.__init__, SessionContext.__init__, nor McpToolset.__init__ exposes a message_handler, so there is no public hook.
Observed on google-adk==2.3.0 (with mcp==1.27.2).
Describe the solution you'd like
Forward an optional message_handler: MessageHandlerFnT | None through the same path as sampling_callback:
McpToolset(..., message_handler=...) →
MCPSessionManager(..., message_handler=...) →
SessionContext(..., message_handler=...) →
- passed into the
ClientSession(...) constructor.
This would let toolsets consume tools/list_changed (e.g. to invalidate a cached tool list) and other server notifications through a supported API.
Describe alternatives you've considered
Today the only working hook is to assign the private attribute after create_session():
session = await super().create_session(headers=headers)
target = getattr(session, "_session", session)
if hasattr(target, "_message_handler"):
target._message_handler = my_handler
This works (the SDK reads _message_handler dynamically per message, so post-construction assignment takes effect on the running receive loop), but it depends on a private attribute that any ADK/SDK change could rename or remove. A public parameter would remove the need for this workaround.
Additional context
Use case: a gateway that implements the MCP tools/list_changed capability emits the notification on the standalone GET SSE stream; the client wants to invalidate its cached tools/list result on receipt. The GET stream itself opens fine once the server returns an mcp-session-id; the only missing piece on the ADK side is delivery of the notification to a caller-supplied handler.
Is your feature request related to a problem?
There is no supported way to have
McpToolset/MCPSessionManagerreact to server-initiated MCP notifications — in particularnotifications/tools/list_changed.The underlying
mcpSDKClientSessionaccepts amessage_handlerconstructor parameter, and invokes it for every incoming message (_handle_incoming→await self._message_handler(req)). But in ADK theClientSessionis constructed internally insideSessionContext._run(google/adk/tools/mcp_tool/session_context.py) with onlysampling_callback/sampling_capabilitiesforwarded — nomessage_handler. So the SDK falls back to_default_message_handler, which silently discards all server notifications, includingToolListChangedNotification.Neither
MCPSessionManager.__init__,SessionContext.__init__, norMcpToolset.__init__exposes amessage_handler, so there is no public hook.Observed on
google-adk==2.3.0(withmcp==1.27.2).Describe the solution you'd like
Forward an optional
message_handler: MessageHandlerFnT | Nonethrough the same path assampling_callback:McpToolset(..., message_handler=...)→MCPSessionManager(..., message_handler=...)→SessionContext(..., message_handler=...)→ClientSession(...)constructor.This would let toolsets consume
tools/list_changed(e.g. to invalidate a cached tool list) and other server notifications through a supported API.Describe alternatives you've considered
Today the only working hook is to assign the private attribute after
create_session():This works (the SDK reads
_message_handlerdynamically per message, so post-construction assignment takes effect on the running receive loop), but it depends on a private attribute that any ADK/SDK change could rename or remove. A public parameter would remove the need for this workaround.Additional context
Use case: a gateway that implements the MCP
tools/list_changedcapability emits the notification on the standalone GET SSE stream; the client wants to invalidate its cachedtools/listresult on receipt. The GET stream itself opens fine once the server returns anmcp-session-id; the only missing piece on the ADK side is delivery of the notification to a caller-supplied handler.