Ana içeriğe geç

Yapılandırılmış çıktı

Makine çevirisi

Bu sayfa İngilizce dokümantasyondan otomatik olarak çevrildi; esas alınması gereken sürüm İngilizce sayfadır. Yanlış görünen bir şey varsa, nasıl bildireceğinizi Çeviriler sayfası açıklar.

Düz bir str döndüren bir araç, sonucu iki kez üretir: content içinde metin olarak ve structured_content içinde {"result": "..."} olarak.

Bu sayfa o ikinci kanalla ilgili: nereden geldiği, alabileceği her biçim ve SDK'nın onu nasıl tutarlı tuttuğu.

Kısaca: dönüş türü açıklaması (annotation) çıktı şemasıdır. Onu zaten yazdınız.

Çıktı şeması

server.py
from mcp.server import MCPServer

mcp = MCPServer("Weather")

READINGS = {"London": 17, "Cairo": 34, "Reykjavik": 4}


@mcp.tool()
def get_temperature(city: str) -> int:
    """Current temperature in a city, in whole degrees Celsius."""
    return READINGS[city]

Önemli olan satır imza: -> int.

Bu sayede SDK'nın tools/list sırasında gönderdiği araç, parametrelerinizden oluşturduğu girdi şemasının yanında (onu Araçlar sayfası anlatır) bir de output_schema taşır:

{
  "properties": {
    "result": {"title": "Result", "type": "integer"}
  },
  "required": ["result"],
  "title": "get_temperatureOutput",
  "type": "object"
}

Tek başına bir int JSON nesnesi değildir, bu yüzden SDK onu {"result": ...} içine sarar. Aracı çağırdığınızda iki kanal da dolar:

result.content             # [TextContent(text="17")]
result.structured_content  # {"result": 17}

Her skaler aynı sarmalayıcıyı alır: str, int, float, bool, bytes, None.

İki kanal

Neden aynı değer iki kez gönderiliyor?

  • content model içindir. Bir dil modeli metin okur; sonucun gördüğü tek kısmı budur.
  • structured_content, modelin içinde çalıştığı uygulama içindir: "17" geçen bir cümle değil, 17 isteyen kod.
  • output_schema ikisi arasındaki sözleşmedir ve araç daha hiç çağrılmadan yayımlanır.

Siz tek bir Python değeri döndürürsünüz. Üçünü de SDK doldurur.

Bir model döndürme

Biçimi bir Pydantic BaseModel olarak bildirin ve bir örneğini döndürün:

server.py
from pydantic import BaseModel, Field

from mcp.server import MCPServer

mcp = MCPServer("Weather")


class WeatherData(BaseModel):
    temperature: float = Field(description="Degrees Celsius.")
    humidity: float = Field(description="Relative humidity, 0 to 1.")
    conditions: str


@mcp.tool()
def get_weather(city: str) -> WeatherData:
    """Current weather for a city."""
    return WeatherData(temperature=16.2, humidity=0.83, conditions="Overcast")

Artık şema WeatherData'nın kendisi. Sarmalayıcı yok, result anahtarı yok:

{
  "properties": {
    "temperature": {"description": "Degrees Celsius.", "title": "Temperature", "type": "number"},
    "humidity": {"description": "Relative humidity, 0 to 1.", "title": "Humidity", "type": "number"},
    "conditions": {"title": "Conditions", "type": "string"}
  },
  "required": ["temperature", "humidity", "conditions"],
  "title": "WeatherData",
  "type": "object"
}

structured_content alan alan o nesnedir:

result.structured_content  # {"temperature": 16.2, "humidity": 0.83, "conditions": "Overcast"}

Model de dışarıda kalmaz. SDK aynı nesneyi content için JSON metnine serileştirir:

{
  "temperature": 16.2,
  "humidity": 0.83,
  "conditions": "Overcast"
}

temperature ve humidity üzerindeki Field(description=...) bilgisinin şemaya düştüğüne dikkat edin. Girdilerinizi tanımlayan aynı Field, çıktılarınızı da tanımlar.

Info

FastAPI'nin response_model'ını kullandıysanız bunu zaten biliyorsunuz: bildirilen yanıt olarak bir Pydantic modeli, sizin yerinize serileştirilir ve belgelenir. Tek fark, burada bildirimin tamamının dönüş açıklaması olmasıdır.

Bir TypedDict

