mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-24 06:24:18 +02:00
use pydantic_core.to_json
This commit is contained in:
parent
c01aabbd05
commit
a9253d3111
9 changed files with 24 additions and 49 deletions
|
|
@ -3,7 +3,6 @@
|
|||
from __future__ import annotations as _annotations
|
||||
|
||||
import inspect
|
||||
import json
|
||||
from collections.abc import Awaitable, Callable, Sequence
|
||||
from typing import TYPE_CHECKING, Annotated, Any, Literal
|
||||
|
||||
|
|
@ -192,7 +191,9 @@ class Prompt(BaseModel):
|
|||
content = TextContent(type="text", text=msg)
|
||||
messages.append(Message(role="user", content=content))
|
||||
else:
|
||||
content = json.dumps(pydantic_core.to_jsonable_python(msg))
|
||||
content = pydantic_core.to_json(
|
||||
msg, fallback=str, indent=2
|
||||
).decode()
|
||||
messages.append(Message(role="user", content=content))
|
||||
except Exception:
|
||||
raise ValueError(
|
||||
|
|
|
|||
|
|
@ -97,15 +97,12 @@ class FunctionResource(Resource):
|
|||
|
||||
if isinstance(result, Resource):
|
||||
return await result.read(context=context)
|
||||
if isinstance(result, bytes):
|
||||
elif isinstance(result, bytes):
|
||||
return result
|
||||
if isinstance(result, str):
|
||||
elif isinstance(result, str):
|
||||
return result
|
||||
try:
|
||||
return json.dumps(pydantic_core.to_jsonable_python(result))
|
||||
except (TypeError, pydantic_core.PydanticSerializationError):
|
||||
# If JSON serialization fails, try str()
|
||||
return str(result)
|
||||
else:
|
||||
return pydantic_core.to_json(result, fallback=str, indent=2).decode()
|
||||
except Exception as e:
|
||||
raise ValueError(f"Error reading resource {self.uri}: {e}")
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ from typing import TYPE_CHECKING, Any, Generic, Literal
|
|||
|
||||
import anyio
|
||||
import httpx
|
||||
import pydantic_core
|
||||
import uvicorn
|
||||
from mcp.server.lowlevel.helper_types import ReadResourceContents
|
||||
from mcp.server.lowlevel.server import LifespanResultT
|
||||
|
|
@ -27,6 +26,7 @@ from mcp.types import (
|
|||
EmbeddedResource,
|
||||
GetPromptResult,
|
||||
ImageContent,
|
||||
PromptMessage,
|
||||
TextContent,
|
||||
)
|
||||
from mcp.types import Prompt as MCPPrompt
|
||||
|
|
@ -435,7 +435,12 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
messages = await self._prompt_manager.render_prompt(
|
||||
name, arguments=arguments or {}, context=context
|
||||
)
|
||||
return GetPromptResult(messages=pydantic_core.to_jsonable_python(messages))
|
||||
|
||||
return GetPromptResult(
|
||||
messages=[
|
||||
PromptMessage(role=m.role, content=m.content) for m in messages
|
||||
]
|
||||
)
|
||||
else:
|
||||
for server in self._mounted_servers.values():
|
||||
if server.match_prompt(name):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
import json
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING, Annotated, Any
|
||||
|
||||
|
|
@ -166,23 +165,7 @@ def _convert_to_content(
|
|||
|
||||
return other_content + mcp_types
|
||||
|
||||
# if the result is a bytes object, convert it to a text content object
|
||||
if not isinstance(result, str):
|
||||
try:
|
||||
jsonable_result = pydantic_core.to_jsonable_python(result)
|
||||
if jsonable_result is None:
|
||||
return [TextContent(type="text", text="null")]
|
||||
elif isinstance(jsonable_result, bool):
|
||||
return [
|
||||
TextContent(
|
||||
type="text", text="true" if jsonable_result else "false"
|
||||
)
|
||||
]
|
||||
elif isinstance(jsonable_result, str | int | float):
|
||||
return [TextContent(type="text", text=str(jsonable_result))]
|
||||
else:
|
||||
return [TextContent(type="text", text=json.dumps(jsonable_result))]
|
||||
except Exception:
|
||||
result = str(result)
|
||||
result = pydantic_core.to_json(result, fallback=str, indent=2).decode()
|
||||
|
||||
return [TextContent(type="text", text=result)]
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ class TestFunctionResource:
|
|||
fn=lambda: MyModel(name="test"),
|
||||
)
|
||||
content = await resource.read()
|
||||
assert content == '{"name": "test"}'
|
||||
assert content == '{\n "name": "test"\n}'
|
||||
|
||||
async def test_custom_type_conversion(self):
|
||||
"""Test handling of custom types."""
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ class TestResourceTemplate:
|
|||
|
||||
assert isinstance(resource, FunctionResource)
|
||||
content = await resource.read()
|
||||
assert content == "hello"
|
||||
assert content == '"hello"'
|
||||
|
||||
async def test_wildcard_param_can_create_resource(self):
|
||||
"""Test that wildcard parameters are valid."""
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ class TestResourcesAndTemplates:
|
|||
async with Client(main_app) as client:
|
||||
resource = await client.read_resource("data+data://users")
|
||||
assert isinstance(resource[0], TextResourceContents)
|
||||
assert resource[0].text == '["user1", "user2"]'
|
||||
assert resource[0].text == '[\n "user1",\n "user2"\n]'
|
||||
|
||||
async def test_mount_with_resource_templates(self):
|
||||
"""Test mounting a server with resource templates."""
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ class TestTools:
|
|||
async with Client(tool_server) as client:
|
||||
result = await client.call_tool("list_tool", {})
|
||||
assert isinstance(result[0], TextContent)
|
||||
assert result[0].text == '["x", 2]'
|
||||
assert result[0].text == '[\n "x",\n 2\n]'
|
||||
|
||||
|
||||
class TestToolReturnTypes:
|
||||
|
|
@ -130,7 +130,7 @@ class TestToolReturnTypes:
|
|||
async with Client(mcp) as client:
|
||||
result = await client.call_tool("bytes_tool", {})
|
||||
assert isinstance(result[0], TextContent)
|
||||
assert result[0].text == "Hello, world!"
|
||||
assert result[0].text == '"Hello, world!"'
|
||||
|
||||
async def test_uuid(self):
|
||||
mcp = FastMCP()
|
||||
|
|
@ -144,7 +144,7 @@ class TestToolReturnTypes:
|
|||
async with Client(mcp) as client:
|
||||
result = await client.call_tool("uuid_tool", {})
|
||||
assert isinstance(result[0], TextContent)
|
||||
assert result[0].text == str(test_uuid)
|
||||
assert result[0].text == f'"{test_uuid}"'
|
||||
|
||||
async def test_path(self):
|
||||
mcp = FastMCP()
|
||||
|
|
@ -158,7 +158,7 @@ class TestToolReturnTypes:
|
|||
async with Client(mcp) as client:
|
||||
result = await client.call_tool("path_tool", {})
|
||||
assert isinstance(result[0], TextContent)
|
||||
assert result[0].text == str(test_path)
|
||||
assert result[0].text == f'"{test_path}"'
|
||||
|
||||
async def test_datetime(self):
|
||||
mcp = FastMCP()
|
||||
|
|
@ -172,7 +172,7 @@ class TestToolReturnTypes:
|
|||
async with Client(mcp) as client:
|
||||
result = await client.call_tool("datetime_tool", {})
|
||||
assert isinstance(result[0], TextContent)
|
||||
assert result[0].text == dt.isoformat()
|
||||
assert result[0].text == f'"{dt.isoformat()}"'
|
||||
|
||||
async def test_image(self, tmp_path: Path):
|
||||
mcp = FastMCP()
|
||||
|
|
|
|||
|
|
@ -387,18 +387,7 @@ class TestCallTools:
|
|||
assert isinstance(result, list)
|
||||
assert len(result) == 1
|
||||
assert isinstance(result[0], TextContent)
|
||||
assert result[0].text == '["rex", "gertrude"]'
|
||||
assert json.loads(result[0].text) == ["rex", "gertrude"]
|
||||
|
||||
result = await manager.call_tool(
|
||||
"name_shrimp",
|
||||
{"tank": '{"x": null, "shrimp": [{"name": "rex"}, {"name": "gertrude"}]}'},
|
||||
)
|
||||
assert isinstance(result, list)
|
||||
assert len(result) == 1
|
||||
assert isinstance(result[0], TextContent)
|
||||
assert result[0].text == '["rex", "gertrude"]'
|
||||
assert json.loads(result[0].text) == ["rex", "gertrude"]
|
||||
assert result[0].text == '[\n "rex",\n "gertrude"\n]'
|
||||
|
||||
|
||||
class TestToolSchema:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue