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 件 担当者 1 名 @sanketpatil06 が担当を希望しています GitHub で見る
mcp needs review spam
主要言語
Python
スター
21.5k
フォーク
4k
平均マージ
1日 14時間
マージ済み PR(30日)
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 を短くまとめたダイジェスト。