modelcontextprotocol / modelcontextprotocol/python-sdk

Generated output schema uses Pydantic's validation shape while structured output uses its serialization shape

Ouverte
#3,100 2 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

bug needs confirmation P2 v1 v2
Langage dominant
Python
Étoiles
24.3k
Forks
4k
Merge moyen
1 j 1 h
PR mergées (30 j)
31

Description

Initial Checks
  • I confirm that I'm using the latest version of MCP Python SDK.
  • I confirm that I searched existing issues and pull requests before opening this issue.
Description

For a Pydantic return model whose validation and serialization shapes differ, the generated outputSchema describes the validation shape while structuredContent uses the serialization shape. The SDK therefore publishes an output schema that rejects its own generated structured result.

This is related to, but not a duplicate of, #1073 / #1099. That change aligned ordinary field aliases by serializing structured output with aliases. Split validation_alias / serialization_alias values still expose different validation and serialization shapes, and serialization-only fields such as computed_field reveal the same underlying mismatch.

Example Code
from __future__ import annotations

import asyncio
import json

from jsonschema import Draft202012Validator
from pydantic import BaseModel, ConfigDict, Field, computed_field

try:
    from mcp.server.mcpserver.tools.base import Tool
except ImportError:  # MCP Python SDK 1.x
    from mcp.server.fastmcp.tools.base import Tool


class AliasOutput(BaseModel):
    model_config = ConfigDict(extra="forbid", populate_by_name=True)
    value: int = Field(validation_alias="wireIn", serialization_alias="wireOut")


def alias_output() -> AliasOutput:
    return AliasOutput(value=1)


class ComputedOutput(BaseModel):
    model_config = ConfigDict(extra="forbid")
    value: int

    @computed_field
    @property
    def doubled(self) -> int:
        return self.value * 2


def computed_output() -> ComputedOutput:
    return ComputedOutput(value=1)


async def check(function: object) -> None:
    tool = Tool.from_function(function)
    converted = await tool.run({}, None, convert_result=True)
    structured = converted[1] if isinstance(converted, tuple) else converted.structured_content
    errors = [
        error.message
        for error in Draft202012Validator(tool.output_schema).iter_errors(structured)
    ]
    print(function.__name__)
    print("schema:", json.dumps(tool.output_schema, sort_keys=True))
    print("structured:", json.dumps(structured, sort_keys=True))
    print("schema_errors:", errors)


async def main() -> None:
    await check(alias_output)
    await check(computed_output)


asyncio.run(main())

Observed validator messages:

alias_output
schema: ... "wireIn" ...
structured: {"wireOut": 1}
schema_errors: ["Additional properties are not allowed ('wireOut' was unexpected)", "'wireIn' is a required property"]

computed_output
schema: ... "value" ...
structured: {"doubled": 2, "value": 1}
schema_errors: ["Additional properties are not allowed ('doubled' was unexpected)"]
Expected behavior

The generated outputSchema should describe the serialized structured output. Generating the output model schema in Pydantic serialization mode makes both witnesses conform: the alias schema uses wireOut, and the computed-field schema includes doubled.

Python & MCP Python SDK
  • Python: 3.13.13
  • MCP Python SDK: 1.28.1 (latest stable)
  • Also reproduced on current main: 2713b53b127afc094dc97d6067df9f69b647661c (2.0.0b2)
  • Pydantic: 2.13.4

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez par Tool.from_function dans l’implémentation des outils du serveur MCP, en utilisant les chemins d’importation indiqués pour les versions du SDK, et suivez la production de output_schema et structured_content. Reproduisez les exemples alias_output et computed_output avec Draft202012Validator. Le travail est terminé lorsque le schéma généré accepte les deux résultats structurés sérialisés, y compris wireOut et doubled.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
api, backend
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
Calme
Clarté
Clairement spécifiée
Accessibilité débutants
52/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.