Skip to content

Expose a public message_handler seam for MCP ClientSession (consume server notifications e.g. tools/list_changed) #6385

Description

@JesusCaseroDiazCano

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_incomingawait 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.

Metadata

Metadata

Assignees

Labels

mcp[Component] This issues is related to MCP supportneeds review[Status] The PR/issue is awaiting review from the maintainerspam[Status] Issues suspected of having comments which are spam

Fields

No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions