Çok turlu istekler
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.
Bazen bir araç işini tek turda bitiremez. Yalnızca kullanıcıda olan bir şeye ihtiyaç duyar: bir seçim, bir onay, bir kimlik bilgisi.
2026-07-28 öncesinde sunucu bunu geri çağırarak elde ederdi: asıl isteği işlemenin tam ortasında istemciye kendi isteğini, örneğin bir elicitation (kullanıcıdan bilgi isteme) ya da bir örnekleme (sampling) çağrısını açardı. 2026-07-28 spesifikasyonu bu geri kanalı (back-channel) emekliye ayırıyor.
Bunun yerine sunucu döndürür.
Geri çağırmak yerine döndürme
Sunucu tools/call isteğini CallToolResult yerine bir InputRequiredResult ile yanıtlar. İşi iki alanı yapar:
input_requests: sunucunun hâlâ ihtiyaç duyduğu şeyler; anahtarları sunucunun seçtiği adlar olan bir dict. Her değer birElicitRequest, birCreateMessageRequestya da birListRootsRequestolur.request_state: opak bir token. İstemci yeniden denemede onu olduğu gibi geri yollar. Onu okuyan tek şey sunucunuzdur.
İstemci her isteği karşılar, ardından yanıtlarını input_responses içinde, token'ı da request_state içinde taşıyarak aynı aracı yeniden çağırır. Sunucunun eksiği artık tamamdır ve normal bir CallToolResult döndürür.
Protokolün tamamı bu. Her adım istemciden sunucuya giden sıradan bir istektir. Hiçbir şey ters yönde akmaz.
Sunucu tarafı
@mcp.tool() üzerinde bunu elle kurmanız nadiren gerekir: kullanıcıya soran (Elicit), istemcinin LLM'inden örnekleme yapan (Sample) veya kök dizinlerini (roots) listeleyen (ListRoots) bir bağımlılık bildirin; SDK InputRequiredResult'ı sizin yerinize döndürür. Bu biçim Bağımlılıklar sayfasının konusudur. İki biçim bir arada kullanılamaz: bir çağrının tek bir input_responses/request_state kanalı vardır, bu yüzden Resolve(...) parametreleri kullanan bir araç gövdesinden ayrıca InputRequiredResult döndüremez. Bildirilmiş bir InputRequiredResult dönüş türü kayıt sırasında reddedilir (InvalidSignature); bildirilmemiş olanı ise çağrıyı çalışma zamanında başarısız kılar. Elle kurulan biçim düşük seviyeli Server'dır; onun on_call_tool işleyicisi iki sonuç türünden herhangi birini döndürebilir:
from mcp.server import Server, ServerRequestContext
from mcp.types import (
CallToolRequestParams,
CallToolResult,
ElicitRequest,
ElicitRequestFormParams,
ElicitResult,
InputRequiredResult,
ListToolsResult,
PaginatedRequestParams,
TextContent,
Tool,
)
ASK_REGION = ElicitRequest(
params=ElicitRequestFormParams(
message="Which region should the database live in?",
requested_schema={
"type": "object",
"properties": {"region": {"type": "string"}},
"required": ["region"],
},
)
)
async def list_tools(ctx: ServerRequestContext, params: PaginatedRequestParams | None) -> ListToolsResult:
return ListToolsResult(
tools=[
Tool(
name="provision",
description="Provision a database. Asks which region to put it in.",
input_schema={
"type": "object",
"properties": {"name": {"type": "string"}},
"required": ["name"],
},
)
]
)
async def call_tool(ctx: ServerRequestContext, params: CallToolRequestParams) -> CallToolResult | InputRequiredResult:
answer = (params.input_responses or {}).get("region")
if not isinstance(answer, ElicitResult) or answer.content is None:
return InputRequiredResult(input_requests={"region": ASK_REGION}, request_state="provision-v1")
name = (params.arguments or {})["name"]
text = f"Provisioned {name!r} in {answer.content['region']}."
return CallToolResult(content=[TextContent(type="text", text=text)])
server = Server("Provisioner", on_list_tools=list_tools, on_call_tool=call_tool)
on_call_tool'un tür ipucu-> CallToolResult | InputRequiredResultşeklindedir. İkincisini döndürmek sunucu tarafı API'sinin tamamıdır.- İlk çağrıda
params.input_responsesdeğeriNone'dır; bu yüzden koruma koşulu devreye girer ve işleyici yanıtlamak yerine sorar. - Yeniden denemede, istemcinin gönderdiği
ElicitResult, sunucununinput_requestsiçinde kullandığı aynı anahtarın ("region") altında durur.
O dosyadaki geri kalan her şey (açık input_schema, elle kurulan CallToolResult) sıradan düşük seviyeli Server'dır ve Düşük seviyeli Server sayfasında anlatılır. Bu sayfa yalnızca ikinci dönüş türünü ekler.
Araçların ötesi
tools/call özel değildir: 2026-07-28 sürümünde bir sunucu prompts/get ve resources/read isteklerini de aynı şekilde yanıtlayabilir. MCPServer üzerinde bir @mcp.prompt() fonksiyonu (ya da bir @mcp.resource() şablon fonksiyonu) InputRequiredResult'ı kendisi döndürür ve yeniden denemenin yanıtlarını bağlamdan okur:
from mcp.server.mcpserver import Context, MCPServer
from mcp.server.mcpserver.prompts.base import UserMessage
from mcp.types import ElicitRequest, ElicitRequestFormParams, ElicitResult, InputRequiredResult
mcp = MCPServer("Briefing")
ASK_AUDIENCE = ElicitRequest(
params=ElicitRequestFormParams(
message="Who is the briefing for?",
requested_schema={
"type": "object",
"properties": {"audience": {"type": "string"}},
"required": ["audience"],
},
)
)
@mcp.prompt()
async def briefing(ctx: Context) -> list[UserMessage] | InputRequiredResult:
"""Draft a briefing tuned to its audience."""
answer = (ctx.input_responses or {}).get("audience")
if not isinstance(answer, ElicitResult) or answer.content is None:
return InputRequiredResult(input_requests={"audience": ASK_AUDIENCE})
return [UserMessage(f"Write a briefing for {answer.content['audience']}.")]
- İlk tur
InputRequiredResult'ı döndürür. Yeniden denemedectx.input_responsesyanıtları aynı anahtarlar altında tutar ve fonksiyon olağan sonucunu döndürür: burada prompt mesajları, bir şablon kaynak için kaynak içeriği. - Ayarladığınız bir
request_state, sunucudaki diğer her şey gibi, ağa çıkmadan önce mühürlenir ve geri geldiğinde doğrulanır; aşağıdakirequestState'i koruma bölümü mührün size ne sağladığını ve anahtarları ne zaman yapılandırmanız gerektiğini anlatır. - Bağımlılık biçimi uymadığında bir
@mcp.tool()fonksiyonu da sonucu aynı şekilde doğrudan döndürebilir. - Statik
@mcp.resource()fonksiyonları buna katılmaz:Contextalmazlar, dolayısıyla yeniden denemeyi hiçbir zaman okuyamazlar. Yalnızca şablon kaynaklar soru sorabilir. - Aşağıdaki nesil kuralları aynen geçerlidir: 2026 öncesi bir oturumda
InputRequiredResultdöndürmek, uyarının anlattığı aynı-32603hatasıdır.
İstemci tarafı
Döngüyü sizin yerinize Client çalıştırır.
Sunucunun isteyebileceği callback'leri (elicitation_callback, sampling_callback, list_roots_callback) kaydedin ve aracı çağırın. Bir InputRequiredResult geldiğinde Client, input_requests içindeki her girdiyi eşleşen callback'e yönlendirir, yanıtlar ve geri yollanan request_state ile yeniden dener ve bir CallToolResult dönene kadar devam eder:
from mcp import Client
from mcp.client import ClientRequestContext
from mcp.types import ElicitRequestParams, ElicitResult
async def handle_elicitation(context: ClientRequestContext, params: ElicitRequestParams) -> ElicitResult:
return ElicitResult(action="accept", content={"region": "eu-west-1"})
async def main() -> None:
async with Client("http://127.0.0.1:8000/mcp", elicitation_callback=handle_elicitation) as client:
result = await client.call_tool("provision", {"name": "orders"})
print(result.content)
- Bu
elicitation_callback, 2026 öncesi bir sunucunun geri kanal üzerinden gönderdiğielicitation/createisteğinin ulaşacağı callback'in aynısıdır.sampling/createMessageilesampling_callback,roots/listilelist_roots_callbackiçin de aynısı geçerlidir: 2026-07-28 sürümünde bağımsız sunucu->istemci RPC'leri artık yoktur, ancak birebir aynıElicitRequest/CreateMessageRequest/ListRootsRequestyükleriinput_requestsiçinde taşınır ve aynı üç callback'e yönlendirilir. Tek bir callback seti her iki nesle de hizmet verir. call_tooldüz birCallToolResultdöndürür. Aradaki turlar çağırana görünmez.get_promptveread_resourceaynı döngüyü yürütür.
Check
Callback'i kaydetmezseniz döngü ilk turda başarısız olur: SDK'nın yedek callback'i her
elicitation'ı bir hatayla yanıtlar ve call_tool, "Elicitation not supported" mesajıyla
MCPError fırlatır.
Döngü sınırlıdır. Varsayılan üst sınır Client(..., input_required_max_rounds=10) değeridir; bunu aştıktan sonra hâlâ InputRequiredResult döndüren bir sunucu call_tool'un hata fırlatmasına yol açar. Bir tur yalnızca request_state taşıyıp hiç input_requests taşımıyorsa Client yeniden denemeden önce kısa bir süre bekler (50 ms'den başlayıp iki katına çıkarak 250 ms tavanına ulaşır); böylece yalnızca "henüz bitmedi" diyen bir sunucu sürekli yoklanmaz.
Döngüyü kendiniz yürütme
Otomatik döngü tek süreçli bir istemci için yeterlidir. Şu durumlarda döngüyü kendiniz üstlenin:
- İstemciniz dağıtık yapıdaysa: soruyu kullanıcıya gösteren süreç
call_tool'u çağıran süreç değildir, bu yüzden yeniden denemeyi başka bir worker gönderir.request_state, kendi depolamanız üzerinden bu sınırın ötesine taşıdığınız kalıcı saklanabilir token'dır;input_responsesise karşı tarafın onunla birlikte geri gönderdiği şeydir. - Her turu incelemek istiyorsanız: her
input_requestsgirdisini loglamak ya da denetlemek, belirli istek türlerini reddetmek veya adımlar arasında kendi bekleme (backoff) stratejinizi uygulamak. - Tur sayısına değil gerçek süreye dayalı bir sınır istiyorsanız:
input_required_max_rounds'a güvenmek yerine kendi döngünüzüanyio.fail_after(...)içine sarın.
Alttaki oturuma inin; orada allow_input_required=True size birleşim türünü doğrudan verir:
from mcp import Client
from mcp.types import CallToolResult, ElicitRequest, ElicitResult, InputRequest, InputRequiredResult, InputResponse
def fulfil(request: InputRequest) -> InputResponse:
if not isinstance(request, ElicitRequest):
raise NotImplementedError(f"this client cannot answer a {request.method!r} request")
return ElicitResult(action="accept", content={"region": "eu-west-1"})
async def provision(client: Client, name: str) -> CallToolResult:
result = await client.session.call_tool("provision", {"name": name}, allow_input_required=True)
while isinstance(result, InputRequiredResult):
responses = {key: fulfil(request) for key, request in (result.input_requests or {}).items()}
result = await client.session.call_tool(
"provision",
{"name": name},
input_responses=responses,
request_state=result.request_state,
allow_input_required=True,
)
return result
client.session.call_tool(..., allow_input_required=True)dönüş türünüCallToolResult | InputRequiredResultolarak genişletir. Onu yeniden daraltanisinstancedenetimidir.request_stateartık sizin elinizde. Adımlar arasında onu bir yere yazın; konuşma yepyeni bir süreçten kaldığı yerden sürebilir.input_requestsiçindeki her girdi içininput_responsesiçine aynı anahtarla birInputResponsekoyarsınız. Kullanıcı arayüzünüzün yerifulfil'dir; buradaki, yanıtı sabit kodlar.- Her adımda aynı araç adı, aynı
arguments. Yeniden deneme yeni bir yöntem değil, asıl çağrının yeniden yapılmasıdır.
requestState'i koruma
Yukarıdaki her şey request_state'i bir yankı olarak ele alır; ağ üzerinde de bundan ibarettir. Ancak istemci onu adımlar arasında elinde tutar (süreçler arasında bir yere yazmak tam da önceki bölümün onayladığı şeydir), dolayısıyla geri gelen şey istemcinin sağladığı girdidir: değiştirilmiş, süresi dolmuş ya da bambaşka bir çağrıdan alınmış olabilir. Spesifikasyon, durumun yetkilendirmeyi, kaynak erişimini veya iş mantığını etkileyebildiği her yerde sunucuların bu durumun bütünlüğünü korumasını ve doğrulama başarısız olduğunda turu reddetmesini zorunlu kılar.
MCPServer onu varsayılan olarak korur. Her sunucu, süreç başlarken üretilen bir anahtar altında giden requestState'i mühürler ve gelen her yankıyı (çözümleyici durumunu da elle kurulan durumu da) doğrular. Hiçbir şey yapılandırmaz, düz metin yazar ve düz metin okursunuz; ağ üzerinde yalnızca opak, şifreli bir token taşınır.
Varsayılan anahtar süreçle birlikte doğar ve ölür; tek bir sürecin ötesine dağıtım yapmadan önce bilmeniz gereken tek şey budur:
from mcp.server.mcpserver import MCPServer, RequestStateSecurity
# Multi-instance or restart-surviving: one or more shared secret keys (>= 32 bytes each).
mcp = MCPServer("fleet", request_state_security=RequestStateSecurity(keys=[key]))
- Varsayılan (yapılandırma yok) tek bir sürece uygundur: stdio ya da tam olarak bir HTTP worker'ı. Başka bir worker'a, yük dengeleyici arkasındaki başka bir örneğe ya da yeniden başlatma sonrası aynı sunucuya düşen bir yeniden deneme, o sürecin elinde olmayan bir anahtarla mühürlenmiştir; istemci aşağıdaki sabit ret yanıtını alır ve akışa baştan başlamak zorundadır.
keys=[...], bir yeniden denemenin başka bir örneğe ulaşabildiği (çok worker'lıuvicorn, yük dengelemeli HTTP) ya da yeniden başlatmalardan sağ çıkması gerektiği her durumda zorunludur: her örnek, herhangi bir kardeşinin ürettiğini doğrular. Aynı mekanizma; üretilmiş bir anahtar yerine sizin gizli anahtarınız.- Kendi kriptografiniz için (örneğin bir KMS ya da mevcut bir token servisi)
keysyerineRequestStateSecurity(codec=...)geçirin; aşağıdaki Kendi kriptografinizi getirme bölümü sözleşmeyi anlatır.
Mührün taşıdıkları
Varsayılan da olsa yapılandırılmış da olsa, ağ üzerindeki requestState şifreli ve kimliği doğrulanmış bir token'dır. Kodunuz onu hiç görmez: işleyiciler ve çözümleyiciler düz metin yazar, düz metin okur (ctx.request_state); SDK çıkışta mühürler, girişte doğrular. Bütünlüğün ötesinde her token şunlara bağlanır:
- Bir zaman penceresi. Her tur yeni bir son kullanma süresiyle yeniden mühürler; bu yüzden
RequestStateSecurity(ttl=...)(varsayılan 600 saniye) akışın tamamını değil, tur başına düşünme süresini sınırlar. - Kimliği doğrulanmış principal. İstek, SDK'nın doğruladığı bir OAuth erişim token'ı taşıdığında durum, token'ın istemcisine, yayımcısına (issuer) ve öznesine (subject) bağlanır: bir kullanıcı için üretilmiş durum, iki kullanıcı aynı OAuth istemcisini paylaşsa bile başka bir kullanıcı altında başarısız olur. Özne sağlamayan bir doğrulayıcı, bağlamayı yalnızca istemci kimliğine indirger; URL tabanlı istemci kimliklerinde bu kimliği o istemci yazılımının tüm kullanıcıları paylaşır. Kimlik doğrulama SDK dışında sonlandırıldığında (öndeki bir vekil sunucu) ya da aktarımda kimlik doğrulama yoksa bağlanacak bir principal yoktur ve
RequestStateSecurity(bind_principal=...)kendi kimlik sinyalinizden bir tane sağlamadıkça bu denetim etkisizdir. Token doğrulayıcınız hangi bileşenleri sağlıyorsa bunları tutarlı biçimde sağlamalıdır: bazı isteklerde özneyi ekleyip bazılarında atlayan bir doğrulayıcı, principal'ı akışın ortasında değiştirir ve süren turlar reddedilir. - Kaynaklandığı istek. Yöntem, araç ya da prompt adı (veya kaynak URI'si) ve argümanların bir özeti (digest). Başka bir araca, başka argümanlara ya da başka bir yönteme karşı yeniden oynatılan bir token başarısız olur.
- Sorulan sorunun ta kendisi. Her çözümleyici yanıtı, hem ilk geldiği turda hem de kaydedilmiş bir yanıt sonradan yeniden kullanıldığında, istemciye gösterilen oluşturulmuş soruya sabitlenir. Mesajı yeniden yazılmış ya da şeması değişmiş bir sürümü dağıtırsanız sunucu bayat bir yanıtı tüketmek yerine yeniden sorar. Aynı sabitleme ters yönde de işler: mesajları çağrıya özgü verilerden değil, aracın argümanlarından türetin. Bir zaman damgasından ya da canlı bir kurdan kurulan mesaj her turda farklı oluşur; bu yüzden kaydedilmiş her yanıt bayat görünür ve sunucu, istemcinin tur sınırı çağrıyı sonlandırana kadar yeniden sorar.
Bunların hepsi SDK'nın işidir; sizin değil, kendinizinkini getirseniz bile codec'in de değil.
Anahtar rotasyonu
Yeni durumu keys[0] mühürler; listedeki her anahtar doğrular. Kesintisiz rotasyon, her biri bir sonrakinden önce tamamen yayılmış üç aşamadan oluşur:
RequestStateSecurity(keys=[OLD, NEW]) # 1: every instance learns to verify NEW; OLD still mints
RequestStateSecurity(keys=[NEW, OLD]) # 2: NEW mints; in-flight OLD state keeps verifying
RequestStateSecurity(keys=[NEW]) # 3: one ttl after phase 2 is fully out, retire OLD
Asla önce üreten anahtarı terfi ettirmeyin: bazı örneklerin henüz doğrulayamadığı bir anahtarla üretmek, yayılımın ortasında süren turları düşürür.
Anahtarların kapsamı tek bir servistir. Mühürlü zarf ayrıca sunucunun adını bir audience claim'i olarak taşır; bu yüzden tesadüfen aynı gizli anahtarı paylaşan başka bir servisin ürettiği token zaten reddedilir. Claim ancak ad kadar ayırt edicidir; bu yüzden açık bir politika verilen sunucunun gerçek bir adı olmalı ya da RequestStateSecurity(audience=...) ayarlamalıdır: adsız bir sunucu oluşturulurken istisna fırlatır. audience= ayrıca bir servisin başka bir servisin ürettiği durumu kabul etmesi gereken, bilinçli kurulmuş çok servisli topolojilere de hizmet eder. (Yapılandırmasız varsayılan muaftır: anahtarı süreçten hiç çıkmaz, dolayısıyla audience claim'inin ekleyeceği bir şey yoktur.)
Kendi kriptografinizi getirme
RequestStateSecurity(codec=...), seal(bytes) -> str ve unseal(str) -> bytes yöntemleri olan ve kendisinin üretmediği her token için InvalidRequestState fırlatan herhangi bir şeyi kabul eder. Klasik biçim, bir KMS üzerinden zarf şifrelemedir: başlangıçta bir veri anahtarını bir kez açar ve token başına kriptografiyi yerel tutarsınız:
import os
from cryptography.exceptions import InvalidTag
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from mcp.server import MCPServer
from mcp.server.mcpserver import InvalidRequestState, RequestStateSecurity
PREFIX = "kms1." # format version; fed to GCM as associated data, so it is bound under the tag
def unwrap_data_key() -> bytes:
"""One KMS call at process start, kms.decrypt(CiphertextBlob=...); every token after that is local crypto."""
return os.urandom(32) # stand-in for the unwrapped 32-byte data key
class EnvelopeCodec:
def __init__(self, data_key: bytes) -> None:
self._aesgcm = AESGCM(data_key)
def seal(self, payload: bytes) -> str:
nonce = os.urandom(12)
return PREFIX + (nonce + self._aesgcm.encrypt(nonce, payload, PREFIX.encode())).hex()
def unseal(self, token: str) -> bytes:
if not token.startswith(PREFIX):
raise InvalidRequestState("unknown token format")
body = token[len(PREFIX) :]
try:
raw = bytes.fromhex(body)
if raw.hex() != body: # only the exact string seal() produced verifies
raise ValueError("non-canonical hex")
return self._aesgcm.decrypt(raw[:12], raw[12:], PREFIX.encode())
except (ValueError, InvalidTag) as exc:
raise InvalidRequestState("token failed verification") from exc
mcp = MCPServer("Deployer", request_state_security=RequestStateSecurity(codec=EnvelopeCodec(unwrap_data_key())))
TTL, principal bağlama ve istek bağlama codec'in işi değildir: SDK bunları her codec için seal'den önce yüke damgalar ve unseal'den sonra yeniden doğrular. Bir codec'in tek yükümlülüğü bütünlük (kurcalanmışsa istisna fırlatmak) ve ideal olarak gizliliktir.
Doğrulama başarısız olduğunda
Gelen her başarısızlık (kurcalanmış, süresi dolmuş, başka bir isteğe ya da principal'a karşı yeniden oynatılmış veya bu sunucunun bilmediği bir anahtarla mühürlenmiş olsun) aynı yanıtı alır:
{"code": -32602, "message": "Invalid or expired requestState"}
Her neden için tek bir sabit mesaj; böylece ağ üzerinden hangi denetimin başarısız olduğu asla açığa çıkmaz, gerçek neden sunucu log'una gider. tools/call, prompts/get ve resources/read üzerinden gelen her requestState denetlenir; hiç durum üretmeyen bir işleyiciye gelen de buna dahildir. Pratikte en sık görülen ret bir saldırgan değildir: varsayılan, sürece özel anahtarın bir yeniden başlatma öncesinden ya da başka bir örnekten gelen bir yeniden denemeyle karşılaşmasıdır. İstemci akışı yeniden başlatır; bu önemli olduğunda çözüm keys=[...] kullanmaktır.
Elle kurulan durum
Kendiniz ayarladığınız bir request_state'i (bir araç, prompt ya da kaynak şablonu fonksiyonundan InputRequiredResult döndürerek), çözümleyici durumunu işleyen aynı mekanizma tek satır kod değişmeden mühürler ve doğrular: düz metin yazın, düz metin okuyun; yukarıdaki her bağlama geçerlidir.
SDK'nın, yapılandırılmış olsa bile sizin yerinize sabitleyemeyeceği tek şey soru kimliğidir: durumunuzdaki bir yanıtın sizin sorularınızdan hangisine ait olduğunu bilmez. Yanıtları soruya göre anahtarlayarak saklıyorsanız duruma kendi soru tanımlayıcınızı ekleyin ve yeniden denemede onu denetleyin.
Düşük seviyeli Server hiçbir şeyin hazır gelmediği katmandır: MCPServer'ın aksine, sınırı kendiniz ekleyene kadar hiçbir şey mühürlenmez ve bunu yapana kadar request_state ağ üzerinden tam yazıldığı gibi geçer. Tek satırlık katılım Düşük seviyeli Server sayfasında gösterilir.
2026-07-28 sürümüne özgü bir sonuç
InputRequiredResult yalnızca 2026-07-28 protokol sürümünde vardır. Bellek içi Client(server) onu sizin yerinize anlaşarak belirler; ağ üzerinde mode="auto" keşfeder. Bağlandıktan sonra client.protocol_version size ne elde ettiğinizi söyler.
Warning
2026 öncesi bir oturumda InputRequiredResult koyacak bir yer yoktur. mode="legacy" bir
bağlantıda işleyicinizden bir tane döndürürseniz çalıştırıcı onu anlaşılan sürüme
serileştiremez; istemciye -32603 "Handler returned an invalid result" hatası döner. Her iki
nesle de hizmet veren bir sunucu, ona el atmadan önce ctx.protocol_version değerini
denetlemelidir.
Info
URL kipinde elicitation, 2026 bağlantısında tam olarak bu mekanizmayı kullanır.
input_requests içindeki girdi, parametreleri ElicitRequestURLParams olan bir
ElicitRequest'tir; kullanıcı bant dışı akışı tamamlar ve istemciniz çağrıyı yeniden dener.
Aynı döngü, yeni API yok. Üst seviyeli sunucu tarafı Elicitation
sayfasındadır.
Özet
- 2026-07-28 sürümünde, çağrının ortasında girdiye ihtiyaç duyan bir sunucu
InputRequiredResultdöndürür. İstemciye asla istek açmaz. input_requestsihtiyaç duyduklarıdır.request_stateyalnızca sunucunun okuduğu opak bir devam token'ıdır.- Yeniden deneme döngüsünü
Clientsizin yerinize çalıştırır:elicitation_callback/sampling_callback/list_roots_callbackkaydedin,call_tooldüz birCallToolResultdöndürür.input_required_max_rounds(varsayılan 10) onu sınırlar. - Turları incelemek ya da kalıcı saklamak için
client.session.call_tool(..., allow_input_required=True)kullanın vewhile isinstance(result, InputRequiredResult)döngüsünü kendiniz üstlenin. @mcp.tool()üzerinde, kullanıcıya soran bir bağımlılık bu sonucu sizin yerinize üretir (Bağımlılıklar); elle kurulan biçim düşük seviyeliServer'dır.- Prompt'lar ve kaynaklar da katılır: bir
@mcp.prompt()ya da şablon@mcp.resource()fonksiyonuInputRequiredResult'ı kendisi döndürür ve yeniden denemedectx.input_responses'ı okur. requestStateistemcinin sağladığı girdi olarak geri gelir; bu yüzdenMCPServeronu (çözümleyici durumunu da elle kurulan durumu da) varsayılan olarak sürece özel bir anahtar altında mühürler. Çok örnekli dağıtımlar, her örneğin bir kardeşinin ürettiğini doğrulayabilmesi içinRequestStateSecurity(keys=[...])(ya da özel bir codec) geçirir. Mühür her token'ı bir zaman penceresine, kaynaklandığı isteğe ve (istek SDK'nın doğruladığı kimlik doğrulama bilgisini taşıdığında ya dabind_principal=kendi kimlik sinyalinizi sağladığında) kimliği doğrulanmış principal'a bağlar (requestState'i koruma).
Sunucunun başlattığı örneklemenin ve itme tarzı geri kanalın geri kalanının yerini alan mekanizma budur; Kullanım dışı özellikler sayfasına bakın.