anthropics / anthropics/claude-agent-sdk-python
Feature Request: Expose MCP `_meta` field in ToolResultBlock for handler-only data
- Ngôn ngữ chính
- Python
- Star
- 8.1k
- Fork
- 1.3k
- Merge trung bình
- 2 ngày 31 phút
- Pull request đã merge (30 ngày)
- 1
Mô tả
## 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)
Hướng dẫn đóng góp
Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này
Đánh giá
Issue này chưa được đánh giá.