Her biçim bir sınıfı hak etmez. Bir TypedDict aynı şemayı üretir:

server.py
from typing import TypedDict

from mcp.server import MCPServer

mcp = MCPServer("Weather")


class WeatherData(TypedDict):
    temperature: float
    humidity: float
    conditions: str


@mcp.tool()
def get_weather(city: str) -> WeatherData:
    """Current weather for a city."""
    return WeatherData(temperature=16.2, humidity=0.83, conditions="Overcast")

TypedDict çalışma zamanında düz bir dict'tir; siz de onu oluşturup döndürürsünüz. Şema, doğrulama ve structured_content, BaseModel sürümüyle birebir aynıdır (TypedDict'te yeri olmayan açıklamalar hariç).

Bir dataclass

Dataclass'lar da çalışır; öznitelikleri tür ipucu taşıyan herhangi bir sıradan sınıf da öyle. SDK arka planda açıklamalardan bir Pydantic modeli oluşturur.

server.py
from dataclasses import dataclass

from mcp.server import MCPServer

mcp = MCPServer("Weather")


@dataclass
class WeatherData:
    temperature: float
    humidity: float
    conditions: str


@mcp.tool()
def get_weather(city: str) -> WeatherData:
    """Current weather for a city."""
    return WeatherData(temperature=16.2, humidity=0.83, conditions="Overcast")

Üç yazım, tek şema. Kod tabanınızda hangisi varsa onu kullanın.

Listeler

Bir list[...] de JSON nesnesi değildir, bu yüzden {"result": ...} sarmalayıcısını alır; öğe türünüz içinde bir $defs başvurusu olarak yer alır:

server.py
from pydantic import BaseModel

from mcp.server import MCPServer

mcp = MCPServer("Weather")


class WeatherData(BaseModel):
    temperature: float
    humidity: float
    conditions: str


@mcp.tool()
def get_forecast(city: str, days: int) -> list[WeatherData]:
    """Daily forecast for a city."""
    return [WeatherData(temperature=16.2 + day, humidity=0.83, conditions="Overcast") for day in range(days)]
{
  "$defs": {
    "WeatherData": {
      "properties": {
        "temperature": {"title": "Temperature", "type": "number"},
        "humidity": {"title": "Humidity", "type": "number"},
        "conditions": {"title": "Conditions", "type": "string"}
      },
      "required": ["temperature", "humidity", "conditions"],
      "title": "WeatherData",
      "type": "object"
    }
  },
  "properties": {
    "result": {"items": {"$ref": "#/$defs/WeatherData"}, "title": "Result", "type": "array"}
  },
  "required": ["result"],
  "title": "get_forecastOutput",
  "type": "object"
}

İki günlük bir tahmin istediğinizde structured_content, {"result": [{...}, {...}]} olur. content ise öğe başına bir tane olmak üzere iki TextContent bloğuna dönüşür: liste, model için tek bir dizge olarak dökülmek yerine düzleştirilir.

tuple[...], union'lar ve Optional[...] aynı şekilde sarılır.

Sözlükler

dict[str, ...] zaten bir JSON nesnesi olan tek generic türdür, bu yüzden sarılmaz:

server.py
from mcp.server import MCPServer

mcp = MCPServer("Weather")

READINGS = {"London": 16.2, "Cairo": 34.1, "Reykjavik": 4.4}


@mcp.tool()
def get_temperatures(cities: list[str]) -> dict[str, float]:
    """Current temperature for each city, in degrees Celsius."""
    return {city: READINGS[city] for city in cities}
{
  "additionalProperties": {"type": "number"},
  "title": "get_temperaturesDictOutput",
  "type": "object"
}
result.structured_content  # {"London": 16.2, "Reykjavik": 4.4}

Anahtarlar str olmalıdır. Bir dict[int, float] JSON nesnesi olamaz, bu yüzden {"result": ...} sarmalayıcısına geri düşer.

Doğrulama

output_schema belgeleme değildir. Fonksiyonunuz ne döndürürse döndürsün, sunucudan çıkmadan önce ona göre doğrulanır.

Değeri elle oluşturduğunuz sürece bunu fark etmezsiniz: Pydantic, WeatherData'nızın bir WeatherData olduğundan zaten emin olmuştur. Bunu, verinin sizin denetlemediğiniz bir yerden geldiği gün fark edersiniz:

server.py
import json

from pydantic import BaseModel

from mcp.server import MCPServer

