Araçlar
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.
Araç, modelin çağırabildiği bir fonksiyondur.
Sıradan bir Python fonksiyonunun üstüne @mcp.tool() koyarak bir araç tanımlarsınız. API'nin tamamı bu.
İlk aracınız
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(query: str, limit: int) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r} (showing up to {limit})."
Yazdığınıza bir bakın. Şema yok, JSON yok, protokol yok; yalnızca bir fonksiyon. SDK ondan üç şey okur:
- Aracın adı fonksiyonun adıdır:
search_books. - Modelin gördüğü açıklama docstring'dir:
Search the catalog by title or author. - Modelin geçirmesine izin verilen argümanlar tür ipuçlarından gelir:
query: strvelimit: int.
Girdi şeması
SDK bu tür ipuçlarından bir JSON Schema üretir ve tools/list sırasında istemciye gönderir:
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"},
"limit": {"title": "Limit", "type": "integer"}
},
"required": ["query", "limit"],
"title": "search_booksArguments"
}
Hiçbirinin varsayılan değeri olmadığı için iki argüman da required içinde. Bunu birazdan düzelteceksiniz. (title anahtarları Pydantic'in ürettiği kalıntılardır; sözleşmeyi oluşturan şey özellikler, türleri ve required'dır.)
Tip
Tür ipuçları burada dokümantasyon değildir. Sözleşmenin ta kendisidir. Bir istemci "limit": "ten"
gönderirse SDK bunu, fonksiyonunuz daha çalışmadan reddeder.
Modele dönen sonuç
Aracı {"query": "dune", "limit": 5} ile çağırın; sonuç iki parçadan oluşur:
result.content # [TextContent(text="Found 3 books matching 'dune' (showing up to 5).")]
result.structured_content # {'result': "Found 3 books matching 'dune' (showing up to 5)."}
content, modelin okuduğu metindir. structured_content ise istemci uygulama için tür bilgisi taşıyan veridir. Dönüş türünü -> str olarak bildirdiğiniz için oradadır.
structured_content'i şimdilik dert etmeyin. Araçlarınızdan gerçek Python nesneleri döndürün, gerisi doğru şekilde halledilir; Yapılandırılmış çıktı sayfası tamamen bununla ilgili.
Deneyin
Sunucuyu MCP Inspector ile çalıştırın:
uv run mcp dev server.py
Yazdırdığı URL'yi açın, Tools sekmesine gidin ve search_books'u çağırın.
Inspector, zorunlu bir query metin alanı ve zorunlu bir limit sayı alanı içeren bir form gösterir. Bu formu tür ipuçlarınızdan oluşturdu. Diğer tüm MCP istemcileri de aynısını yapar.
İsteğe bağlı argümanlar
Bir parametreye varsayılan değer verin, zorunlu olmaktan çıkar. Hepsi bu. Bildiğiniz Python.
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(query: str, limit: int = 10) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r} (showing up to {limit})."
Şema da buna uyar:
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"},
"limit": {"default": 10, "title": "Limit", "type": "integer"}
},
"required": ["query"],
"title": "search_booksArguments"
}
limit, required listesinden çıktı ve "default": 10 kazandı. Onu göndermeyen bir istemci, tıpkı Python'da olacağı gibi 10 alır.
Field ile daha zengin şemalar
Tür ipuçları sizi epey ileri götürür, ancak bazen bir argümanı açıklamak ya da kısıtlamak istersiniz.
Türü Annotated içine sarın ve bir Pydantic Field ekleyin:
from typing import Annotated, Literal
from pydantic import Field
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(
query: Annotated[str, Field(description="Title or author to search for.")],
limit: Annotated[int, Field(ge=1, le=50, description="Maximum number of results.")] = 10,
genre: Literal["fiction", "non-fiction", "poetry"] | None = None,
) -> str:
"""Search the catalog by title or author."""
where = f" in {genre}" if genre else ""
return f"Found 3 books matching {query!r}{where} (showing up to {limit})."
Üç yeni şey var, hepsi parametrelerin üzerinde:
Field(description=...): modelin docstring'le birlikte okuduğu, argümana özel bir açıklama.Field(ge=1, le=50): sayısal sınırlar. Şemaya"minimum": 1, "maximum": 50olarak yansırlar.Literal["fiction", "non-fiction", "poetry"]: bir enum. Model yalnızca bunlardan birini seçebilir.
Check
Kısıtlamalar süs değildir. Aracı limit=999 ile çağırın; SDK, fonksiyonunuz çalışmadan önce
bir araç hatasıyla yanıt verir:
Input should be less than or equal to 50
Bu hata araç sonucu olarak modele geri döner; model onu okur ve geçerli bir değerle yeniden dener.
le=50 ifadesini bir kez yazdınız ve kendi kendini düzelten ajanları bedavaya elde ettiniz.
Info
FastAPI veya Pydantic kullandıysanız bunların hepsini zaten biliyorsunuz. Aynı Field,
aynı Annotated, aynı doğrulama. Burada MCP'ye özgü öğrenilecek hiçbir şey yok.
Parametre olarak model
Bir araç birkaç taneden fazla argüman aldığında bunları bir Pydantic modelinde toplayın:
from pydantic import BaseModel, Field
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
class Book(BaseModel):
title: str
author: str
year: int = Field(ge=1450, description="Year of first publication.")
@mcp.tool()
def add_book(book: Book) -> str:
"""Add a book to the catalog."""
return f"Added {book.title!r} by {book.author} ({book.year})."
Book şeması aracın girdi şemasının içine (bir $defs referansı olarak) yerleştirilir, model onu bir JSON nesnesi olarak doldurur ve fonksiyonunuz zaten doğrulanmış, .title, .author ve .year öznitelikleri olan gerçek bir Book örneği alır.
Dilediğiniz gibi karıştırabilirsiniz: model parametrelerinin yanında sıradan parametreler, iç içe modeller, model listeleri. Baştan sona Pydantic.
async def
Bir araç G/Ç yapıyorsa (bir API çağırıyor, dosya okuyor, veritabanı sorguluyorsa) onu async def olarak bildirin ve içinde await kullanın. SDK onu await eder.
Sıradan bir def araç da çalışır: SDK onu bir iş parçacığında çalıştırır, böylece sunucuyu asla engellemez.
Yapılandırılacak başka bir şey yok.
Adlar, başlıklar ve annotation'lar
SDK'nın çıkarsadığı her şeyi dekoratörde geçersiz kılabilirsiniz:
from mcp.server import MCPServer
from mcp.types import ToolAnnotations
mcp = MCPServer("Bookshop")
@mcp.tool(
title="Search the catalog",
annotations=ToolAnnotations(read_only_hint=True, open_world_hint=False),
)
def search_books(query: str) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r}."
title, arayüzler için insanların okuyabileceği bir addır. İstemcilersearch_booksyerine "Search the catalog" gösterir.annotations, istemci için davranışsal ipuçlarıdır:read_only_hint=True: bu araç hiçbir şeyi değiştirmez.open_world_hint=False: açık web üzerinde değil, kapalı bir şeyler kümesi (bu katalog) üzerinde çalışır.- Diğer ikisi,
destructive_hintveidempotent_hint, yazan bir aracı tanımlar: bir şeyi silebilir mi, ve onu iki kez çağırmak bir kez çağırmakla aynı şey mi? Spesifikasyon her ikisini de yalnızca salt okunur olmayan araçlar için tanımlar; bu yüzdensearch_booksüzerinde hiçbir şey ifade etmezler.
Kurallara uyan bir istemci bunları "bunu çalıştırmadan önce kullanıcıya sormam gerekir mi?" gibi kararlar vermek için kullanır. Bunlar ipucudur, güvenlik değil. Bir istemcinin bunlara uyacağına asla güvenmeyin.
Tip
Adı ve açıklamayı fonksiyon adından ve docstring'den türetmek istemiyorsanız @mcp.tool()
name= ve description= de kabul eder. Çoğu zaman türetmek istersiniz.
Özet
- Bir fonksiyonun üstündeki
@mcp.tool()onu araç yapar. Ad fonksiyondan, açıklama docstring'den gelir. - Tür ipuçları girdi şemasının ta kendisidir. Varsayılan değerler argümanları isteğe bağlı yapar.
Annotated[..., Field(...)]açıklama ve kısıtlama ekler;Literalenum ekler.- Yapılandırılmış bir "gövde" almanın yolu Pydantic model parametresidir.
- Hatalı argümanlar sizin yerinize reddedilir; hem de modelin okuyup toparlanabileceği bir hatayla.
- G/Ç için
async def, geri kalan her şey için sıradandef.
return ettiğiniz değerin başına neler geldiği Yapılandırılmış çıktı sayfasında.