Commit 833a41f
fix(mcpserver): omit unreturned TypedDict keys from structuredContent
A tool annotated to return a TypedDict with `NotRequired` keys (or
`total=False`) published an outputSchema declaring those properties
non-nullable, while `convert_result` materialised every key the tool did
not return as an explicit `null`. The response therefore violated the
schema the same server had just advertised, and a conforming client -- the
SDK's own included -- rejected the call:
RuntimeError: Invalid structured content returned by tool get_person:
None is not of type 'string'
That made every such tool unusable, even though TypedDict is one of the
return shapes structured output documents. The unstructured text block and
structuredContent also disagreed, so a client skipping validation saw two
different answers.
`_create_model_from_typeddict` gives non-required keys a `None` default to
make them optional on the generated model; the file's own comment already
noted that such a model "should use exclude_unset=True when dumping to get
TypedDict semantics", but nothing did. Mark those models with a private
base class and honour it in `convert_result`, so an omitted key stays
omitted.
Scoped deliberately to TypedDict-derived models: `exclude_unset` applied
to every output shape would change payloads for BaseModel, dataclass and
wrapped returns, where emitting defaults is correct.
The published outputSchema is unchanged, so no existing expectation moves.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent a4f4ccd commit 833a41f
2 files changed
Lines changed: 81 additions & 4 deletions
File tree
- src/mcp/server/mcpserver/utilities
- tests/server/mcpserver
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
139 | 139 | | |
140 | 140 | | |
141 | 141 | | |
142 | | - | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
143 | 147 | | |
144 | 148 | | |
145 | 149 | | |
| |||
494 | 498 | | |
495 | 499 | | |
496 | 500 | | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
497 | 512 | | |
498 | 513 | | |
499 | 514 | | |
| |||
507 | 522 | | |
508 | 523 | | |
509 | 524 | | |
510 | | - | |
| 525 | + | |
| 526 | + | |
511 | 527 | | |
512 | 528 | | |
513 | 529 | | |
514 | 530 | | |
515 | | - | |
| 531 | + | |
516 | 532 | | |
517 | 533 | | |
518 | 534 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| |||
811 | 812 | | |
812 | 813 | | |
813 | 814 | | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
| 874 | + | |
814 | 875 | | |
815 | 876 | | |
816 | 877 | | |
| |||
0 commit comments