[Bug]: Inconsistent structured output wrapping and opt-out for typing.Dict and typing sequence generics in func_metadata
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
- #3772 par @chauvuusvn — fermée sans fusion
Évaluation
- Difficulté
- 2/5
- Temps estimé
- 1-3 heures
- Accessibilité débutants
- 82/100
Piste de recherche
Commence par src/mcp/server/mcpserver/utilities/func_metadata.py et lis _create_output_model() et func_metadata(), où le ticket signale les deux incohérences. Vérifie les tests existants d’introspection des types de retour et ajoute des tests pour typing.Dict, typing.List et typing.Tuple sans arguments de type, ainsi que pour leurs équivalents intégrés. Le travail est terminé lorsque typing.Dict sans arguments de type produit un schéma d’objet non enveloppé et que les séquences du module typing sans arguments de type renoncent à la sortie structurée, comme décrit.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
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
In src/mcp/server/mcpserver/utilities/func_metadata.py, return type introspection handles built-in types differently from their unsubscripted typing generic counterparts (typing.Dict, typing.List, typing.Tuple).
Issue 1: typing.Dict is forcibly wrapped in {"result": ...} instead of top-level object
When a tool returns dict or dict[str, Any], _create_output_model sets wrap_output = False and derives a top-level JSON object schema:
elif type_annotation is dict:
model = dict[str, Any]
However, when a tool is annotated with typing.Dict (unsubscripted), get_origin(typing.Dict) is dict while get_args(typing.Dict) is (). In _create_output_model():
if origin is dict:
args = get_args(type_expr)
if len(args) == 2 and args[0] is str:
model = type_expr
else:
# dict with non-str keys needs wrapping
model = _create_wrapped_model(func_name, original_annotation)
wrap_output = True
Because len(args) == 0 != 2, it falls through to else (intended only for non-str keys like dict[int, str]). As a result:
dict: treated as top-level JSON object (wrap_output = False)typing.Dict: unexpectedly wrapped into{"result": ...}(wrap_output = True)
Issue 2: typing.List and typing.Tuple bypass the opt-out gate
In func_metadata():
if structured_output is None and (return_type_expr in (list, tuple) or _returns_content(return_type_expr)):
return FuncMetadata(arg_model=arguments_model)
Because typing.List is not list (typing.List in (list, tuple) evaluates to False), tools annotated with -> typing.List or -> typing.Tuple fail to opt out and are forced through _create_output_model(), generating an unwanted wrapper schema { "result": typing.List }.
Example Code
import typing
from mcp.server.mcpserver.utilities.func_metadata import func_metadata
def tool_built_in_dict() -> dict:
return {"status": "ok"}
def tool_typing_dict() -> typing.Dict:
return {"status": "ok"}
def tool_built_in_list() -> list:
return ["a", "b"]
def tool_typing_list() -> typing.List:
return ["a", "b"]
meta_bi_dict = func_metadata(tool_built_in_dict)
meta_ty_dict = func_metadata(tool_typing_dict)
meta_bi_list = func_metadata(tool_built_in_list)
meta_ty_list = func_metadata(tool_typing_list)
# Inconsistent wrapping:
print("dict wrap_output:", meta_bi_dict.wrap_output) # False
print("typing.Dict wrap_output:", meta_ty_dict.wrap_output) # True (Expected: False)
# Inconsistent opt-out:
print("list schema:", meta_bi_list.output_schema) # None
print("typing.List schema:", meta_ty_list.output_schema) # {'type': 'object', 'properties': {'result': ...}} (Expected: None)
Proposed Fix
- In
_create_output_model():
elif origin is dict:
args = get_args(type_expr)
if len(args) == 2 and args[0] is str:
model = type_expr
elif len(args) == 0:
# Bare typing.Dict is equivalent to bare dict
model = dict[str, Any]
else:
model = _create_wrapped_model(func_name, original_annotation)
wrap_output = True
- In
func_metadata():
if structured_output is None and (
return_type_expr in (list, tuple, typing.List, typing.Tuple) or _returns_content(return_type_expr)
):
return FuncMetadata(arg_model=arguments_model)
Version
Python 3.10+, MCP Python SDK 2.x
- Langage dominant
- Python
- Étoiles
- 24.7k
- Forks
- 4.1k
- Merge moyen
- 18 h 27 min
- PR mergées (30 j)
- 15
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de modelcontextprotocol/python-sdk
-
bug v2
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
modelcontextprotocol/python-sdk#3811 ·
Les mainteneurs répondent en général sous 1 jour
-
bug v2
Difficulté 2/5 1-3 heures Accessibilité débutants 79/100
modelcontextprotocol/python-sdk#3810 ·
Les mainteneurs répondent en général sous 1 jour
-
bug v1 v2
Difficulté 2/5 1-3 heures Accessibilité débutants 79/100
modelcontextprotocol/python-sdk#3781 · 2 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
bug v2
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
modelcontextprotocol/python-sdk#3769 · 2 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
2026 Streamable HTTP rejects whitespace-padded Mcp-Name valuesPeut-être à nouveau libre Une pull request pour cette issue a été fermée sans être fusionnée. Ouvertebug spec-2026-07-28 v2
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
modelcontextprotocol/python-sdk#3763 · 4 commentaires ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de modelcontextprotocol/python-sdk
Issues similaires
-
DBFS append mode silently overwrites existing filesPeut-être pris @IlyaKhalafi l’a pris aujourd’hui. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 77/100
fsspec/filesystem_spec#2429 ·
Les mainteneurs répondent en général sous 1 jour
-
[Bug]: Retrieval rerank fallback statistics stay zero after provider failurePeut-être pris Une pull request liée à cette issue est ouverte ou déjà fusionnée. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
volcengine/OpenViking#7014 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
fedora-copr/copr#4600 ·
Les mainteneurs répondent en général sous 2 jours
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
ansys/pydpf-core#3620 ·
Les mainteneurs répondent en général sous 1 jour
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour