Fall back to unstructured output when output model selection fails (#3573)
Fixes #3573
Disclosure: this fix was developed with AI assistance; I have reviewed the diff, run the tests locally, and can explain the change.
Summary
func_metadata raises an uncaught pydantic.errors.PydanticSchemaGenerationError when a tool's return annotation is Iterator[...] / AsyncIterator[...] (either the typing or the collections.abc spelling), so a correctly annotated generator tool cannot register at all. The fallback machinery for unserializable return types already exists — the call to _create_output_model() simply sits outside the try that routes those failures to it.
This wraps that call in the same failure path: on PydanticSchemaGenerationError the tool degrades to unstructured output (output_schema=None), and with structured_output=True the existing flow raises InvalidSignature. Generator[...] is untouched — pydantic treats it as a sequence and its structured array schema keeps working.
Motivation and Context
-> Iterator[str] is the PEP 484 spelling for generator functions, but registering one currently crashes at decoration time instead of taking the documented unstructured fallback (#3573). Fixing it at the model-selection step reuses the existing fallback semantics in one place rather than special-casing iterator types.
How Has This Been Tested?
- Reproduced on
main(f1b6589) for all four spellings (typing/collections.abc×Iterator/AsyncIterator) with the default andstructured_output=True. tests/server/mcpserver/test_func_metadata.py: 60/60 pass, including two new regression tests (the four spellings fall back to unstructured / raiseInvalidSignature;Generator[str, None, None]keeps its structured array schema).- Adjacent suites (
tests/server/mcpserver/tools,tests/interaction/mcpserver/test_tools.py): 85/85 pass.ruff checkandruff format --checkclean.
Breaking Changes
None. Registrations that previously crashed now succeed as unstructured tools (or raise InvalidSignature under structured_output=True, matching the documented contract for unserializable return types).
Types of changes
- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing functionality to change)
- Documentation update
Checklist
- I am assigned to the linked issue (or it is labeled
help wanted, or I'm a maintainer) - I have disclosed any AI assistance and can explain the change in my own words
- I have read the MCP Documentation
- My code follows the repository's style guidelines
- New and existing tests pass locally
- I have added appropriate error handling
- I have added or updated documentation as needed