modelcontextprotocol / modelcontextprotocol/python-sdk
CallToolResult structuredContent is not alias-normalized to match outputSchema
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Python
- Star
- 24.3k
- Fork
- 4k
- Merge trung bình
- 1 ngày 1 giờ
- Pull request đã merge (30 ngày)
- 31
Mô tả
Initial Checks
- I confirm that I'm using the newest release of my line (the latest 2.x, or the latest 1.x if I'm still on v1)
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Release line
2.x (current stable)
Description
FuncMetadata.convert_result treats a plain model return and a CallToolResult return differently for Pydantic field aliases.
- Plain returns:
validate_python(..., by_alias=True, by_name=True)thenmodel_dump(..., by_alias=True)/dump_python(..., by_alias=True), sostructuredContentkeys matchoutputSchema. CallToolResultpath:validate_python(result.structured_content)only, then returns the result unchanged.
Because validation accepts Python field names when populate_by_name / by_name is in play, a tool can pass validation while emitting wire keys that do not match the published outputSchema (which uses aliases). Clients that validate structuredContent against outputSchema then reject a server-produced payload.
Related but not the same as #1073 / #1099 (plain-return alias dump) or #3100 / #3118 (schema validation vs serialization mode). This is specifically the CallToolResult short-circuit skipping alias normalization.
Verified on current main tip 08a3bc8eaf5bb69a6cb05ac708a86f5325977c20.
Expected: after validating CallToolResult.structured_content, normalize it the same way as the plain-return path (by_alias dump) before returning.
Example Code
from __future__ import annotations
import json
from typing import Annotated
from pydantic import BaseModel, ConfigDict, Field
from mcp_types import CallToolResult, TextContent
from mcp.server.mcpserver.utilities.func_metadata import func_metadata
class AliasOut(BaseModel):
model_config = ConfigDict(populate_by_name=True)
field_first: str = Field(alias="first")
field_second: str = Field(alias="second")
def plain() -> AliasOut:
return AliasOut(field_first="a", field_second="b")
def via_call_tool_result() -> Annotated[CallToolResult, AliasOut]:
# Python field names — accepted by validate when populate_by_name is on
return CallToolResult(
content=[TextContent(text="ok")],
structured_content={"field_first": "a", "field_second": "b"},
)
plain_meta = func_metadata(plain)
ctr_meta = func_metadata(via_call_tool_result)
print("outputSchema keys:", sorted(plain_meta.output_schema["properties"]))
print("plain:", json.dumps(plain_meta.convert_result(plain()).structured_content, sort_keys=True))
print(
"CallToolResult:",
json.dumps(ctr_meta.convert_result(via_call_tool_result()).structured_content, sort_keys=True),
)
Observed on main @ 08a3bc8:
outputSchema keys: ['first', 'second']
plain: {"first": "a", "second": "b"}
CallToolResult: {"field_first": "a", "field_second": "b"}
Python & MCP Python SDK
- Python: 3.12.10
- MCP Python SDK: current
main@08a3bc8eaf5bb69a6cb05ac708a86f5325977c20(2.1.2.dev16+08a3bc8) - Pydantic: 2.12.5 (venv) / also reproduced with system 2.13.5 for imports
AI disclosure
This issue was drafted with AI assistance (Cursor / Grok). I verified the reproduction on current main and reviewed the convert_result paths in func_metadata.py before filing. I would like to open a fix PR for this issue.
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu trong mcp/server/mcpserver/utilities/func_metadata.py tại FuncMetadata.convert_result và so sánh nhánh CallToolResult với đường dẫn trả về thông thường được mô tả trong issue. Trước tiên, hãy chạy ví dụ alias được cung cấp; hoàn tất khi CallToolResult.structured_content sử dụng các alias của outputSchema sau khi chuyển đổi, đồng thời giữ nguyên phần còn lại của kết quả.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- python
- Lĩnh vực
- api
- Loại issue
- Lỗi
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức độ hoạt động
- Sôi nổi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức phù hợp với người mới
- 76/100