mcp = MCPServer("Weather")

UPSTREAM = {"London": '{"temperature": 16.2, "conditions": "Overcast"}'}


class WeatherData(BaseModel):
    temperature: float
    humidity: float
    conditions: str


@mcp.tool()
def get_weather(city: str) -> WeatherData:
    """Current weather for a city."""
    return json.loads(UPSTREAM[city])

Açıklama WeatherData vaat ediyor. Üst servisin yanıtı humidity göndermeyi bırakmış.

Check

get_weather'ı çağırdığınızda istemciye sessizce yarı boş bir nesne vermez. Çağrı başarısız olur ve hatanın ilk satırları alanın adını verir:

Error executing tool get_weather: 1 validation error for WeatherData
humidity
  Field required [type=missing, input_value={'temperature': 16.2, 'conditions': 'Overcast'}, input_type=dict]

Bu metin, is_error=True ile araç sonucu olarak geri döner; böylece model, var olmayan bir hava durumunu kendinden emin biçimde okumak yerine çağrının başarısız olduğunu bilir.

Bu arada, -> WeatherData bir araçtan düz bir dict döndürmek sorun değil. json.loads'un ürettiği tam olarak buydu. Doğrulama Python türüne değil, değere uygulanır.

Devre dışı bırakma

Bazen dönüş açıklaması protokol için değil, tür denetleyiciniz içindir. structured_output=False geçirin; araç yalnızca metin üretir:

server.py
from mcp.server import MCPServer

mcp = MCPServer("Weather")


@mcp.tool(structured_output=False)
def weather_report(city: str) -> str:
    """A human-readable weather report for a city."""
    return f"{city}: 17 degrees, overcast, light rain easing by evening."

output_schema yok, sarmalama yok, doğrulama yok. structured_content None'dır ve content döndürdüğünüz dizgedir.

Tersi olan structured_output=True, otomatik algılamayı bir zorunluluğa çevirir: dönüş türü şema üretemeyen bir araç, metne geri düşmek yerine içe aktarma anında istisna fırlatır.

Tür ipucu olmayan bir sınıf

İstemeden yapılandırılmamış sonuca varmanın bir yolu vardır: gövdesinde hiç açıklama olmayan bir sınıf döndürmek.

server.py
from mcp.server import MCPServer

mcp = MCPServer("Weather")


class Station:
    def __init__(self, name: str, online: bool):
        self.name = name
        self.online = online


@mcp.tool()
def get_station(name: str) -> Station:
    """Look up a weather station by name."""
    return Station(name=name, online=True)

Station, name ve online değerlerini __init__ içinde atar, ama sınıf hiçbir şey bildirmez. SDK sınıf açıklamalarını okur, hiçbir şey bulamaz ve vazgeçer.

Warning

Sessizce vazgeçer. output_schema None'dır, structured_content None'dır ve modelin okuduğu metin nesnenin repr'idir:

"<server.Station object at 0x7f539d75b230>"

Hata yok, uyarı yok, işe yaramaz bir araç. Açıklamaları sınıf gövdesine taşıyın ya da structured_output=True geçirin; bu, modül içe aktarıldığı anda durumu kesin bir hataya çevirir: Function get_station: return type <class 'server.Station'> is not serializable for structured output.

Tip

Tam denetim mi gerekiyor (CallToolResult'ı kendiniz oluşturmak ya da uygulamanın görüp modelin göremediği bir _meta eklemek)? Bunun yeri Düşük seviyeli Server.

Özet

  • Dönüş türü açıklaması çıktı şemasıdır. tools/list içinde output_schema olarak yayımlanır.
  • Skalerler, listeler, tuple'lar ve union'lar {"result": ...} içine sarılır. Modeller, TypedDict'ler, dataclass'lar, açıklamalı sınıflar ve dict[str, ...] zaten nesnedir ve oldukları gibi kalırlar.
  • Her sonuç hem content (metin, model için) hem de structured_content (veri, uygulama için) taşır.
  • Döndürdüğünüz şey şemaya göre doğrulanır. Uyuşmazlık bozuk bir sonuç değil, bir araç hatasıdır.
  • structured_output=False bir aracı devre dışı bırakır. Tür ipucu olmayan bir sınıf sessizce devre dışı kalır; buna dikkat edin.

Artık bir aracın geri söyleyebileceği her şeye hâkimsiniz. Sırada ikinci ilkel yapı var: Kaynaklar.