İstemci
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.
Client, bir Python programının bir MCP sunucusuyla konuşmasını sağlayan nesnedir.
Tek bir yaşam döngüsü olan tek bir nesnedir: oluşturun, async with bloğuna girin, yöntemleri çağırın. Her protokol fiili (araçları listeleme, birini çağırma, bir kaynağı okuma, bir prompt'u oluşturma) bu nesne üzerinde, türü belirli bir sonuç döndüren bir async yöntemdir.
İlk istemciniz
from mcp import Client
from mcp.server import MCPServer
mcp = MCPServer("Bookshop", instructions="Search the catalog before recommending a book.")
@mcp.tool()
def search_books(query: str) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r}."
async def main() -> None:
async with Client(mcp) as client:
print(client.server_info)
print(client.server_capabilities)
print(client.protocol_version)
print(client.instructions)
Üstteki sunucu yalnızca bağlanacak bir şeyiniz olsun diye orada. İstemci, vurgulanan beş satırdan ibaret.
Client(mcp)çağrısına sunucu nesnesinin kendisi verilir. Bu, bellek içi aktarımdır: alt süreç yok, port yok, HTTP yok. Bu sayfadaki her örnek ve yazdığınız her test böyle bağlanır.async withyaşam döngüsüdür. Bloğa girdiğinizde bağlantı kurulur ve anlaşma yapılır; çıktığınızda bağlantı kesilir.connect()/close()çifti yoktur ve blok bittikten sonra birClientyeniden kullanılamaz.- Bloğun içinde bağlantı bilgileri düz özellikler olarak zaten hazırdır.
Client'a geçirebilecekleriniz
Client tek bir konumsal argüman alır ve aktarımı onun türünden çözümler:
- Bir
MCPServer(veya düşük seviyeliServer) örneği: süreç içinde bağlanır. - Bir URL dizesi (
Client("http://localhost:8000/mcp")): Streamable HTTP, yani üretim yolu. - Bir aktarım:
async with ... as (read, write)ile kullanabileceğiniz herhangi bir şey; örneğin bir alt süreci saranstdio_client(...).
Bu sayfadaki geri kalan her şey üçünde de aynıdır. Başlıklar, alt süreçler, zaman aşımları ve Transport protokolünün kendi sayfası var: İstemci aktarımları.
Bağlı bir istemcide bulunanlar
Bloğa girdiğiniz anda doldurulan dört salt okunur özellik:
client.server_info: sunucunun kimliği; kimlik bildirmeyen 2026 neslinden bir sunucu içinNone(python-sdk sunucuları varsayılan olarak bildirir). Buradaserver_info.name"Bookshop",server_info.versionise sunucu ne bildiriyorsa odur.client.server_capabilities: sunucunun neler yapabildiği (tools,resources,prompts,completions, ...). Sunucuda olmayan bir yetenekNoneolur.client.protocol_version: iki tarafın üzerinde anlaştığı protokol sürümü. Burada"2026-07-28".client.instructions: sunucununinstructions=dizesi; sunucu bir tane ayarlamadıysaNone.
Hiç protokol sürümü seçmediniz. Varsayılan olarak Client sunucuyu yoklar ve eski sunucularda klasik el sıkışmaya geri döner; böylece tek bir istemci her nesilden sunucuyla çalışır. Bunu denetlemeniz gerektiğinde ayrıntıların tamamı Protokol sürümleri sayfasında.
Tip
client.session, alttaki ClientSession'dır; düşük seviyeli kaçış kapısı.
Bu sayfadaki hiçbir şey için ona ihtiyacınız olmaz.
Araçları listeleme
from mcp import Client
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.tool(title="Search the catalog")
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})."
async def main() -> None:
async with Client(mcp) as client:
result = await client.list_tools()
for tool in result.tools:
print(tool.name)
print(tool.title)
print(tool.description)
print(tool.input_schema)
list_tools() bir ListToolsResult döndürür; araçlar .tools içindedir. Her biri, bir host'un modele vereceği eksiksiz tanımdır:
tool.name # 'search_books'
tool.title # 'Search the catalog'
tool.description # 'Search the catalog by title or author.'
tool.input_schema ise sunucunun fonksiyonun tür ipuçlarından türettiği JSON Schema'dır:
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"},
"limit": {"default": 10, "title": "Limit", "type": "integer"}
},
"required": ["query"],
"title": "search_booksArguments"
}
Bu şema, bir arayüzün argüman formu oluşturması için gereken her şeydir; bir modelin geçerli argümanlar üretmesi için gereken her şey de odur.
Tip
title isteğe bağlıdır; bu yüzden araçları bir insana gösteren arayüzün seçim yapması gerekir: varsa title,
yoksa name. from mcp.shared.metadata_utils import get_display_name tam olarak bunu yapar;
araçlar, kaynaklar, kaynak şablonları ve prompt'lar için.
Bir aracı çağırma
call_tool(name, arguments) aracı çalıştırır ve size bir CallToolResult geri verir.
from pydantic import BaseModel
from mcp import Client
from mcp.server import MCPServer
from mcp.types import TextContent
mcp = MCPServer("Bookshop")
class Book(BaseModel):
title: str
author: str
year: int
@mcp.tool()
def lookup_book(title: str) -> Book:
"""Look up a book by its exact title."""
if title != "Dune":
raise ValueError(f"No book titled {title!r} in the catalog.")
return Book(title="Dune", author="Frank Herbert", year=1965)
async def main() -> None:
async with Client(mcp) as client:
result = await client.call_tool("lookup_book", {"title": "Dune"})
for block in result.content:
if isinstance(block, TextContent):
print(block.text)
print(result.structured_content)
print(result.is_error)
Sunucunun lookup_book aracı bir Pydantic Book döndürür. İstemcinin gördüğü şudur:
result.content # [TextContent(type='text', text='{\n "title": "Dune",\n "author": "Frank Herbert",\n "year": 1965\n}')]
result.structured_content # {'title': 'Dune', 'author': 'Frank Herbert', 'year': 1965}
result.is_error # False
Tek dönüş değeri, okunacak üç şey. Her birinin tüketicisi farklı.
content: modelin okuduğu
content, içerik bloklarından oluşan bir list'tir ve bir içerik bloğu bir birleşim (union) türüdür: TextContent, ImageContent, AudioContent, ResourceLink veya EmbeddedResource. Bir araç farklı türlerden birkaç tane döndürebilir.
main'in block.text'e dokunmadan önce isinstance(block, TextContent) ile türü daraltmasının nedeni budur. isinstance dışında hiç .text olmadığına dikkat edin: tür denetleyicisi buna izin vermez, çünkü ImageContent'te .text değil .data vardır. Birleşim türü, bir aracın size ne gönderebileceği konusunda dürüsttür; kodunuz da öyle olmalı.
structured_content: uygulamanızın okuduğu
structured_content, aracın JSON olarak dönüş değeridir ve aracın bildirdiği output_schema ile eşleşir. Dize ayrıştırma yok, tahmin yürütme yok.
İkisi de varsa aynı şeyi bilerek iki kez söylerler: content model için, structured_content kod içindir. Yapılandırılmış yarının nereden geldiği ve nasıl denetleneceği Yapılandırılmış çıktı sayfasında.
is_error: aracın başarısız olup olmadığı
İstisna fırlatan bir araç, istemcinizde istisna fırlatmaz. is_error=True taşıyan sıradan bir sonuç olarak geri döner.
Check
lookup_book'tan "Solaris"'i isteyin (katalogda olmayan bir başlık); fonksiyon
ValueError fırlatır. Çağrı yine de normal biçimde döner:
result.is_error # True
result.content # [TextContent(type='text', text="Error executing tool lookup_book: No book titled 'Solaris' in the catalog.")]
result.structured_content # None
İstisnanın mesajı content'e düştü; model onu orada okuyup yeniden deneyebilir. Bu
kasıtlıdır: bir araç hatası çökme değil, konuşmanın bir parçasıdır. structured_content'e
güvenmeden önce her zaman is_error'a bakın.
Warning
is_error=True, kendi raise'inizden fazlasını kapsar. Sunucuda hiç olmayan bir araç isteyin
(call_tool("does_not_exist", {})); hiçbir şey fırlatılmaz. Aynı şekil geri gelir:
content'te Unknown tool: does_not_exist ile birlikte is_error=True. Bir Client yöntemi
yalnızca sunucu sonuç yerine bir JSON-RPC hatası ile yanıt verdiğinde MCPError fırlatır;
sunucunun hangisini ne zaman ürettiği Hataları ele alma sayfasında.
Kaynaklar
Kaynak fiilleri çift gelir: listelemenin iki yolu, okumanın tek yolu.
from mcp import Client
from mcp.server import MCPServer
from mcp.types import TextResourceContents
mcp = MCPServer("Bookshop")
@mcp.resource("catalog://genres")
def genres() -> list[str]:
"""The genres the catalog is organised by."""
return ["fiction", "non-fiction", "poetry"]
@mcp.resource("catalog://genres/{genre}")
def books_in_genre(genre: str) -> str:
"""Every title we stock in one genre."""
return f"3 books filed under {genre}."
async def main() -> None:
async with Client(mcp) as client:
listed = await client.list_resources()
print([resource.uri for resource in listed.resources])
templates = await client.list_resource_templates()
print([template.uri_template for template in templates.resource_templates])
result = await client.read_resource("catalog://genres/poetry")
for contents in result.contents:
if isinstance(contents, TextResourceContents):
print(contents.text)
list_resources()somut kaynakları, yani sabit URI'si olanları döndürür. Burada:['catalog://genres'].list_resource_templates()parametreli olanları döndürür. Burada:['catalog://genres/{genre}']. İki ayrı liste olmalarının nedeni, bir şablonun siz onu doldurana kadar okunabilir olmamasıdır.read_resource(uri)düz birstrURI alır ve ikisinde de çalışır:"catalog://genres/poetry"geçirin, sunucu onu şablonla eşleştirir.
read_resource, TextResourceContents veya BlobResourceContents öğelerinden oluşan bir liste olan contents döndürür. Araç içeriğiyle aynı fikir: isinstance ile daraltın, sonra .text'i (veya .blob'u) okuyun.
Bir istemciye bir kaynağın ne zaman değiştiği de bildirilebilir. 2025 neslinden bağlantılarda bu, subscribe_resource(uri) / unsubscribe_resource(uri) çiftidir; MCPServer'ın uygulamadığı bir yöntem çifti olduğundan, 2026-07-28 sürümündeki bağlantıda (bu fiillerin artık var olmadığı yerde) istek -32601, Method not found ile yanıtlanır. 2026'daki karşılığı, MCPServer'ın gerçekten sunduğu bir subscriptions/listen akışıdır (orada server_capabilities.resources.subscribe değeri True'dur) ve onu client.listen(...) ile tüketmek bu bölümün Abonelikler sayfasının konusudur.
Prompt'lar
from mcp import Client
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.prompt(title="Recommend a book")
def recommend(genre: str) -> str:
"""Ask for a recommendation in a genre."""
return f"Recommend one {genre} book from the catalog and say why."
async def main() -> None:
async with Client(mcp) as client:
listed = await client.list_prompts()
print(listed.prompts)
result = await client.get_prompt("recommend", {"genre": "poetry"})
for message in result.messages:
print(message.role, message.content)
list_prompts() size sunucunun neler sunduğunu ve her prompt'un neye ihtiyaç duyduğunu söyler:
prompt.name # 'recommend'
prompt.title # 'Recommend a book'
prompt.arguments # [PromptArgument(name='genre', required=True)]
get_prompt(name, arguments) onu oluşturur. Argümanlar sözlüğü str -> str biçimindedir: prompt argümanları her zaman dizedir. Sonuç messages'dır; her biri bir role ve bir content bloğu taşıyan PromptMessage öğelerinden oluşan bir liste:
message.role # 'user'
message.content # TextContent(type='text', text='Recommend one poetry book from the catalog and say why.')
Host bu mesajları doğrudan modele verir. Özelliğin tamamı bu.
Tamamlamalar
Tamamlama işleyicisi olan bir sunucu, kullanıcı yazdıkça prompt ve kaynak şablonu argümanlarını otomatik tamamlayabilir.
from mcp import Client
from mcp.server import MCPServer
from mcp.types import Completion, CompletionArgument, CompletionContext, PromptReference, ResourceTemplateReference
mcp = MCPServer("Bookshop")
GENRES = ["fiction", "non-fiction", "poetry"]
@mcp.prompt()
def recommend(genre: str) -> str:
"""Ask for a recommendation in a genre."""
return f"Recommend one {genre} book from the catalog and say why."
@mcp.completion()
async def complete_genre(
ref: PromptReference | ResourceTemplateReference,
argument: CompletionArgument,
context: CompletionContext | None,
) -> Completion | None:
return Completion(values=[genre for genre in GENRES if genre.startswith(argument.value)])
async def main() -> None:
async with Client(mcp) as client:
result = await client.complete(
ref=PromptReference(type="ref/prompt", name="recommend"),
argument={"name": "genre", "value": "p"},
)
print(result.completion.values)
ref, hangi prompt'u veya şablonu doldurduğunuzu söyler: birPromptReferenceya daResourceTemplateReference.argument,{"name": ..., "value": ...}biçimindedir: argüman ve kullanıcının şimdiye kadar yazdığı.
Yanıt result.completion.values içindedir. "p" yazın, sunucu ['poetry'] ile döner. Sunucu tarafı ve bir işleyicinin önerilerini daraltmak için önceden doldurulmuş diğer argümanları nasıl kullandığı Tamamlamalar sayfasında.
Sayfalama
Her list_* yöntemi bir cursor= anahtar sözcüğü alır ve her sonuç bir next_cursor taşır. next_cursor None olduğunda her şeyi almışsınız demektir.
from mcp import Client
from mcp.server import MCPServer
from mcp.types import Tool
mcp = MCPServer("Bookshop")
@mcp.tool()
def search_books(query: str) -> str:
"""Search the catalog by title or author."""
return f"Found 3 books matching {query!r}."
@mcp.tool()
def reserve_book(title: str) -> str:
"""Put a book on hold."""
return f"Reserved {title!r}."
async def main() -> None:
async with Client(mcp) as client:
tools: list[Tool] = []
cursor: str | None = None
while True:
page = await client.list_tools(cursor=cursor)
tools.extend(page.tools)
if page.next_cursor is None:
break
cursor = page.next_cursor
print([tool.name for tool in tools])
Bu döngü her sunucuya karşı doğrudur. MCPServer her şeyi tek sayfada döndürür; bu yüzden next_cursor None olur ve döngü bir kez çalışır. Çoğu kodun bunu hiç yazmamasının nedeni budur. Gerçekten sayfalayan sunucular ve imleçlerin uyduğu kurallar Sayfalama sayfasında.
Testlerde
Süreç ve port olmadan Client(mcp), sunucunuz için zaten bir test düzeneğidir.
Bunun için yapılmış tek bir kurucu bayrağı var: Client(mcp, raise_exceptions=True). Yalnızca bellek içi bağlantılarda etkisi olur; onu açıklayan ve bütün kalıbı onun etrafında kuran sayfa ise Test etme.
Özet
Client(x)bir sunucu nesnesine bellek içinden, bir URL dizesine Streamable HTTP üzerinden, geri kalan her şeye de bir aktarım aracılığıyla bağlanır.async withyaşam döngüsünün tamamıdır. İçindeserver_capabilitiesveprotocol_versionzaten doludur; sunucu sağladığındaserver_infoveinstructionsda öyle.list_tools()size her aracınname,title,descriptionveinput_schemadeğerlerini verir.call_tool()model içincontent, kodunuz içinstructured_contentveis_errordöndürür. İstisna fırlatan bir araç istisna değil, sonuçtur.contentblok türlerinin bir birleşimidir; okumadan önceisinstanceile daraltın.list_resources/list_resource_templates/read_resource,list_prompts/get_promptvecompletefiilleri tamamlar.- Her
list_*cursor=alır;next_cursorNoneolana kadar döngüye devam edin.
Bir sunucunun istemciden isteyebilecekleri ve bunları nasıl yanıtlayacağınız İstemci callback'leri sayfasında.