ag-ui-protocol / ag-ui-protocol/ag-ui

ToolCallResultEvent loses MCP `structuredContent` — needs an optional structured field

Đang mở
#1,593 3 bình luận 0 reaction 0 người được giao Xem trên GitHub
enhancement proposal
Ngôn ngữ chính
Python
Star
15.9k
Fork
1.4k
Merge trung bình
1 ngày 17 giờ
Pull request đã merge (30 ngày)
163

Mô tả

## Summary

`ToolCallResultEvent` defines `content: str` and has no field for structured tool output. MCP servers routinely return `CallToolResult` with both:

- `content` — text/blocks for the model
- `structuredContent` — a structured object for the client (MCP Apps UI trees, validated payloads, etc.)

The structured field has nowhere to go in AG-UI today, so it's dropped at the protocol boundary even when the integration layer (LangChain, Agno, Mastra, custom servers) preserves it.

This is closely related to #918 — that issue surfaced the symptom (AG-UI choking on a list-typed `content` value coming from LangChain's MCP adapter) and was closed with a stringification workaround. The underlying design point — that AG-UI's tool-result event has no place to put `structuredContent` — was never resolved. Quoting the original reporter's reply:

> Not sure this should be closed? My solution was a workaround for an underlying issue.

## Why it matters

The most visible casualty is the **MCP Apps extension**: an MCP Apps tool returns a UI tree in `structuredContent`, the host renderer needs that tree to render the iframe contents. If AG-UI is in the pipeline, the tree is gone by the time the host gets the tool result, so the renderer either:

- can't render at all, or
- has to re-invoke the tool out-of-band to recover the data we already had (current workaround in our project).

This applies to any AG-UI integration — LangChain, Agno, Mastra, anything backed by an MCP server.

## Suggested change

Add an optional structured field to `ToolCallResultEvent`:

```python
class ToolCallResultEvent(BaseEvent):
type: Literal[EventType.TOOL_CALL_RESULT] = EventType.TOOL_CALL_RESULT
message_id: str
tool_call_id: str
content: str # text for the model — unchanged
structured_content: Optional[Any] = None # NEW: arbitrary JSON for the host
role: Optional[Literal["tool"]] = None
```

Backward compatible (default `None`). Hosts that don't care can ignore it; MCP-Apps hosts can populate iframes from it directly.

If `Any` is too loose, `Optional[Dict[str, Any]]` matches MCP's spec for `structuredContent`.

## Reproduction

1. Stand up a FastMCP server with an `app=True` tool that returns a Prefab `PrefabApp` (yields `structuredContent` containing the UI JSON tree).
2. Drive it through any AG-UI integration (Agno, LangChain, etc.).
3. On the host side, observe that the `ToolCallResultEvent` `content` is the textual placeholder ("[Rendered Prefab UI]") and there is no field carrying the actual tree.
4. The iframe sits at "Waiting for content…" because `structuredContent` is empty.

## Workaround we're using

We re-invoke the MCP tool ourselves from the AG-UI event emitter, capture `structuredContent`, and stuff it into our own activity event. Cost: a duplicate sandbox/network round-trip per UI-rendering tool call.

## References

- #918 — same root cause, closed prematurely with a stringification workaround
- MCP spec — Tools & Structured Content: https://spec.modelcontextprotocol.io/specification/2025-03-26/server/tools/
- Related upstream-of-AG-UI bug in Agno: agno-agi/agno#7686

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.