Skip to content

Commit 38a2691

Browse files
committed
move _convert_to_content
1 parent 98c5ae9 commit 38a2691

File tree

2 files changed

+39
-33
lines changed

2 files changed

+39
-33
lines changed

src/mcp/server/fastmcp/server.py

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
AbstractAsyncContextManager,
1010
asynccontextmanager,
1111
)
12-
from itertools import chain
1312
from typing import Any, Generic, Literal
1413

1514
import anyio
@@ -38,7 +37,6 @@
3837
from mcp.server.fastmcp.resources import FunctionResource, Resource, ResourceManager
3938
from mcp.server.fastmcp.tools import Tool, ToolManager
4039
from mcp.server.fastmcp.utilities.logging import configure_logging, get_logger
41-
from mcp.server.fastmcp.utilities.types import Image
4240
from mcp.server.lowlevel.helper_types import ReadResourceContents
4341
from mcp.server.lowlevel.server import LifespanResultT
4442
from mcp.server.lowlevel.server import Server as MCPServer
@@ -54,7 +52,6 @@
5452
AnyFunction,
5553
ContentBlock,
5654
GetPromptResult,
57-
TextContent,
5855
ToolAnnotations,
5956
)
6057
from mcp.types import Prompt as MCPPrompt
@@ -968,28 +965,6 @@ async def get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -
968965
raise ValueError(str(e))
969966

970967

971-
def _convert_to_content(
972-
result: Any,
973-
) -> Sequence[ContentBlock]:
974-
"""Convert a result to a sequence of content objects."""
975-
if result is None:
976-
return []
977-
978-
if isinstance(result, ContentBlock):
979-
return [result]
980-
981-
if isinstance(result, Image):
982-
return [result.to_image_content()]
983-
984-
if isinstance(result, list | tuple):
985-
return list(chain.from_iterable(_convert_to_content(item) for item in result)) # type: ignore[reportUnknownVariableType]
986-
987-
if not isinstance(result, str):
988-
result = pydantic_core.to_json(result, fallback=str, indent=2).decode()
989-
990-
return [TextContent(type="text", text=result)]
991-
992-
993968
class Context(BaseModel, Generic[ServerSessionT, LifespanContextT, RequestT]):
994969
"""Context object providing access to MCP capabilities.
995970

src/mcp/server/fastmcp/utilities/func_metadata.py

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
import json
33
from collections.abc import Awaitable, Callable, Iterable, Sequence
44
from dataclasses import asdict, is_dataclass
5+
from itertools import chain
56
from types import GenericAlias
67
from typing import Annotated, Any, ForwardRef, Literal, get_args, get_origin, get_type_hints
78

9+
import pydantic_core
810
from pydantic import (
911
BaseModel,
1012
ConfigDict,
@@ -19,7 +21,8 @@
1921

2022
from mcp.server.fastmcp.exceptions import InvalidSignature
2123
from mcp.server.fastmcp.utilities.logging import get_logger
22-
from mcp.types import ContentBlock
24+
from mcp.server.fastmcp.utilities.types import Image
25+
from mcp.types import ContentBlock, TextContent
2326

2427
logger = get_logger(__name__)
2528

@@ -85,13 +88,11 @@ def convert_result(self, result: Any) -> Any:
8588
tool call handler provides generic backwards compatibility serialization of
8689
structured content**. This is for FastMCP backwards compatibility: we need to
8790
retain FastMCP's ad hoc conversion logic for constructing unstructured output
88-
from function return values (see _convert_to_content in mcp.server.fastmcp.server),
89-
whereas the lowlevel server simply serializes the structured output.
91+
from function return values, whereas the lowlevel server simply serializes
92+
the structured output.
9093
9194
This backwards compatibility provision will be removed in a future version of FastMCP.
9295
"""
93-
from mcp.server.fastmcp.server import _convert_to_content # type: ignore
94-
9596
unstructured_content = _convert_to_content(result)
9697

9798
if self.output_model is None:
@@ -271,9 +272,6 @@ def func_metadata(
271272
f"Function {func.__name__}: return type {annotation} is not supported for structured output. "
272273
)
273274

274-
import sys
275-
276-
print(f"HEY Function {func.__name__} metadata: {output_model=} {output_conversion=}", file=sys.stderr)
277275

278276
return FuncMetadata(arg_model=arguments_model, output_model=output_model, output_conversion=output_conversion)
279277

@@ -476,3 +474,36 @@ def _get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
476474
typed_return = _get_typed_annotation(signature.return_annotation, globalns)
477475
typed_signature = inspect.Signature(typed_params, return_annotation=typed_return)
478476
return typed_signature
477+
478+
479+
def _convert_to_content(
480+
result: Any,
481+
) -> Sequence[ContentBlock]:
482+
"""
483+
Convert a result to a sequence of content objects.
484+
485+
Note: This conversion logic comes from previous versions of FastMCP and is being
486+
retained for purposes of backwards compatibility. It produces different unstructured
487+
output than the lowlevel server tool call handler, which serializes structured content.
488+
"""
489+
if result is None:
490+
return []
491+
492+
if isinstance(result, ContentBlock):
493+
return [result]
494+
495+
if isinstance(result, Image):
496+
return [result.to_image_content()]
497+
498+
if isinstance(result, list | tuple):
499+
return list(
500+
chain.from_iterable(
501+
_convert_to_content(item)
502+
for item in result # type: ignore
503+
)
504+
)
505+
506+
if not isinstance(result, str):
507+
result = pydantic_core.to_json(result, fallback=str, indent=2).decode()
508+
509+
return [TextContent(type="text", text=result)]

0 commit comments

Comments
 (0)