anthropics / anthropics/claude-agent-sdk-python

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

未關閉
#407 0 則留言 4 個 reaction 已指派 0 人 在 GitHub 檢視
enhancement
主要語言
Python
星號
8.1k
分支
1.3k
平均合併
2 天 31 分鐘
30 天內合併 PR
1

描述

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

貢獻指南

這個儲存庫沒有索引到貢獻指南

評估

這個 Issue 還沒有評估資料。

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。