func_metadata raises uncaught PydanticSchemaGenerationError for Iterator/AsyncIterator tool return annotations instead of the unstructured fallback
還沒有人認領這個 Issue。
評估
研究方向
問題位於 src/mcp/server/mcpserver/utilities/func_metadata.py。查看第 444 行,其中 _create_output_model 在 try/except 區塊外部被呼叫。修復方法是將該呼叫移到現有的、捕獲 PydanticUserError 和 pydantic_core.SchemaError 的 try/except 區塊內部。使用提供的重現腳本進行測試,以確保 Iterator/AsyncIterator 回傳型別現在要麼回退到非結構化輸出,要麼按照文件所述拋出 InvalidSignature。
由索引模型根據 Issue 內容生成。
描述
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
Registering a tool whose function is annotated -> Iterator[...] or -> AsyncIterator[...] — the PEP 484 spelling for generator functions — raises a raw pydantic.errors.PydanticSchemaGenerationError at registration time, instead of either falling back to an unstructured tool (structured_output=None, the default) or raising the SDK's InvalidSignature (structured_output=True). The same happens via @server.tool() / Tool.from_function(), so a properly typed generator tool cannot be registered at all.
Actual output of the script in "Example Code" (error text truncated at 80 chars by the script):
func_metadata(search): UNCAUGHT pydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for typing.Iterator[str]. Set `arbitrary
func_metadata(search, structured_output=True): UNCAUGHT pydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for typing.Iterator[str]. Set `arbitrary
Tool.from_function(search): UNCAUGHT pydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for typing.Iterator[str]. Set `arbitrary
Full traceback (captured with the collections.abc spelling of the same annotation, so the error names it accordingly):
File "src/mcp/server/mcpserver/utilities/func_metadata.py", line 444, in func_metadata
output_model, wrap_output = _create_output_model(original_annotation, return_type_expr, func.__name__)
File "src/mcp/server/mcpserver/utilities/func_metadata.py", line 550, in _create_output_model
model = _create_wrapped_model(func_name, original_annotation)
File "src/mcp/server/mcpserver/utilities/func_metadata.py", line 621, in _create_wrapped_model
return create_model(model_name, result=annotation)
pydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for collections.abc.Iterator[str]. Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it.
What I expected is what already happens for other unserializable return types, pinned by test_structured_output_unserializable_type_error (tests/server/mcpserver/test_func_metadata.py:1233, passes on main) and documented in docs/servers/structured-output.md: with structured_output=None, registration succeeds and output_schema is None (fallback to text); with structured_output=True, InvalidSignature: Function search: return type ... is not serializable for structured output. For contrast, Iterable[str] and Generator[str, None, None] both register successfully through the same wrapped-model path — only the PEP 484-recommended spellings for generators crash.
Root cause: _create_output_model(...) is called at src/mcp/server/mcpserver/utilities/func_metadata.py:444, outside the try/except at lines 446–470 whose except tuple (PydanticUserError, pydantic_core.SchemaError, ...) exists exactly so that "an unsupported return type surfaces here, at registration" as a clean failure. _create_output_model → _create_wrapped_model → create_model(model_name, result=annotation) (line 621) builds a schema itself, and its PydanticSchemaGenerationError (a PydanticUserError subclass) escapes uncaught. Moving the line 444 call inside the existing try/except looks like it would restore both documented behaviours; I'd be happy to be assigned and open a PR with that approach.
Related: the guard was added in #2434 (for #1131), but it wraps only the FuncMetadata construction, not this call. #1060 reports the same error class for a different type (Image, 1.x fastmcp) and looks unrelated to this code path.
AI disclosure: this issue and its reproduction were prepared with AI assistance.
Example Code
from typing import Iterator
from mcp.server.mcpserver.tools import Tool
from mcp.server.mcpserver.utilities.func_metadata import func_metadata
def search(n: int) -> Iterator[str]:
yield from ["a"] * n
for label, call in [
("func_metadata(search)", lambda: func_metadata(search)),
("func_metadata(search, structured_output=True)", lambda: func_metadata(search, structured_output=True)),
("Tool.from_function(search)", lambda: Tool.from_function(search)),
]:
try:
call()
print(f"{label}: OK")
except Exception as e:
print(f"{label}: UNCAUGHT {type(e).__module__}.{type(e).__name__}: {str(e)[:80]}")
# AsyncIterator[str] return annotations behave identically
Python & MCP Python SDK
mcp: main @ 6affe5c0d3588fd1705713b3703dc68015cfe3eb
func_metadata.py is identical in v2.2.0 (latest 2.x release);
the same crash reproduces on a fresh `pip install mcp==2.2.0`
Python 3.13.15
pydantic 2.12.5
macOS 26.6.2 (arm64)
- 主要語言
- Python
- 星號
- 24.3k
- 分支
- 4k
- 平均合併
- 1 天 16 小時
- 30 天內合併 PR
- 25
貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
modelcontextprotocol/python-sdk 的其他 Issue
-
v1 v2
難度 2/5 1-3 小時 新手友好度 70/100
modelcontextprotocol/python-sdk#3578 · 1 則留言 ·
-
v2
難度 2/5 1-3 小時 新手友好度 75/100
modelcontextprotocol/python-sdk#3566 ·
-
v1 v2
難度 2/5 1-3 小時 新手友好度 85/100
modelcontextprotocol/python-sdk#3546 · 5 則留言 ·
-
v1 v2
難度 2/5 1-3 小時 新手友好度 76/100
modelcontextprotocol/python-sdk#3545 · 2 則留言 ·
-
v1 v2
難度 1/5 1 小時以內 新手友好度 91/100
modelcontextprotocol/python-sdk#3508 · 2 則留言 ·
查看 modelcontextprotocol/python-sdk 的全部 Issue
相似的 Issue
-
area: harness bug status: needs-triage
難度 2/5 1-3 小時 新手友好度 75/100
Human-Agent-Society/reef#625 ·
-
難度 2/5 1-3 小時 新手友好度 70/100
-
難度 1/5 1 小時以內 新手友好度 80/100
learningequality/kolibri#15351 · 2 則留言 ·
-
難度 2/5 1-3 小時 新手友好度 75/100
-
Name consistency 未關閉
難度 2/5 1-3 小時 新手友好度 75/100
eellak/triplestore#65 · 1 則留言 ·