google / google/adk-python

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

Offen
#6,385 3 Kommentare 0 Reaktionen 1 zugewiesene Person Beansprucht von @sanketpatil06 Auf GitHub ansehen
mcp needs review spam
Vorherrschende Sprache
Python
Sterne
21.5k
Forks
4k
Ø Merge
1 T. 14 Std.
Gemergte PRs (30 T.)
37

Beschreibung

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

Beitragsleitfaden

Beitragsleitfaden öffnen

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.