Sayfalama
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.
Çoğu sunucunun buna hiç ihtiyacı olmaz.
MCPServer, her list_* isteğini elindeki her şeyle, tek sayfada, next_cursor=None ile yanıtlar. Birkaç düzine araç, kaynak veya prompt için doğru yanıt budur ve yapılandıracak bir şey yoktur.
Sayfalama, kaynak listesi aslında bir veritabanı olan sunucu içindir: tek yanıtta serileştirmeyi reddettiği binlerce satır. Protokolün buna yanıtı imleçtir (cursor): sunucu bir sayfa ile birlikte opak bir token döndürür, istemci de sonraki sayfayı almak için bu token'ı geri gönderir.
@mcp.resource()'ta bunların hiçbiri için bir kanca yoktur. Sayfalamak için liste işleyicisini düşük seviyeli Server üzerinde kendiniz yazarsınız.
Sayfalayan bir sunucu
from typing import Any
from mcp.server import Server, ServerRequestContext
from mcp.types import ListResourcesResult, PaginatedRequestParams, Resource
BOOKS = [f"book-{n}" for n in range(1, 101)]
PAGE_SIZE = 10
async def list_books(ctx: ServerRequestContext[Any], params: PaginatedRequestParams | None) -> ListResourcesResult:
start = 0 if params is None or params.cursor is None else int(params.cursor)
end = start + PAGE_SIZE
page = [Resource(uri=f"books://catalog/{name}", name=name) for name in BOOKS[start:end]]
next_cursor = str(end) if end < len(BOOKS) else None
return ListResourcesResult(resources=page, next_cursor=next_cursor)
server = Server("Bookshop", on_list_resources=list_books)
- Düşük seviyeli bir
Server'da işleyiciler dekoratör değil, kurucu argümanlarıdır.on_list_resourcesherresources/lististeğini yanıtlar; bağlantının tamamı bu. - Sayfalanan her işleyicinin türü
params: PaginatedRequestParams | None'dır ve örnek ikisini de kabul eder. Ancak bir bağlantı üzerinden SDK size hiçbir zamanNonevermez (paramsüyesi olmayan bir istek, işleyiciye varsayılan değerleriyle model olarak ulaşır); bu yüzden önemli olan sinyalparams.cursor is None'dır: en baştan başla. - Bir imlecin ne olduğuna siz karar verirsiniz. Burada dizge olarak yazılmış bir ofsettir. Bir zaman damgası, bir birincil anahtar, bir base64 blob'u: çıkışta üretebileceğiniz ve dönüşte tanıyabileceğiniz herhangi bir şey.
next_cursor=None, "bu son sayfaydı" demenin yoludur. Sayaç yok, toplam yok,has_moreyok. Sinyalin tamamıNone'dır.
Tip
10'luk bir PAGE_SIZE örneği okunur kılar. Kendinizinkini endpoint başına seçin:
tek satırlık kaynaklardan oluşan bir liste 500'lük bir sayfayı kaldırır; şişkin prompt
şablonlarından oluşan bir liste kaldıramaz. İstemcinin bu konuda söz hakkı yoktur ve bu bilinçli bir tasarımdır.
Deneyin
Client(server), düşük seviyeli bir Server'a bellek içinde, bir MCPServer'a bağlandığı gibi bağlanır.
list_resources()'ı argümansız çağırın. book-1'den book-10'a kadar on kaynak alırsınız ve next_cursor, "10" dizgesidir.
Bunu list_resources(cursor="10") ile geri verin; ilk kaynak book-11, yeni next_cursor ise "20" olur.
Onuncu sayfa, next_cursor değeri None olarak döner. Bitti.
İstemci döngüsü
Client üzerindeki her list_* metodu (list_tools, list_resources, list_resource_templates, list_prompts) bir cursor= anahtar kelimesi alır. Sayfalanmış bir listeyi sonuna kadar okumak tek bir while True'dur:
from typing import Any
from mcp import Client
from mcp.server import Server, ServerRequestContext
from mcp.types import ListResourcesResult, PaginatedRequestParams, Resource
BOOKS = [f"book-{n}" for n in range(1, 101)]
PAGE_SIZE = 10
async def list_books(ctx: ServerRequestContext[Any], params: PaginatedRequestParams | None) -> ListResourcesResult:
start = 0 if params is None or params.cursor is None else int(params.cursor)
end = start + PAGE_SIZE
page = [Resource(uri=f"books://catalog/{name}", name=name) for name in BOOKS[start:end]]
next_cursor = str(end) if end < len(BOOKS) else None
return ListResourcesResult(resources=page, next_cursor=next_cursor)
server = Server("Bookshop", on_list_resources=list_books)
async def main() -> None:
async with Client(server) as client:
resources: list[Resource] = []
cursor: str | None = None
while True:
page = await client.list_resources(cursor=cursor)
resources.extend(page.resources)
if page.next_cursor is None:
break
cursor = page.next_cursor
print(f"{len(resources)} resources")
cursor,Noneolarak başlar; bu yüzden ilk istek imleç taşımaz.next_cursor'a bakmadan önce listeyi genişletin: son sayfada da kaynaklar vardır.- Çıkış koşulu
next_cursor is None'dır. Bunun dışındaki her şey, dokunulmadan doğrudancursor='a geri gider.
main()'ini çalıştırın; 100 resources yazdırır: on tane onluk sayfa, on sayfa olduğundan hiç haberi olmayan bir döngü tarafından birleştirilmiş.
Bu, İstemci sayfasının her list_* fiili için gösterdiği döngünün aynısıdır ve sayfalamayan bir sunucuya karşı hiçbir maliyeti yoktur: ilk yanıtta next_cursor, None olur ve döngü bir kez çalışır.
Üç kural
İmleçler opaktır. Bir istemci bir imleci asla ayrıştırmamalı, oluşturmamalı veya tahmin etmemelidir. Bir imlecin tek meşru kaynağı, bir önceki sayfanın next_cursor'ıdır; harfi harfine.
Sayfa boyutunu sunucu seçer. Protokolde limit= yoktur. Farklı bir sayfa boyutuna ihtiyacınız varsa sunucuyu değiştirirsiniz.
Sayfalamayı yok sayan bir istemci yine de çalışır. list_resources()'ı bir kez çağırır, ilk onu alır ve attığı next_cursor'ı hiç fark etmez. Hiçbir şey bozulmaz; yalnızca daha azını görür.
Check
Opak, opak demektir. Bir imleç uydurursanız (list_resources(cursor="page-2")) protokolün
sizin için yapabileceği hiçbir şey yoktur. Bu sunucu int("page-2")'yi dener, işleyici istisna fırlatır
ve istemciye dönen şudur:
MCPError(-32603, 'Internal server error', None)
Sunucudan almadığınız bir imleç bir hatadır, bir özellik isteği değil.
Özet
MCPServerher şeyi tek sayfada döndürür. Sayfalama isteğe bağlıdır ve buna düşük seviyeliServerüzerinde geçersiniz.on_list_resources(veon_list_tools,on_list_prompts,on_list_resource_templates)PaginatedRequestParams | Nonealır; ilk sayfa içinparams.cursor,None'dır.- Bir sayfa ile birlikte
next_cursordöndürürsünüz: sonradan tanıyacağınız herhangi bir dizge ya da geriye bir şey kalmadığındaNone. - İstemci döngüsü:
cursor=geçirin, biriktirin,next_cursor is Noneolana kadar tekrarlayın. - İmleçler opaktır, sayfa boyutu sunucunundur ve sayfalamayan bir istemci yine de birinci sayfayı alır.
Elle yazılan Server API'sinin geri kalanı (on_call_tool, input_schema dict'leri, _meta) Düşük seviyeli Server sayfasında.