modelcontextprotocol / modelcontextprotocol/python-sdk
Tool input schemas carry a pydantic-derived title on every property
Personne n'a encore pris cette issue.
- Langage dominant
- Python
- Étoiles
- 24.3k
- Forks
- 4k
- Merge moyen
- 1 j 1 h
- PR mergées (30 j)
- 31
Description
Description
Every tool's inputSchema carries a title on every property, derived by pydantic from the field name. A parameter named exercise_id gets "title": "Exercise Id" — a restatement of the key it already sits under. Tool schemas are re-sent to the model on every request, so this is paid for in context on every turn.
I ran into this running a local 27B model against the wger MCP server, where context is genuinely scarce. Measuring its live tools/list:
| bytes | share | |
|---|---|---|
| whole payload, 49 tools | 43,710 | 100% |
| tool descriptions (prose) | 10,904 | 25% |
auto-derived title keys (297 of them) |
8,333 | 19% |
anyOf null-wrapping on optionals |
1,869 | 4% |
My agent is granted 43 of those tools, which is ~11,100 tokens of schema against a 32k window — 42% of the context gone before the first message, once the system prompt is counted. Roughly 2,000 of those tokens are titles.
Reproduction — any tool at all:
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("demo")
@mcp.tool()
def log_set(exercise_id: str, reps: int) -> str:
"""Log a set."""
return "ok"
inputSchema.properties is:
{
"exercise_id": {"title": "Exercise Id", "type": "string"},
"reps": {"title": "Reps", "type": "integer"}
}
I'd expect the titles not to be there, since they add nothing a model can act on that the property name doesn't already say.
GenerateJsonSchema has a hook for exactly this — field_title_should_be_set — and Tool.from_function already passes a custom generator elsewhere in the file, so it's a small change. Suppressing the automatic titles leaves an explicit Field(title=...) intact, which seems like the right line to draw: an explicit title is the author's choice, an auto-derived one is a default nobody asked for.
Two things I'd want a maintainer's call on before this is worth doing:
- Default or opt-in. Changing the default updates 12 test expectations in this repo (mostly
snapshot(...)intests/docs_src/), so it's visible. An opt-in flag onMCPServer(...)avoids that but adds public API. - Scope. Output schemas, prompts and resource templates generate titles the same way. Output schemas alone are another 61 titles in the payload I measured. Worth doing together, or separately?
Happy to open a PR if it's useful — I have the change and the test updates working locally against main, full suite green. Equally happy to leave it if you'd rather write it yourselves.
Disclosure: I used an AI agent to take the measurements and draft the change. The problem is one I actually hit, and I've read and can explain the result.
References
field_title_should_be_set— https://docs.pydantic.dev/latest/api/json_schema/#pydantic.json_schema.GenerateJsonSchema.field_title_should_be_set- Server measured — https://github.com/wger-project/mcp-server
Guide de contribution
Ouvrir 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.
Piste de recherche
Commencez par Tool.from_function et son générateur personnalisé existant, puis examinez le hook GenerateJsonSchema.field_title_should_be_set de Pydantic. Vérifiez les attentes des snapshots dans tests/docs_src/ et déterminez avec un maintainer si la modification ne concerne que les schémas d’entrée ou également d’autres schémas générés. La tâche est terminée lorsque les titres de propriétés automatiques sont supprimés, que les titres explicites de Field sont conservés et que les tests concernés sont mis à jour et passent.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- python
- Domaine
- api, backend
- Type d'issue
- Fonctionnalité
- Difficulté
- 3/5
- Temps estimé
- 1-2 jours
- Activité
- Active
- Clarté
- Plutôt claire
- Accessibilité débutants
- 52/100