MCP wyjaśnione według specyfikacji: czym naprawdę jest serwer
Jedna linia JSON do subprocessu i trzynaście definicji narzędzi w odpowiedzi — według rewizji 2026-07-28 bez handshake.
Na tej stronie
Zainstaluj opublikowany serwer MCP, wyślij do niego jedną linię JSON i przeczytaj, co wróci.
npm i @modelcontextprotocol/server-everything@2026.8.31
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
| npx @modelcontextprotocol/server-everything stdio{"result":{"tools":[{"name":"echo","title":"Echo Tool","description":"Echoes
back the input string","inputSchema":{"$schema":"http://json-schema.org/draft-07/
schema#","type":"object","properties":{"message":{"type":"string","description":
"Message to echo"}},"required":["message"]},"annotations":{"readOnlyHint":true,
… … 7,663 bytes on one line …
"jsonrpc":"2.0","id":1}Trzynaście definicji narzędzi, w jednej linii, z procesu, który przeczytał jedną linię ze swojego standardowego wejścia. Właśnie porozmawiałeś z Model Context Protocol, bez SDK, bez biblioteki klienta i bez frameworka. To wszystko: transport, format wiadomości i mały zestaw nazwanych metod.
Rozdział 18 definiował narzędzie jako dwie rzeczy — JSON Schema, który widzi model, oraz endpoint w Twoim kodzie, którego model nigdy nie widzi. Rozdział 23 zbudował harness, który trzyma ich katalog. Żaden z nich nie odpowiedział na pytanie, które decyduje, czy cokolwiek z tego da się ponownie wykorzystać: kto pisze schemat i jak trafia on od autora do Twojego prompt? MCP jest jedną z odpowiedzi na to pytanie i warto przeczytać ją u źródła, bo niemal wszystko, co o nim napisano, opisuje rewizję, która już nie istnieje.
Trzy rzeczy w poleceniu, które właśnie uruchomiłeś, były błędne — i każda z nich jest sekcją tego rozdziału. Nie przeniosło wersji protokołu, więc zgodny serwer powinien był je odrzucić. Mimo to dostało odpowiedź, z powodu, który specyfikacja nazywa zagrożeniem, a nie funkcją. I poprosiło o jeden z trzech prymitywów, nigdy nie odkrywając, że pozostałe dwa istnieją.
Problem, który rozwiązuje, i analogia, którą podaje sama specyfikacja
Link do sekcji: Problem, który rozwiązuje, i analogia, którą podaje sama specyfikacjaZanim przejdziemy do przewodu, policzmy. Masz aplikacji AI i rzeczy, do których powinny móc sięgać — kalendarz, tracker zgłoszeń, bazę danych hurtowni, narzędzie projektowe. Bez wspólnego kontraktu ktoś pisze integracji, a każda z nich to schemat plus endpoint plus historia uwierzytelniania plus koszt utrzymania. Z kontraktem dostawca narzędzia pisze serwer, dostawca aplikacji pisze klienta, a suma wynosi .
To nie jest nowa obserwacja, a specyfikacja mówi, czyj to był pomysł:
MCP takes some inspiration from the Language Server Protocol, which standardizes how to add support for programming languages across a whole ecosystem of development tools. In a similar way, MCP standardizes how to integrate additional context and tools into the ecosystem of AI applications.1
Potraktuj to porównanie dosłownie, a nie jak komplement. Przed tamtym protokołem obsługa języka w edytorze oznaczała wtyczkę dla każdego edytora; potem zespół języka wysyłał jeden serwer i każdy edytor go dostawał. Miarą sukcesu nie była elegancja, tylko to, że liczba integracji przestała się mnożyć. Tutaj wynika z tego to samo: wartość jest w liczbie implementacji, nie w projekcie. Protokół, którym mówią dwa produkty, to format danych z dodatkową ceremonią.
Co naprawdę jest na przewodzie
Link do sekcji: Co naprawdę jest na przewodzieWiadomości MCP to JSON-RPC 2.0. Żądanie jest obiektem z jsonrpc, id, method i opcjonalnym params; odpowiedź niesie to samo id oraz albo result, albo error; notyfikacja to żądanie bez id i nie dostaje odpowiedzi. Specyfikacja dodaje na to trzy ograniczenia: id musi być ciągiem znaków albo liczbą i nie może być null, nie może kolidować z innym żądaniem w toku, a każdy wynik musi nieść pole resultType.2
W transporcie stdio — tym, którego użyło powyższe polecenie — reguła ramkowania to jedna linia na wiadomość:
Messages are delimited by newlines, and MUST NOT contain embedded newlines. […] The server MUST NOT write anything to its
stdoutthat is not a valid MCP message.3
Ta ostatnia klauzula to najczęstszy sposób, w jaki domowy serwer się psuje, i psuje się po cichu: zabłąkane console.log, pasek postępu, ostrzeżenie o deprecjacji z zależności — i parser linii po stronie klienta trafia na coś, co nie jest JSON. Wyjście awaryjne jest w tej samej sekcji — serwer może pisać, co chce, do stderr, a klient nie powinien traktować tego jako błędu. Powyższy serwer referencyjny przy każdym uruchomieniu wypisuje Starting default (STDIO) server... na stderr, dlatego pipe nadal zadziałał.
Drugi standardowy transport to Streamable HTTP: każda wiadomość jest POST do jednego endpointu, a odpowiedź jest albo obiektem JSON, albo strumieniem Server-Sent Events ograniczonym do żądania — formatem przewodu, który rozdział 14 parsował ręcznie. Semantyka jest identyczna w obu przypadkach, bo transport jest wiązaniem: definiuje ramkowanie i dostarczenie, nie znaczenie.4
Pierwsza rzecz, która była błędna: nie było wersji
Link do sekcji: Pierwsza rzecz, która była błędna: nie było wersjiPowyższe polecenie wysłało tools/list i nic więcej. W obecnej rewizji takie żądanie jest niepoprawnie sformułowane, a zgodny serwer musi je odrzucić.
Od 2026-07-28 MCP jest protokołem bezstanowym, a specyfikacja mówi to bez zastrzeżeń:
The Model Context Protocol (MCP) is a stateless protocol: all the information needed to process a request is contained in the request itself. A server processes each request independently; no state should be inferred from previous requests, even those on the same connection or stream.2
Dlatego każde żądanie niesie własną wersję protokołu i własne możliwości klienta, w zastrzeżonym obiekcie _meta wewnątrz params. Dwa z tych pól są wymagane w każdym pojedynczym żądaniu; żądanie bez któregokolwiek jest niepoprawnie sformułowane i serwer musi odpowiedzieć -32602:2
klucz _meta | wymagane | czym jest |
|---|---|---|
io.modelcontextprotocol/protocolVersion | tak | rewizja, którą mówi to żądanie, np. "2026-07-28" |
io.modelcontextprotocol/clientCapabilities | tak | co klient może zrobić dla serwera w tym żądaniu |
io.modelcontextprotocol/clientInfo | nie (ale powinno) | nazwa i wersja klienta, tylko do wyświetlania i logów |
io.modelcontextprotocol/logLevel | nie | minimalny poziom logów, które serwer powinien emitować dla tego żądania |
Rozpisane poprawne tools/list wygląda tak — i to ostatni raz, gdy ten rozdział pokazuje metadane w całości, bo odtąd są w każdym żądaniu:
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{"_meta":{
"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientCapabilities":{"elicitation":{"form":{}}},
"io.modelcontextprotocol/clientInfo":{"name":"bare-hands","version":"0.0.1"}}}}Obiekt możliwości jest negocjacją. Nie ma już osobnego kroku negocjacji: klient deklaruje, co potrafi zrobić w każdym żądaniu, serwer deklaruje, co potrafi zrobić w wyniku, i żadna strona nie może użyć funkcji, której druga nie zadeklarowała. Serwer, który potrzebuje możliwości niezadeklarowanej przez klienta, musi odpowiedzieć -32021 i nazwać brakującą możliwość w data.requiredCapabilities. Serwer, który nie mówi żądaną wersją, musi odpowiedzieć -32022 i wymienić wersje, które obsługuje.2
Klienci, którzy chcą znać odpowiedź z góry, mogą o nią zapytać: server/discover to obowiązkowe RPC, które w jednym przebiegu zwraca obsługiwane wersje, możliwości, tożsamość i opcjonalny blok instructions.5 Wywołanie go jest opcjonalne. Implementacja nie.
Druga rzecz, która była błędna: serwer był legacy
Link do sekcji: Druga rzecz, która była błędna: serwer był legacyPolecenie zadziałało. W obecnej rewizji nie powinno, a powód zasługuje bardziej na pomiar niż na akapit, bo w jednej linii pokazuje stan całego ekosystemu.
Sondowanie serwera referencyjnego wykonaj tak, jak specyfikacja każe sondować nowoczesnemu klientowi:
echo '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{
"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientCapabilities":{}}}}' \
| npx @modelcontextprotocol/server-everything stdio{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method not found"}}To trzecia gałąź reguły zgodności: DiscoverResult oznacza nowoczesny, rozpoznany nowoczesny błąd oznacza nowoczesny, ale złą wersję, a cokolwiek innego — w tym -32601 — oznacza legacy, więc wróć do handshake initialize.3 Zróbmy to, prosząc o obecną rewizję:
→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2026-07-28",
"capabilities":{},"clientInfo":{"name":"bare-hands","version":"0.0.1"}}}
← {"result":{"protocolVersion":"2025-11-25","capabilities":{"tools":{"listChanged":true},
"prompts":{"listChanged":true},"resources":{"subscribe":true,"listChanged":true},
"logging":{},"tasks":{…},"completions":{}},"serverInfo":{"name":"mcp-servers/everything",
"title":"Everything Reference Server","version":"2.0.0"},"instructions":"…"}}Klient poprosił o 2026-07-28, a serwer odpowiedział 2025-11-25. 7 września 2026 oficjalny serwer referencyjny — pakiet npm @modelcontextprotocol/server-everything, wersja 2026.8.31, opublikowany 31 sierpnia 2026 — nie implementuje obecnej rewizji. Nie robi tego też, według dat, TypeScript SDK, na którym jest zbudowany: release 1.30.0 wyszedł 27 lipca 2026, dzień przed rewizją.
Ważniejsza jest konsekwencja, nie plotka. Niemal wszystko napisane o MCP opisuje protokół z handshake initialize, sesją, żądaniem roots/list wysyłanym przez serwer do klienta oraz transportem HTTP+SSE. Wszystkie cztery zniknęły albo znikają. Gdy czytasz cokolwiek o MCP, w tym tę stronę, pierwszą rzeczą, której masz szukać, jest numer rewizji.
A powód, dla którego pierwsze polecenie zadziałało, specyfikacja opisuje jako zagrożenie, nie funkcję:
some legacy servers do not validate that a request arrives after
initializeand would process an era-ambiguous method (such astools/call) under legacy semantics. Probing yields a deterministic failure instead.3
Pomiar: wysłanie tools/list do tego serwera bez żadnego handshake zwraca pełny katalog. Metoda, która powinna zostać odrzucona, została obsłużona, dokładnie dlatego specyfikacja każe najpierw sondować za pomocą server/discover, nawet jeśli wspierasz tylko nowoczesne wersje.
Trzy role i zdanie, które warto cytować z całego dokumentu
Link do sekcji: Trzy role i zdanie, które warto cytować z całego dokumentuMCP ma trzy strony, a różnica między pierwszymi dwiema to ta, którą ludzie zacierają:
Host. Aplikacja: produkt czatu, edytor, agent. Posiada rozmowę, model, dane uwierzytelniające i zgodę użytkownika. Tworzy klientów i egzekwuje granicę bezpieczeństwa między nimi.
Klient. Łącznik wewnątrz hosta. Każdy klient rozmawia z dokładnie jednym serwerem — ścisła relacja 1:1 — i dołącza wersję protokołu oraz możliwości do każdego żądania, które routuje.
Serwer. Proces albo usługa, która udostępnia zasoby, narzędzia i prompt. Może być lokalny albo zdalny, działa niezależnie, a całe jego zadanie to jeden skupiony obszar.6
Reguła „dokładnie jednego serwera” nie jest księgowością. To ona sprawia, że poniższa zasada projektowa jest implementowalna, i to jest zdanie, które należy wziąć ze specyfikacji, jeśli bierzesz tylko jedno:
Servers should not be able to read the whole conversation, nor "see into" other servers. Servers receive only necessary contextual information. Full conversation history stays with the host. Each server maintains isolation. Cross-server interactions are controlled by the host.6
To odwraca model mentalny, z którym przychodzi większość osób. Serwer pogodowy, który podpinasz do asystenta, nie widzi, o co zapytałeś. Widzi tools/call z argumentami wybranymi przez model i nic więcej — nie poprzednie tury, nie Twój system prompt, nie wyniki, które przed chwilą zwrócił serwer kalendarza. Jeśli dwa serwery mają współpracować, host świadomie przenosi wartość z jednego do drugiego, bo model o to poprosił. Dlatego izolacja jest właściwością bezpieczeństwa, na której opiera się rozdział 30: skompromitowany serwer ma mały, zdefiniowany promień rażenia, a jego powiększenie wymaga współpracy hosta.
Trzecia rzecz: trzy prymitywy, posortowane według tego, kto rządzi
Link do sekcji: Trzecia rzecz: trzy prymitywy, posortowane według tego, kto rządziPierwsze polecenie poprosiło ten serwer o narzędzia i dostało trzynaście. Zadaj mu dwa pozostałe pytania, a odpowie także na nie: resources/list zwraca siedem, prompts/list zwraca cztery. Żadne z nich się nie pojawiło, bo nikt nie zapytał. I tu dochodzimy do dydaktycznego kręgosłupa MCP, siedzącego w specyfikacji jako tabela, której prawie nikt nie cytuje:
| Prymityw | Kontrola | Opis | Przykład |
|---|---|---|---|
| Prompts | Kontrolowane przez użytkownika | Interaktywne szablony wywoływane wyborem użytkownika | Polecenia slash, opcje menu |
| Zasoby | Kontrolowane przez aplikację | Dane kontekstowe dołączane i zarządzane przez klienta | Zawartość plików, historia git |
| Narzędzia | Kontrolowane przez model | Funkcje udostępnione LLM do wykonywania działań | Żądania API POST, zapis plików |
Nie „trzy sposoby na wystawienie możliwości”. Trzy odpowiedzi na pytanie: kto decyduje, że to się dzieje. Model decyduje o wywołaniu narzędzia. Aplikacja decyduje o dołączeniu zasobu. Osoba decyduje o uruchomieniu prompt. Pomyl to, a funkcja nadal działa, ale działa w złym momencie i z niewłaściwego powodu.
Najłatwiej poczuć to na kalendarzu. Oto serwer, który wystawia ten sam kalendarz trzy razy, po jednym jako każdy prymityw, w stu liniach czystego Node bez zależności:
const TOOL = {
name: "create_event",
description: "Create a calendar event. Writes to the calendar.",
inputSchema: {
type: "object",
properties: {
title: { type: "string", description: "Event title." },
startsAt: { type: "string", format: "date-time", description: "Start, ISO 8601 UTC." },
},
required: ["title"],
},
};
switch (method) {
case "resources/read":
return ok(id, { contents: [{ uri: "calendar://week",
mimeType: "application/json", text: JSON.stringify(EVENTS) }],
ttlMs: 60000, cacheScope: "private" });
case "prompts/get":
return ok(id, { description: PROMPT.description, messages: [{ role: "user",
content: { type: "text", text: `Read calendar://week and draft a plan. ` +
`Focus: ${params.arguments?.focus ?? "balance"}.` } }] });
case "tools/list":
return ok(id, { tools: [TOOL], ttlMs: 300000, cacheScope: "public" });
}Uruchom go i zapytaj na wszystkie trzy sposoby. Prawdziwy output, jedna wiadomość na linię na przewodzie, tutaj zawinięty na potrzeby strony, z pominiętym _meta żądania i blokiem tożsamości serwera:
→ resources/read {"uri":"calendar://week"}
← {"resultType":"complete","contents":[{"uri":"calendar://week",
"mimeType":"application/json","text":"[{\"id\":\"e1\",\"title\":\"Standup\",
\"startsAt\":\"2026-09-07T09:00:00Z\"},{\"id\":\"e2\",\"title\":\"Design review\",
\"startsAt\":\"2026-09-09T15:00:00Z\"}]"}],"ttlMs":60000,"cacheScope":"private"}
→ prompts/get {"name":"prepare_week","arguments":{"focus":"deep work"}}
← {"resultType":"complete","description":"Read the week and draft a plan.",
"messages":[{"role":"user","content":{"type":"text",
"text":"Read calendar://week and draft a plan. Focus: deep work."}}]}
→ tools/call {"name":"create_event","arguments":{"title":"Dentist",
"startsAt":"2026-09-10T08:30:00Z"}}
← {"resultType":"complete","content":[{"type":"text",
"text":"Created e3: Dentist at 2026-09-10T08:30:00Z"}],
"structuredContent":{"id":"e3","title":"Dentist","startsAt":"2026-09-10T08:30:00Z"},
"isError":false}Trzy metody, trzy kształty, jeden kalendarz. Teraz sedno:
Czytanie tygodnia jest zasobem
Link do sekcji: Czytanie tygodnia jest zasobemJest adresowane przez URI, jest bezwładne, a aplikacja decyduje, czy dołączyć je do rozmowy. Nic w protokole nie pozwala modelowi sięgnąć po nie samodzielnie. Wynik niesie ttlMs i cacheScope, nowe w tej rewizji, więc klient może cache’ować tydzień przez minutę zamiast odpytywać.
Tworzenie wydarzenia jest narzędziem
Link do sekcji: Tworzenie wydarzenia jest narzędziemMa schemat, ma skutki uboczne, a model decyduje, kiedy je wywołać. Jego wynik niesie isError, czyli pole, za którym argumentował rozdział 18: błąd walidacji wraca jako wynik narzędzia, który model może przeczytać i poprawić, a nie jako błąd protokołu.
„Przygotuj mój tydzień” jest prompt
Link do sekcji: „Przygotuj mój tydzień” jest promptTo nazwany, przyjmujący argumenty szablon, który osoba wywołuje — polecenie slash w menu. Zwraca wiadomości, nie odpowiedź. To sposób, aby autor serwera wysłał sformułowanie, które działa z jego własnymi narzędziami, czyli dokładnie tę wiedzę, którą autor serwera ma, a użytkownik nie.
Prawie wszyscy robią z tych trzech rzeczy narzędzia. Rezultatem jest katalog, w którym odczyt, który aplikacja powinna była po cichu dołączyć, konkuruje o attention modelu z zapisem wymagającym zgody, a jedna rzecz, dla której osoba chciała przycisku, jest zakopana w schemacie. Poprawne rozdzielenie nic nie kosztuje i rozstrzyga się je, zanim napiszesz pierwszą linię.
Serwer nie może do Ciebie zadzwonić
Link do sekcji: Serwer nie może do Ciebie zadzwonićNarzędzie kalendarza ma jeden wymagany argument, title, i opcjonalny startsAt. Poproś je o utworzenie wydarzenia bez daty, a wraca coś ciekawego:
→ tools/call {"name":"create_event","arguments":{"title":"Dentist"}}
← {"resultType":"input_required",
"inputRequests":{"when":{"method":"elicitation/create","params":{"mode":"form",
"message":"When should \"Dentist\" start?",
"requestedSchema":{"type":"object",
"properties":{"startsAt":{"type":"string","format":"date-time"}},
"required":["startsAt"]}}}},
"requestState":"eyJ0aXRsZSI6IkRlbnRpc3QifQ=="}Serwer nie wysłał żądania. Odpowiedział na to, które dostał, za pomocą resultType: "input_required" i opisu tego, czego nadal potrzebuje. Klient zbiera odpowiedź od osoby, a potem ponownie wysyła pierwotne wywołanie — z nowym id, niosąc inputResponses i odsyłając nieprzezroczyste requestState:
→ tools/call {"name":"create_event","arguments":{"title":"Dentist"},
"inputResponses":{"when":{"action":"accept",
"content":{"startsAt":"2026-09-10T08:30:00Z"}}},
"requestState":"eyJ0aXRsZSI6IkRlbnRpc3QifQ=="}
← {"resultType":"complete","content":[{"type":"text",
"text":"Created e3: Dentist at 2026-09-10T08:30:00Z"}],"isError":false}To Multi Round-Trip Requests, wprowadzone w obecnej rewizji, i zastąpiło starszy projekt, w którym serwery wysyłały żądania JSON-RPC z powrotem do klientów. Specyfikacja transportu mówi teraz regułę wprost: „servers do not initiate JSON-RPC requests and clients do not send JSON-RPC responses”.4 Jest jeden kierunek inicjatywy i należy do hosta.
Na tym mechanizmie jadą dwie funkcje po stronie klienta, a jedna z nich ma nazwę, która Cię potknie.
Elicitation to serwer proszący osobę o coś: formularz z celowo ograniczonym JSON Schema — płaskie obiekty, właściwości prymitywne, brak zagnieżdżeń — żeby każdy klient mógł go wyrenderować bez silnika layoutu. Niesie twardą regułę: serwery nie mogą używać trybu formularza do proszenia o „passwords, API keys, access tokens, or payment credentials” i muszą używać do tego trybu URL, który wysyła użytkownika na stronę, której klient nigdy nie czyta.7
Sampling to serwer proszący model hosta o generację, żeby serwer mógł być inteligentny bez posiadania klucza API. I tu ostrzeżenie słownikowe, bo to słowo już znaczy coś innego w tym kursie: to nie jest sampling z rozdziału 17. Nic tutaj nie dotyczy temperatury, top-p ani kształtu rozkładu prawdopodobieństwa. To zagnieżdżone wywołanie modelu podróżujące wstecz przez protokół.
Jest drugi powód, żeby po to nie sięgać: od tej rewizji sampling jest deprecated, razem z roots i logging, w ramach SEP-2577, z bezpośrednio sugerowaną migracją — „integrate directly with LLM provider APIs instead of Sampling”.8 Pomysł nie zawiódł technicznie; nie uzasadnił swojej powierzchni, a protokół, który potrafi usuwać rzeczy, jest zdrowszy niż taki, który nie potrafi.
Zepsuj to celowo: połączenia nie są sesjami
Link do sekcji: Zepsuj to celowo: połączenia nie są sesjamiBezstanowość brzmi jak szczegół formatu przewodu, dopóki jej nie przetestujesz. Weź powyższą wymianę trzech wiadomości i uruchom każdą wiadomość w osobnym procesie — świeże node calendar.mjs, bez współdzielonej pamięci, bez przenoszenia czegokolwiek:
process A tools/call (no date) → resultType: input_required
requestState: eyJ0aXRsZSI6IkRlbnRpc3QifQ==
process B tools/call (with the answer, same requestState)
→ resultType: complete
"Created e3: Dentist at 2026-09-10T08:30:00Z"
process C resources/read calendar://week
→ events: 2 (Standup, Design review)Proces B, który nigdy nie widział pytania, dokończył multi-round-trip call rozpoczęte przez proces A. To jest sens requestState: kontynuacja podróżuje w wiadomości, więc nic nie zależy od tego, czy proces jest ten sam.
Proces C to porażka. Wydarzenie zostało utworzone, ale go nie ma — bo zabawkowy serwer trzyma EVENTS w tablicy na poziomie modułu, a tablica na poziomie modułu jest stanem połączenia. Notatka w specyfikacji precyzyjnie nazywa błąd:
an open connection, such as a STDIO process, is not a conversation or session: clients may interleave unrelated requests on the same transport, and a server must not treat connection or process identity as a proxy for conversation or session continuity.2
Zalecana poprawka to nie sesja. To jawny uchwyt: narzędzie tworzące zwraca nieprzezroczysty identyfikator, a każde późniejsze wywołanie bierze go jako zwykły argument. Protokół w ogóle nie ma dla niego pojęcia — „from the wire's perspective a handle is an ordinary string in a tool result and an ordinary argument to subsequent tool calls”.9 To oddaje modelowi odpowiedzialność za niesienie go, a serwerowi odpowiedzialność za walidację przy każdym pojedynczym wywołaniu, czy ten wywołujący może go użyć, bo uchwyt jest nazwą, a nie uprawnieniem.
Ile kosztuje serwer, zanim cokolwiek zrobi
Link do sekcji: Ile kosztuje serwer, zanim cokolwiek zrobiKażde narzędzie wystawione przez serwer jest schematem, który trafia do Twojego prompt przy każdym żądaniu, a rozdział 24 zmierzył, co to robi z oknem. MCP dodaje drugą pozycję kosztową, którą łatwo przeoczyć, więc warto policzyć obie na powyższym serwerze referencyjnym.
13 tool definitions (name + description + inputSchema): 1,307 tokens
cheapest tool, get-tiny-image 52
costliest tool, gzip-file-as-resource 235
server `instructions`, returned by discovery: 312 tokens
------
one server, connected, before it is used: 1,619 tokensDwie obserwacje. Pierwsza jest arytmetyczna: podłącz pięć serwerów tej wielkości, a mniej więcej osiem tysięcy token Twojego okna jest zajęte w każdej turze, na zawsze, niezależnie od tego, czy model użyje któregokolwiek z nich — to mechanizm stojący za redukcją ze 150 000 do 2 000 cytowaną w rozdziale 24 i powód, dla którego istnieje odkrywanie narzędzi just-in-time.
Druga to uwaga o bezpieczeństwie przebrana za księgowość. instructions to tekst w języku naturalnym, napisany przez autora serwera, który ląduje w prompt hosta, a opisy narzędzi obok niego są takie same. Specyfikacja mówi, co z tym zrobić w swoich zasadach bezpieczeństwa: adnotacje i opisy narzędzi „should be considered untrusted, unless obtained from a trusted server”, a hosty „must obtain explicit user consent before invoking any tool”.1 Podłączenie serwera MCP nie jest dodaniem zależności. To przyznanie obcej osobie 1 619 token Twojego system prompt i prawa do bycia wywołaną. Rozdział 30 opowiada, co się dzieje, gdy ta obca osoba jest wroga.
Sekcja datowana: rewizja 2026-07-28 i co psuje
Link do sekcji: Sekcja datowana: rewizja 2026-07-28 i co psujeWszystko w tej sekcji jest prawdziwe dla rewizji protokołu 2026-07-28, obecnej, czytanej 7 września 2026. Rewizje są datowane jako YYYY-MM-DD, a data oznacza ostatni raz, kiedy dokonano zmiany niezgodnej wstecz.10 Dokument normatywny to plik TypeScript, schema/2026-07-28/schema.ts; JSON Schema obok niego jest z niego generowany, dlatego specyfikacja jest tutaj czytana w TypeScript i dlatego uczenie MCP z czegokolwiek innego oznacza uczenie tłumaczenia.
| Co się zmieniło | Było | Jest teraz | Psuje |
|---|---|---|---|
| Handshake | initialize + notifications/initialized, raz na połączenie | usunięty; każde żądanie niesie wersję _meta i możliwości | każdego klienta napisanego przed tą rewizją |
| Sesje | nagłówek Mcp-Session-Id, stan w zakresie połączenia | usunięte; stan podróżuje w jawnych uchwytach wybitych przez serwer | endpointy list, które różniły się per połączenie |
| Discovery | wywnioskowane z wyniku initialize | server/discover, które serwery muszą implementować | nic, ale teraz implementacja jest obowiązkowa |
| Wywołania serwer-do-klienta | serwer wysyłał roots/list, sampling/createMessage, elicitation/create | InputRequiredResult i ponowna próba klienta | każdy serwer, który wypychał żądanie do klienta |
| Kształt wyniku | dowolny obiekt | wymagane resultType: "complete" albo "input_required" | nic: brakujące pole trzeba czytać jako "complete" |
| Subskrypcje | strumień HTTP GET, resources/subscribe | jeden strumień subscriptions/listen z typami opt-in | endpoint GET zniknął |
| Wznawianie strumienia | replay Last-Event-ID na Streamable HTTP | usunięte; przerwany strumień traci żądanie, wyślij ponownie z nowym id | klientów, którzy polegali na ponownym dostarczeniu |
| Roots | funkcja klienta, o którą serwery mogły prosić | deprecated (SEP-2577); przekazuj ścieżki jako argumenty narzędzi lub URI zasobów | jeszcze nic — dwunastomiesięczne okno |
| Sampling i logging | funkcje klienta | deprecated (SEP-2577) | jeszcze nic — dwunastomiesięczne okno |
| Transport HTTP+SSE | deprecated od 2025-03-26 | Deprecated w ramach polityki cyklu życia (SEP-2596) | migracja do Streamable HTTP |
| Rejestracja klienta | OAuth 2.0 Dynamic Client Registration, RFC 7591 | deprecated na rzecz Client ID Metadata Documents | zachowane dla serwerów autoryzacji bez nich |
| Kody błędów | -32002 dla nieznalezionego zasobu | -32602; -32020–-32099 zarezerwowane dla specyfikacji | nowe kody -32020, -32021, -32022 |
Zmiana ładu pod tą tabelą ma większe znaczenie niż dowolny pojedynczy wiersz. Ta rewizja przyjęła cykl życia funkcji i politykę deprecjacji: funkcje są Active, Deprecated albo Removed, funkcja deprecated dokumentuje ścieżkę migracji i pozostaje w specyfikacji przez co najmniej dwanaście miesięcy, zanim kwalifikuje się do usunięcia, a rejestr wymienia wszystko, co obecnie jest w stanie Deprecated.8 Przed tą polityką „deprecated” w protokole AI znaczyło tyle, co mówił ostatni wpis na blogu. Teraz znaczy datę.
Pokaż szczegóły
Rozszerzenia, czyli część, o której nikt jeszcze nie napisał.
Poza rdzeniem MCP definiuje opcjonalne rozszerzenia — „always opt-in and require explicit support from both client and server”, deklarowane przez pole extensions w możliwościach klienta i serwera.1 Trzy warto znać z nazwy:
- Tasks (
io.modelcontextprotocol/tasks), przeniesione w tej rewizji z rdzenia protokołu do oficjalnego rozszerzenia: asynchroniczne wykonywanie długotrwałych operacji, z pollingiem przeztasks/get, wejściem w trakcie przeztasks/updatei trwałymi uchwytami. To odpowiedź na narzędzie, które trwa dwadzieścia minut, co rozdział 23 obsłużył zdarzeniem postępu i sygnałem docierającym do narzędzia. - Skills over MCP, grupa robocza sprawiająca, że agent skills — temat rozdziału 28 — są odkrywalne i konsumowalne przez protokół.
- MCP Apps, interaktywny UI renderowany inline w rozmowie: wykresy, formularze, odtwarzacze wideo.
I zauważ, co teraz znaczy „negocjowane”: nie ma inicjalizacji, przy której można negocjować, więc rozszerzenie deklaruje się per żądanie, jak wszystko inne.
Gdzie MCP leży wobec wszystkiego, z czym bywa mylone
Link do sekcji: Gdzie MCP leży wobec wszystkiego, z czym bywa myloneOto słownictwo całego bloku w jednym miejscu.
| Czym jest | Kto rozmawia z kim | Kiedy jest odpowiedzią | |
|---|---|---|---|
| Zwykłe API | Interfejs dla programu | Twój kod ↔ usługa | Piszesz wywołującego. Kontrolujesz schemat, auth i obsługę błędów, a problem discovery nie istnieje. |
| MCP | Protokół do wystawiania narzędzi, danych i szablonów aplikacji AI | host ↔ serwer, po jednym kliencie | Ktoś inny napisał możliwość i wiele hostów powinno móc jej użyć bez szytej na miarę integracji. |
| RAG | Technika znajdowania tekstu i wkładania go do prompt | Twój kod ↔ Twój indeks | Model musi coś wiedzieć. Rozdział 19. MCP jest sposobem dostarczenia retrievera; nie jest retrieverem. |
| Agent skills | Folder z SKILL.md, który model czyta | model ↔ dokument | Wiedza jest proceduralna — jak my to robimy — i jest prozą, nie funkcją. Rozdział 28. |
| A2A | Protokół współpracy agentów jako równych sobie | agent ↔ agent | Druga strona rozumuje, planuje i utrzymuje stan przez długie zadanie, zamiast odpowiadać na wywołanie. |
| ACP | Był osobnym protokołem komunikacji agentów | — | To nie jest już żywe porównanie. Patrz niżej. |
Dwa z nich zasługują na po jednym zdaniu, bo to tam naprawdę mieszka zamieszanie.
MCP kontra A2A nie jest rywalizacją i obie specyfikacje tak mówią. Dokumentacja A2A rysuje granicę według tego, co jest po drugiej stronie: MCP „defines how an AI agent interacts with and utilizes individual tools and resources, such as a database or an API”, gdzie narzędzie wykonuje „specific, often stateless, functions”; A2A dotyczy agentów, „more autonomous systems”, które „reason, plan, use multiple tools, maintain state over longer interactions, and engage in complex, often multi-turn dialogues”. Jego własne podsumowanie to zdanie do zapamiętania: „A2A is about agents partnering on tasks, while MCP is more about agents using capabilities.”11 Te dwa protokoły się zagnieżdżają — aplikacja używa A2A, żeby dotrzeć do innych agentów, a każdy agent używa MCP, żeby dotrzeć do własnych narzędzi. Rozdział 25 narysował tę linię w jednym procesie, między pytaniem sub-agent a przekazaniem mu rozmowy; A2A rysuje ją między organizacjami.
MCP kontra ACP to porównanie z nieaktualnym założeniem, dlatego właśnie warto na nie odpowiedzieć. Agent Communication Protocol był osobnym otwartym standardem wiadomości agent-to-agent. Jego własna dokumentacja zaczyna się teraz od komunikatu: „ACP is now part of A2A under the Linux Foundation!”12 Uczciwa odpowiedź na „MCP czy ACP?” we wrześniu 2026 brzmi: pytanie ma o jedną opcję mniej, niż sugerują strony, które się na nie pozycjonują.
A porównanie, o które ludzie pytają najczęściej, mcp vs api, ma najmniej ciekawą odpowiedź: MCP jest API. To, co dodaje, nie jest mocą, tylko konwencjami — stałym zestawem nazw metod, wywołaniem discovery, hierarchią kontroli nad prymitywami i modelem izolacji. Oddajesz swobodę projektowania własnego interfejsu i dostajesz każdy host, który mówi protokołem; to wymiana, którą oferował każdy protokół w historii.
Dokąd to prowadzi dalej
Link do sekcji: Dokąd to prowadzi dalejMożesz już czytać specyfikację bez tłumacza, odróżnić zasób od narzędzia i od prompt po tym, kto nim rządzi, wpisać żądanie ręcznie, gdy biblioteka klienta Cię okłamuje, i datować każdy artykuł o MCP po tym, które funkcje deprecated nadal przedstawia jako aktualne.
Nie zrobiłeś jeszcze jednej rzeczy: nie wysłałeś własnego serwera. Rozdział 27 pisze ten sam serwer dwa razy — TypeScript i Python, obok siebie, bo MCP jest jedynym naprawdę dwujęzycznym terytorium w tym kursie, a liczby mówią to w obie strony. Omawia poprawnie dwa żywe transporty, inspector, pakowanie i połowę protokołu, którą ten rozdział celowo zostawił na boku: autoryzację. Bo w chwili, gdy Twój serwer jest zdalny, a nie jest subprocess na Twoim laptopie, klient obcej osoby przedstawi token, a reguła specyfikacji dotycząca tego, co wolno Ci z nim zrobić, jest wyjątkowo surowa.
To rodzi pytanie, na które następny rozdział musi odpowiedzieć, i nie jest ono przyjazne: jeśli token trafia na Twój serwer, a został wystawiony dla czyjegoś audience, co dokładnie powstrzymuje Cię przed jego przekazaniem dalej?
Źródła i metoda
Link do sekcji: Źródła i metodaKażdy cytat, nazwa metody, kod błędu i reguła w tym rozdziale zostały przeczytane ze specyfikacji Model Context Protocol, rewizja 2026-07-28, dnia 7 września 2026. Każdy trace został wyprodukowany lokalnie na Node 22: zabawkowy serwer kalendarza ma 101 linii bez zależności, a serwer referencyjny to opublikowany pakiet npm nazwany niżej. Do napisania tego rozdziału nie wywołano żadnego płatnego API — nic tutaj nie potrzebuje modelu, co samo jest sednem.
Pomiary: @modelcontextprotocol/server-everything@2026.8.31, opublikowany 31 sierpnia 2026, zbudowany na @modelcontextprotocol/sdk@1.30.0, opublikowany 27 lipca 2026 — dzień przed rewizją, którą opisuje ten rozdział. Odpowiada na server/discover za pomocą -32601, negocjuje 2025-11-25, gdy prosisz o 2026-07-28, i obsługuje tools/list bez żadnego handshake. Jego katalog to 13 narzędzi w 7 663 bajtach; liczby token to o200k_base przez tiktoken, po name, description i inputSchema każdej definicji — czyli po tym, co dostawca renderuje do Twojego prompt, a nie po wadze ramki JSON-RPC.
Anthropic, Code execution with MCP: building more efficient agents, 4 listopada 2025, jest źródłem liczby 150 000-do-2 000, cytowanej i użytej w rozdziale 24, a tutaj tylko przywołanej.
Przypisy
Link do sekcji: Przypisy-
Specyfikacja,
modelcontextprotocol.io/specification/latest(przekierowuje do/2026-07-28), przeczytana 7 września 2026. Źródło porównania z Language Server Protocol; stwierdzenia, że specyfikacja jest „based on the TypeScript schema inschema.ts”; podsumowania protokołu bazowego („Stateless, self-contained requests”, „Per-request capability negotiation”); listy rozszerzeń (Tasks, Skills over MCP, MCP Apps) i stwierdzenia, że rozszerzenia „are always opt-in and require explicit support from both client and server”; oraz zasad Security i Trust & Safety, w tym „Hosts must obtain explicit user consent before invoking any tool” i traktowania adnotacji narzędzi jako niezaufanych. ↩ ↩2 ↩3 -
Protokół bazowy,
modelcontextprotocol.io/specification/2026-07-28/basic. Źródło ograniczeń JSON-RPC (id inne niż null, brak ponownego użycia id, wymaganeresultType); sekcji Statelessness i jej notatki, że otwarty proces stdio nie jest sesją; tabeli zastrzeżonych kluczy_metai statusu wymaganego/opcjonalnego każdego pola per żądanie; reguły-32602dla brakującego wymaganego pola; regułyMissingRequiredClientCapability(-32021); oraz polityki przydziału kodów błędów. ↩ ↩2 ↩3 ↩4 ↩5 -
Transport stdio,
modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio. Źródło reguł ramkowania oddzielanego znakami nowej linii, wymogu czystościstdout, dopuszczeniastderroraz sondowania zgodności wstecznej o trzech wynikach — w tym ostrzeżenia, że niektóre serwery legacy przetwarzają metody niejednoznaczne epokowo bez handshake, co pomiar w tym rozdziale odtwarza. ↩ ↩2 ↩3 -
Przegląd transportów,
modelcontextprotocol.io/specification/2026-07-28/basic/transports. Źródło ramy „a transport is a binding” oraz stwierdzenia, że serwery nie inicjują żądań JSON-RPC, a klienci nie wysyłają odpowiedzi JSON-RPC. ↩ ↩2 -
Discovery,
modelcontextprotocol.io/specification/2026-07-28/server/discover. Źródło obowiązkowego statususerver/discover, kształtuDiscoverResultoraz polainstructionsopisanego jako „optional natural-language guidance for LLMs on how to use this server effectively”. ↩ -
Architektura,
modelcontextprotocol.io/specification/2026-07-28/architecture. Źródło definicji hosta, klienta i serwera, reguły 1:1 klient-do-serwera, czterech zasad projektowych, z których zasada izolacji jest tutaj cytowana bez piątego punktu, „Host process enforces security boundaries”, oraz sekcji negocjacji możliwości. ↩ ↩2 -
Elicitation,
.../client/elicitation, oraz Sampling,.../client/sampling. Źródło dwóch trybów elicitation i ich ograniczonego schematu; zakazu proszenia o dane uwierzytelniające przez tryb formularza; definicji sampling, wymogu human-in-the-loop i dołączonego do niego ostrzeżenia o deprecjacji. ↩ -
Key Changes,
modelcontextprotocol.io/specification/2026-07-28/changelog, oraz Feature lifecycle and deprecation policy,.../community/feature-lifecycle. Źródło każdego wiersza tabeli zmian: usunięcia sesji i nagłówkaMcp-Session-Id(SEP-2567); bezstanowości i usunięciainitialize(SEP-2575);server/discover(SEP-2575);subscriptions/listen(SEP-2575); Multi Round-Trip Requests iresultType(SEP-2322); usunięcia wznawialności strumienia (SEP-2575); deprecjacji Roots, Sampling i Logging (SEP-2577); przeklasyfikowania HTTP+SSE (SEP-2596); deprecjacji Dynamic Client Registration na rzecz Client ID Metadata Documents; przenumerowania kodów błędów; oraz dwunastomiesięcznego okna deprecjacji. ↩ ↩2 -
Narzędzia,
modelcontextprotocol.io/specification/2026-07-28/server/tools, oraz Server Features,.../server. Źródło tabeli hierarchii kontroli odtworzonej wyżej; kształtówtools/listitools/call; rozróżnieniaisErrormiędzy błędami protokołu a błędami wykonania narzędzia; reguł nazw narzędzi i notatki o przestrzeni nazw zalecającej „prefixing tool names with a server identifier”; oraz nienormatywnych wskazówek „Stateful Tools” o jawnych uchwytach. ↩ -
Versioning,
modelcontextprotocol.io/specification/versioning. Źródło schematuYYYY-MM-DD, stanów rewizji Draft/Current/Final, potwierdzenia, że 2026-07-28 jest obecna, oraz reguł negocjacji per żądanie. Tabela tierów SDK podmodelcontextprotocol.io/docs/sdkwymienia TypeScript, Python, C#, Go i Rust jako Tier 1, Java i Ruby jako Tier 2 oraz Swift, PHP i Kotlin jako Tier 3. ↩ -
A2A Protocol, wersja 1.0.0,
a2a-protocol.org— specyfikacja i strona A2A and MCP: Relationship and Distinction, przeczytane 7 września 2026. Źródło rozróżnienia narzędzia-kontra-agenci, stwierdzenia, że dwa protokoły „address distinct but highly complementary needs”, oraz formuły partnering/using. ↩ -
Agent Communication Protocol,
agentcommunicationprotocol.dev, przeczytany 7 września 2026: „ACP is now part of A2A under the Linux Foundation!”, baner dodany nad specyfikacją, która nadal jest serwowana w całości — architektura, manifest agenta, discovery agenta, struktura wiadomości, agenci stanowi, cykl życia run i lista endpointów REST nadal odpowiadają 200. Specyfikacja nie zniknęła; projekt tak. ↩