Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions examples/mcp/structured-content-preview/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Manual demo for structuredContent preview behavior.

Run from this directory:
uv run example.py
"""

from __future__ import annotations

import asyncio

from fast_agent import FastAgent

fast = FastAgent("Structured content preview demo")


@fast.agent(
instruction=(
"Use the passthrough model to invoke the requested tool exactly as provided."
),
servers=["structured_preview"],
)
async def main() -> None:
async with fast.run() as agent:
print("\n=== matching text + structuredContent ===")
matching = await agent.send("***CALL_TOOL structured_content_match {}")
print(matching)

print("\n=== mismatched text + structuredContent ===")
mismatched = await agent.send("***CALL_TOOL structured_content_mismatch {}")
print(mismatched)


if __name__ == "__main__":
asyncio.run(main())
13 changes: 13 additions & 0 deletions examples/mcp/structured-content-preview/fastagent.config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
default_model: "passthrough"

logger:
level: "error"
type: "console"
show_chat: false
show_tools: true
truncate_tools: false

mcp:
targets:
- name: structured_preview
target: "uv run preview_server.py"
75 changes: 75 additions & 0 deletions examples/mcp/structured-content-preview/preview_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""MCP server for exercising structured tool result previews.

Run with:
uv run preview_server.py
"""

from __future__ import annotations

import json
from typing import Any

from mcp.server.fastmcp import FastMCP
from mcp.types import CallToolResult, TextContent

app = FastMCP(name="Structured Content Preview Demo")


def _text_block(payload: Any) -> TextContent:
return TextContent(
type="text",
text=json.dumps(payload, ensure_ascii=False, separators=(",", ":")),
)


def _tool_result(*, text_payloads: list[Any], structured_payload: dict[str, Any]) -> CallToolResult:
result = CallToolResult(
content=[_text_block(payload) for payload in text_payloads],
isError=False,
)
setattr(result, "structuredContent", structured_payload)
return result


@app.tool(
name="structured_content_match",
description=(
"Return multiple text blocks that match the structuredContent payload. "
"Useful for checking the new preview path."
),
)
def structured_content_match() -> CallToolResult:
tickets = [
{"ticket_id": "T-100", "status": "open", "owner": "alex"},
{"ticket_id": "T-101", "status": "pending", "owner": "sam"},
]
return _tool_result(
text_payloads=tickets,
structured_payload={"tickets": tickets, "match_state": "match"},
)


@app.tool(
name="structured_content_mismatch",
description=(
"Return multiple text blocks that do not match structuredContent. "
"Useful for seeing how the preview behaves when the two disagree."
),
)
def structured_content_mismatch() -> CallToolResult:
text_tickets = [
{"ticket_id": "T-100", "status": "closed", "owner": "alex"},
{"ticket_id": "T-101", "status": "pending", "owner": "sam"},
]
structured_tickets = [
{"ticket_id": "T-100", "status": "open", "owner": "alex"},
{"ticket_id": "T-101", "status": "escalated", "owner": "sam"},
]
return _tool_result(
text_payloads=text_tickets,
structured_payload={"tickets": structured_tickets, "match_state": "mismatch"},
)


if __name__ == "__main__":
app.run(transport="stdio")
Loading
Loading