Skip to content

Commit 2c2cd0e

Browse files
committed
Revert "add output_schema override to support ChatGPT App SDK"
This reverts commit 88bc72d.
1 parent 88bc72d commit 2c2cd0e

File tree

4 files changed

+3
-105
lines changed

4 files changed

+3
-105
lines changed

src/mcp/server/fastmcp/server.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,6 @@ def add_tool(
385385
annotations: ToolAnnotations | None = None,
386386
icons: list[Icon] | None = None,
387387
meta: dict[str, Any] | None = None,
388-
output_schema: dict[str, Any] | None = None,
389388
structured_output: bool | None = None,
390389
) -> None:
391390
"""Add a tool to the server.
@@ -399,8 +398,6 @@ def add_tool(
399398
title: Optional human-readable title for the tool
400399
description: Optional description of what the tool does
401400
annotations: Optional ToolAnnotations providing additional tool information
402-
meta: Optional metadata dictionary
403-
output_schema: Optional Pydantic model defining the output schema separate from the tool's return type
404401
structured_output: Controls whether the tool's output is structured or unstructured
405402
- If None, auto-detects based on the function's return type annotation
406403
- If True, creates a structured tool (return type annotation permitting)
@@ -414,7 +411,6 @@ def add_tool(
414411
annotations=annotations,
415412
icons=icons,
416413
meta=meta,
417-
output_schema=output_schema,
418414
structured_output=structured_output,
419415
)
420416

@@ -437,7 +433,6 @@ def tool(
437433
annotations: ToolAnnotations | None = None,
438434
icons: list[Icon] | None = None,
439435
meta: dict[str, Any] | None = None,
440-
output_schema: dict[str, Any] | None = None,
441436
structured_output: bool | None = None,
442437
) -> Callable[[AnyFunction], AnyFunction]:
443438
"""Decorator to register a tool.
@@ -451,8 +446,6 @@ def tool(
451446
title: Optional human-readable title for the tool
452447
description: Optional description of what the tool does
453448
annotations: Optional ToolAnnotations providing additional tool information
454-
meta: Optional metadata dictionary
455-
output_schema: Optional Pydantic model defining the output schema separate from the tool's return type
456449
structured_output: Controls whether the tool's output is structured or unstructured
457450
- If None, auto-detects based on the function's return type annotation
458451
- If True, creates a structured tool (return type annotation permitting)
@@ -488,7 +481,6 @@ def decorator(fn: AnyFunction) -> AnyFunction:
488481
annotations=annotations,
489482
icons=icons,
490483
meta=meta,
491-
output_schema=output_schema,
492484
structured_output=structured_output,
493485
)
494486
return fn

src/mcp/server/fastmcp/tools/base.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,9 @@ class Tool(BaseModel):
3535
annotations: ToolAnnotations | None = Field(None, description="Optional annotations for the tool")
3636
icons: list[Icon] | None = Field(default=None, description="Optional list of icons for this tool")
3737
meta: dict[str, Any] | None = Field(default=None, description="Optional metadata for this tool")
38-
output_schema_override: dict[str, Any] | None = Field(
39-
default=None,
40-
description="Optional Pydantic model defining the output schema separate from the tool's return type",
41-
)
4238

4339
@cached_property
4440
def output_schema(self) -> dict[str, Any] | None:
45-
if self.output_schema_override is not None:
46-
return self.output_schema_override
4741
return self.fn_metadata.output_schema
4842

4943
@classmethod
@@ -57,7 +51,6 @@ def from_function(
5751
annotations: ToolAnnotations | None = None,
5852
icons: list[Icon] | None = None,
5953
meta: dict[str, Any] | None = None,
60-
output_schema: dict[str, Any] | None = None,
6154
structured_output: bool | None = None,
6255
) -> Tool:
6356
"""Create a Tool from a function."""
@@ -91,7 +84,6 @@ def from_function(
9184
annotations=annotations,
9285
icons=icons,
9386
meta=meta,
94-
output_schema_override=output_schema,
9587
)
9688

