Bare `dict` / `list` / `tuple` return annotations silently produce no structured output
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 2/5
- Tempo estimado
- 1-3 horas
- Facilidade para iniciantes
- 72/100
- Tipo de issue
- Bug
- Clareza
- Claramente especificada
- Status de atividade
- Ativa
- Stack de tecnologia
- python
- Domínio
- backend-api-design
Direção de pesquisa
Comece lendo _create_output_model em src/mcp/server/mcpserver/utilities/func_metadata.py e a documentação sobre saída estruturada em docs/servers/structured-output.md. Reproduza a diferença entre anotações de contêiner sem tipo e tipadas e, em seguida, confira os testes existentes para esquemas de saída e conteúdo estruturado. O trabalho estará concluído quando as anotações simples de dict, list e tuple tiverem o comportamento esperado de saída estruturada e a documentação o explicar.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
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
A tool annotated with a bare dict (or list / tuple) gets no outputSchema and no structuredContent. The result only arrives as JSON text. -> dict[str, Any] works, and -> str gets {"result": ...}. With structured_output=True the bare annotation raises InvalidSignature: Function f: return type <class 'dict'> is not serializable for structured output, which reads as if a dict weren't serializable.
Expected: -> dict behaves like dict[str, Any], and -> list / -> tuple like list[Any] (wrapped in {"result": ...}). Failing that, the structured-output docs could say that bare containers opt out.
Cause: _create_output_model sends bare dict / list / tuple to the "other class types" case. get_type_hints(dict) is empty, so the model stays None: the silent opt-out documented for classes without type hints, which isn't obvious for builtins. We hit it while building an MCP server: our tools were -> dict, and our client code expected structured_content that was always None.
Proposed fix: a case before the class fallback that sends bare dict down the dict[str, ...] path and wraps bare list / tuple like their generic forms, plus a test and one sentence in docs/servers/structured-output.md. It's about 10 lines of code, and the full suite passes (6206 passed). One behaviour change to weigh: existing bare -> dict / -> list tools would start publishing an outputSchema and validating their results.
I'd like to send this as a PR if you're open to it. It's on a branch: https://github.com/g-s-jithesh/python-sdk/commit/6ecfa04ece6032816c7d3a087fd8c841cb0c43e1
I used an AI coding assistant (Claude Code) to help investigate this and draft the fix. I reproduced the bug myself and reviewed the change.
Example Code
import asyncio
from typing import Any
from mcp import Client
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("repro")
@mcp.tool()
def bare() -> dict:
return {"id": 1}
@mcp.tool()
def typed() -> dict[str, Any]:
return {"id": 1}
async def main() -> None:
async with Client(mcp) as client:
for name in ("bare", "typed"):
print(name, (await client.call_tool(name, {})).structured_content)
asyncio.run(main())
# bare None
# typed {'id': 1}
Python & MCP Python SDK
Python 3.14, Windows 11; mcp 2.3.0, and main at 9f66f37 (src/mcp/server/mcpserver/utilities/func_metadata.py, _create_output_model)
- Linguagem predominante
- Python
- Estrelas
- 24.5k
- Forks
- 4k
- Merge médio
- 18h 28min
- PRs com merge (30d)
- 38
Preparar o ambiente
- Sem Dockerfile nem arquivo Docker Compose
- Tem um modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de modelcontextprotocol/python-sdk
-
2026-07-28 SSE responses share one header list, so in-place middleware appends leak across responsesTalvez livre de novo Um pull request para esta issue foi fechado sem ser mesclado. Abertabug spec-2026-07-28 v2
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 68/100
modelcontextprotocol/python-sdk#3701 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
bug v1 v2
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
modelcontextprotocol/python-sdk#3698 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Streamable HTTP: Content-Type handling is order dependent and accepts invalid valuesTalvez já em andamento @jayzuccarelli assumiu há 5 dias. Abertabug v1 v2
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
modelcontextprotocol/python-sdk#3691 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Skills: URI traversal validation skips percent-encoded dot-segmentsTalvez já em andamento @Varshith-Kali assumiu há 4 dias. Abertabug v2
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
modelcontextprotocol/python-sdk#3685 · 2 comentários ·
Mantenedores costumam responder em até 1 dia
-
Type annotation gaps and unhandled FileNotFoundError in src/mcp/cli/cli.pyTalvez livre de novo Um pull request para esta issue foi fechado sem ser mesclado. Abertaenhancement
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 84/100
modelcontextprotocol/python-sdk#3673 · 4 comentários ·
Mantenedores costumam responder em até 1 dia
Todas as issues de modelcontextprotocol/python-sdk
Issues semelhantes
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
ok-oldking/ok-wuthering-waves#1785 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
healthchecks/healthchecks#1349 ·
-
deployment release-lag
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 68/100
nolte/kamerplanter#2374 ·
Mantenedores costumam responder em até 1 dia
-
batchterms() drops queries without a similar() clause, misaligning results with inputsTalvez já em andamento @MohammadHijjawi97 assumiu hoje. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 86/100
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 77/100
lidofinance/diffyscan#239 ·
Mantenedores costumam responder em até 1 dia