anthropics / anthropics/claude-agent-sdk-python

Feature Request: Expose MCP `_meta` field in ToolResultBlock for handler-only data

Offen
#407 0 Kommentare 4 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
enhancement
Vorherrschende Sprache
Python
Sterne
8.1k
Forks
1.3k
Ø Merge
2 T. 31 Min.
Gemergte PRs (30 T.)
1

Beschreibung

## The Ask

Provide a return path from MCP tools to event handlers that **does not enter model context**.

MCP's `_meta` field is designed for exactly this. Exposing it in `ToolResultBlock` would solve it.

## Problem

MCP tools often need to return two types of data:

| Data Type | Destination | Example |
|-----------|-------------|---------|
| **For model** | Model context | "Found 3 matching records" |
| **For UI/handlers** | Event handlers only | Record IDs, metadata, pagination cursors |

Currently, **everything in the tool return enters model context**. There's no way to say "this part is for handlers, not the model."

The only options today:

| Approach | Reaches Handlers | Enters Model Context |
|----------|------------------|----------------------|
| Put in `content` | ✓ | ✓ (unwanted) |
| Put in `_meta` | ✗ (stripped by SDK) | ✗ |
| Build custom callback | ✓ | ✗ |

To avoid polluting model context, we're forced to build out-of-band callback infrastructure for each tool that needs UI-only data.

## What We Have To Do Today

```python
# 1. Define a notifier class
class DataNotifier:
def __init__(self):
self._callback = None
def set_callback(self, cb):
self._callback = cb
def notify(self, data):
if self._callback:
self._callback(data)

# 2. Create module-level singleton
_notifier = DataNotifier()

# 3. Tool fires callback BEFORE return (before SDK processes)
async def my_tool(args):
result = do_work(args)
_notifier.notify(result["ui_data"]) # Side-channel to handler
return {"content": [{"type": "text", "text": "Done"}]}

# 4. Wire callback during app initialization
notifier.set_callback(handler.on_ui_data)

# 5. Handler receives via callback
def on_ui_data(self, data):
self.panel.update(data)
```

This pattern must be repeated for every tool that needs handler-only data.

## Why Not Just Use `content`?

We can put UI data in `content` and parse it in handlers. But:

1. **Token waste** - Model receives data it doesn't need
2. **Context pollution** - Model sees IDs, cursors, metadata meant for UI
3. **Semantic confusion** - Model may try to interpret UI-specific data structures

MCP designed `_meta` specifically for "hidden data for client applications only, not visible to the model."

## Proposed Solution

Expose `_meta` in `ToolResultBlock`:

```python
@dataclass
class ToolResultBlock:
tool_use_id: str
content: str | List[Dict[str, Any]] | None = None
is_error: bool | None = None
meta: Dict[str, Any] | None = None # NEW
```

Tools return:
```python
return {
"content": [{"type": "text", "text": "Recalled 3 observations"}],
"_meta": {"observation_ids": ["obs_1", "obs_2", "obs_3"]}
}
```

Handlers receive:
```python
def on_tool_complete(self, block: ToolResultBlock):
if block.meta:
self.highlight_items(block.meta["observation_ids"])
```

- **Model sees:** `"Recalled 3 observations"`
- **Handler gets:** `{"observation_ids": ["obs_1", "obs_2", "obs_3"]}`

## Use Cases

| Use Case | Model Sees | Handler Gets (via `_meta`) |
|----------|------------|----------------------------|
| Knowledge retrieval | "Found 3 relevant items" | Item IDs for UI highlighting |
| Search | "Results for 'query'" | Facets, pagination cursor, result IDs |
| File operations | "Uploaded file.txt" | Progress %, bytes transferred |
| Database query | Query results | Execution plan, timing stats |
| API calls | Response data | Rate limits, request ID, headers |

## Related

[anthropics/claude-code#9767](https://github.com/anthropics/claude-code/issues/9767) addresses MCP events/notifications not reaching the LLM for multi-turn state. This request is complementary - we need a channel that reaches event handlers but **bypasses** model context, for UI-only data that shouldn't consume tokens.

## Benefit

- Eliminates custom callback machinery for every UI-data use case
- Aligns SDK with MCP specification intent for `_meta`
- Natural pattern for synchronous tool calls
- Backwards compatible (new optional field)

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

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