9789
async def run(
@@ -106,7 +98,7 @@ async def run(
10698
self.fn,
10799
self.is_async,
108100
arguments,
109-
({self.context_kwarg: context} if self.context_kwarg is not None else None),
101+
{self.context_kwarg: context} if self.context_kwarg is not None else None,
110102
)
111103

112104
if convert_result:

src/mcp/server/fastmcp/tools/tool_manager.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ def add_tool(
5151
annotations: ToolAnnotations | None = None,
5252
icons: list[Icon] | None = None,
5353
meta: dict[str, Any] | None = None,
54-
output_schema: dict[str, Any] | None = None,
5554
structured_output: bool | None = None,
5655
) -> Tool:
5756
"""Add a tool to the server."""
@@ -63,7 +62,6 @@ def add_tool(
6362
annotations=annotations,
6463
icons=icons,
6564
meta=meta,
66-
output_schema=output_schema,
6765
structured_output=structured_output,
6866
)
6967
existing = self._tools.get(tool.name)

tests/server/fastmcp/test_tool_manager.py

Lines changed: 2 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import Any, TypedDict
55

66
import pytest
7-
from pydantic import BaseModel, Field
7+
from pydantic import BaseModel
88

99
from mcp.server.fastmcp import Context, FastMCP
1010
from mcp.server.fastmcp.exceptions import ToolError
@@ -577,97 +577,13 @@ def get_user() -> UserOutput:
577577

578578
# Test that output_schema is populated
579579
expected_schema = {
580-
"properties": {
581-
"name": {"type": "string", "title": "Name"},
582-
"age": {"type": "integer", "title": "Age"},
583-
},
580+
"properties": {"name": {"type": "string", "title": "Name"}, "age": {"type": "integer", "title": "Age"}},
584581
"required": ["name", "age"],
585582
"title": "UserOutput",
586583
"type": "object",
587584
}
588585
assert tool.output_schema == expected_schema
589586

590-
def test_add_output_schema_override(self):
591-
"""Test registering a tool with an explicit output schema."""
592-
593-
# For the ChatGPT App SDK, the tool output should be structured like:
594-
# {
595-
# "structuredOutput": { ... },
596-
# "content": [ { "type": "text", "text": "..." }, ... ],
597-
# "_meta": { ... }
598-
# }
599-
# and the tool output schema should reflect the structure of "structuredOutput"
600-
class UserOutput(BaseModel):
601-
name: str
602-
age: int
603-
604-
# Output structure expected by ChatGPT App SDK
605-
class ToolOutput(BaseModel):
606-
structuredOutput: UserOutput
607-
content: list[dict[str, str]]
608-
meta: dict[str, Any] = Field(alias="_meta")
609-
610-
def get_user(user_id: int) -> ToolOutput:
611-
"""Get user by ID."""
612-
return ToolOutput(
613-
structuredOutput=UserOutput(name="John", age=30),
614-
content=[{"type": "text", "text": "User found"}],
615-
_meta={"request_id": "12345"},
616-
)
617-
618-
manager = ToolManager()
619-
tool = manager.add_tool(get_user, output_schema=UserOutput.model_json_schema())
620-
621-
expected_schema = {
622-
"properties": {
623-
"name": {"type": "string", "title": "Name"},
624-
"age": {"type": "integer", "title": "Age"},
625-
},
626-
"required": ["name", "age"],
627-
"title": "UserOutput",
628-
"type": "object",
629-
}
630-
assert tool.output_schema == expected_schema
631-
assert tool.fn_metadata.output_model == ToolOutput
632-
633-
@pytest.mark.anyio
634-
async def test_call_tool_with_output_schema_override(self):
635-
# For the ChatGPT App SDK, the tool output should be structured like:
636-
# {
637-
# "structuredOutput": { ... },
638-
# "content": [ { "type": "text", "text": "..." }, ... ],
639-
# "_meta": { ... }
640-
# }
641-
# and the tool output schema should reflect the structure of "structuredOutput"
642-
class UserOutput(BaseModel):
643-
name: str
644-
age: int
645-
646-
# Output structure expected by ChatGPT App SDK
647-
class ToolOutput(BaseModel):
648-
structuredOutput: UserOutput
649-
content: list[dict[str, str]]
650-
meta: dict[str, Any] = Field(alias="_meta")
651-
652-
def get_user(user_id: int) -> ToolOutput:
653-
"""Get user by ID."""
654-
return ToolOutput(
655-
structuredOutput=UserOutput(name="John", age=30),
656-
content=[{"type": "some more information about the output data"}],
657-
_meta={"request_id": "12345"},
658-
)
659-
660-
manager = ToolManager()
661-
manager.add_tool(get_user, output_schema=UserOutput.model_json_schema())
662-
result = await manager.call_tool("get_user", {"user_id": 1}, convert_result=True)
663-
664-
expected_result = {
665-
"structuredOutput": {"name": "John", "age": 30},
666-
"content": [{"type": "some more information about the output data"}],
667-
"_meta": {"request_id": "12345"},
668-
}
669-
assert len(result) == 2 and result[1] == expected_result
670-
671587
@pytest.mark.anyio
672588
async def test_tool_with_dict_str_any_output(self):
673589
"""Test tool with dict[str, Any] return type."""

0 commit comments

Comments
 (0)