google / google/adk-python

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

未关闭
#6,385 3 条评论 0 个 reaction 已指派 1 人 已被 @sanketpatil06 认领 在 GitHub 查看
mcp needs review spam
主要语言
Python
星标
21.5k
派生
4k
平均合并
1 天 14 小时
30 天内合并 PR
37

描述

### 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()`:

```python
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.

贡献指南

打开贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。