fastmcp/tests/client/test_result_hydration.py
marvin-context-protocol[bot] 8af9ceb338 Publish FastMCP v4.1.0 docs
2026-10-08 22:57:20 +00:00

68 lines
1.9 KiB
Python

"""Hydration of ``Client.call_tool(...).data`` from the server's output schema.
The client rebuilds Python types from the output schema, so every ``format``
and positional item schema the server emits has to survive that round trip.
"""
from datetime import date, datetime, time, timedelta
from uuid import UUID
from pydantic import BaseModel
from fastmcp import Client, FastMCP
class Reading(BaseModel):
ts: datetime
day: date
at: time
dur: timedelta
ident: UUID
class TestStructuredResultHydration:
async def test_annotated_scalar_formats_arrive_as_python_types(self):
mcp = FastMCP()
@mcp.tool
def read() -> Reading:
return Reading(
ts=datetime(2024, 1, 1, 12),
day=date(2024, 1, 1),
at=time(1, 2),
dur=timedelta(days=1),
ident=UUID(int=7),
)
async with Client(mcp) as client:
data = (await client.call_tool("read")).data
assert data.ts == datetime(2024, 1, 1, 12)
assert data.day == date(2024, 1, 1)
assert data.at == time(1, 2)
assert data.dur == timedelta(days=1)
assert data.ident == UUID(int=7)
async def test_fixed_length_tuple_result_keeps_positions(self):
mcp = FastMCP()
@mcp.tool
def first_seen() -> tuple[date, int]:
return (date(2026, 8, 27), 3)
async with Client(mcp) as client:
data = (await client.call_tool("first_seen")).data
assert data == (date(2026, 8, 27), 3)
async def test_formats_hydrate_inside_collections(self):
mcp = FastMCP()
@mcp.tool
def recent() -> dict[str, list[date]]:
return {"days": [date(2026, 8, 27), date(2026, 8, 28)]}
async with Client(mcp) as client:
data = (await client.call_tool("recent")).data
assert data["days"] == [date(2026, 8, 27), date(2026, 8, 28)]