Title: test(mcpserver): cover PEP 604 union return annotations (#2591) by Bartok9 · Pull Request #2906 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: test(mcpserver): cover PEP 604 union return annotations (#2591) by Bartok9 · Pull Request #2906 · modelcontextprotocol/python-sdk
X Title: test(mcpserver): cover PEP 604 union return annotations (#2591) by Bartok9 · Pull Request #2906 · modelcontextprotocol/python-sdk
Description: Summary Adds a regression test for tool functions whose return annotation is a multi-member PEP 604 union mixing container and scalar types (dict | list | str). Guards an already-fixed-but-untested case so it can't silently regress. Motivation Closes #2591. FastMCP/MCPServer used to crash at registration with PydanticUserError when a tool's return type was a bare PEP 604 union like dict | list | str, because the types.UnionType was passed to create_model() as a field value. This already works on current main (734746a): such a union is neither a types.GenericAlias nor a type, so _try_create_model_and_schema (func_metadata.py) falls through to the catch-all branch that calls _create_wrapped_model(...) with wrap_output=True; #2434 (5cbd259) additionally guards the schema-generation path. But there is no test covering the reported signature — the existing test_structured_output_generic_types only exercises a 2-member, all-scalar union (func_union() -> str | int), not a container+scalar mix with bare (unparametrized) generics, which is a different branch interaction (the dict member touches the RootModel dict special-casing before the union wraps). The three prior fix attempts (#2592, #2599, #2669) were all closed during backlog cleanup rather than for an approach problem, and one of them switched the repro to typed generics to satisfy pyright — which masked the exact reported case. This PR keeps the bare-generic signature (with scoped # type: ignore) so the test reproduces the issue faithfully. Verification uv run pytest tests/server/mcpserver/test_func_metadata.py -q — 34 passed uv run ruff check tests/server/mcpserver/test_func_metadata.py — clean uv run ruff format --check tests/server/mcpserver/test_func_metadata.py — already formatted uv run pyright tests/server/mcpserver/test_func_metadata.py — 0 errors Did NOT change: any source file. This is a test-only addition; the behavior it asserts already ships on main. Real behavior proof Behavior addressed: @mcp.tool() async def my_tool(flag: bool) -> dict | list | str registering without PydanticUserError, and producing a wrapped anyOf output_schema. Real environment: Python 3.12, uv run, branch off upstream/main 734746a. Command run: uv run python -c "..." invoking func_metadata(func, structured_output=True).output_schema for () -> dict | list | str. Captured output (the schema the new test asserts): { "properties": { "result": { "anyOf": [ {"additionalProperties": true, "type": "object"}, {"items": {}, "type": "array"}, {"type": "string"} ], "title": "Result" } }, "required": ["result"], "title": "func_pep604_unionOutput", "type": "object" } Regression test: tests/server/mcpserver/test_func_metadata.py::test_structured_output_pep604_union_return — asserts the exact wrapped anyOf shape above for the issue's signature. What was NOT tested: runtime conversion of an actual returned value through this model (the existing structured-output round-trip tests already cover the wrap path generically); this test is scoped to the schema-generation path that previously raised.
Open Graph Description: Summary Adds a regression test for tool functions whose return annotation is a multi-member PEP 604 union mixing container and scalar types (dict | list | str). Guards an already-fixed-but-unteste...
X Description: Summary Adds a regression test for tool functions whose return annotation is a multi-member PEP 604 union mixing container and scalar types (dict | list | str). Guards an already-fixed-but-unteste...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/pull/2906
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:f5949f3b-a4ee-c5ee-255a-315826972b29 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | A2AE:2F98EE:2F9E6A6:3DD3E24:6A5CCEFE |
| html-safe-nonce | 6426ac7de24b5d4a7a7c0b72a54304fb2f90b2410e47619bfc2f7f6192b7cab9 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBMkFFOjJGOThFRToyRjlFNkE2OjNERDNFMjQ6NkE1Q0NFRkUiLCJ2aXNpdG9yX2lkIjoiNDg1NTMzMTk5NDQyNjE5OTgwNiIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 9783c5bcb25089da7b11969cb5db7f15c8dec6ce9588ce0966923f620cd04bf4 |
| hovercard-subject-tag | pull_request:3888975591 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/modelcontextprotocol/python-sdk/pull/2906/files |
| twitter:image | https://avatars.githubusercontent.com/u/259807879?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/259807879?s=400&v=4 |
| og:image:alt | Summary Adds a regression test for tool functions whose return annotation is a multi-member PEP 604 union mixing container and scalar types (dict | list | str). Guards an already-fixed-but-unteste... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5290d7e14309ad1e76106a9c4237bd1041517e83ea182c8ab756752cb0c6940b |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/modelcontextprotocol/python-sdk git https://github.com/modelcontextprotocol/python-sdk.git |
| octolytics-dimension-user_id | 182288589 |
| octolytics-dimension-user_login | modelcontextprotocol |
| octolytics-dimension-repository_id | 862584018 |
| octolytics-dimension-repository_nwo | modelcontextprotocol/python-sdk |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 862584018 |
| octolytics-dimension-repository_network_root_nwo | modelcontextprotocol/python-sdk |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 9c975978430e9ad293956f2bbdaf153b1bd84a99 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width