Przejdź do treści
26/30Rozdział 26 z 30

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.

terminalBASH
npm i @modelcontextprotocol/server-everything@2026.8.31
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
  | npx @modelcontextprotocol/server-everything stdio
TEXT
{"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 specyfikacja

Zanim przejdziemy do przewodu, policzmy. Masz NN aplikacji AI i MM 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 N×MN \times M 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 N+MN + M.

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ą.

Wiadomoś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 stdout that 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 wersji

Powyż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 _metawymaganeczym jest
io.modelcontextprotocol/protocolVersiontakrewizja, którą mówi to żądanie, np. "2026-07-28"
io.modelcontextprotocol/clientCapabilitiestakco klient może zrobić dla serwera w tym żądaniu
io.modelcontextprotocol/clientInfonie (ale powinno)nazwa i wersja klienta, tylko do wyświetlania i logów
io.modelcontextprotocol/logLevelnieminimalny 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:

one line, split for the pageTEXT
{"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ł legacy

Polecenie 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:

terminalBASH
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
TEXT
{"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ę:

TEXT
→ {"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 initialize and would process an era-ambiguous method (such as tools/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 dokumentu

MCP 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ądzi

Pierwsze 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:

PrymitywKontrolaOpisPrzykład
PromptsKontrolowane przez użytkownikaInteraktywne szablony wywoływane wyborem użytkownikaPolecenia slash, opcje menu
ZasobyKontrolowane przez aplikacjęDane kontekstowe dołączane i zarządzane przez klientaZawartość plików, historia git
NarzędziaKontrolowane przez modelFunkcje 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:

calendar.mjs — the parts that matterJS
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:

TEXT
→ 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:

Jest 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ędziem

Ma 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 prompt

To 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:

TEXT
→ 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:

TEXT
→ 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ą sesjami

Bezstanowość 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:

TEXT
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 zrobi

Każ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.

o200k_base tokensTEXT
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 tokens

Dwie 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 psuje

Wszystko 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łoByłoJest terazPsuje
Handshakeinitialize + notifications/initialized, raz na połączenieusunięty; każde żądanie niesie wersję _meta i możliwościkażdego klienta napisanego przed tą rewizją
Sesjenagłówek Mcp-Session-Id, stan w zakresie połączeniausunięte; stan podróżuje w jawnych uchwytach wybitych przez serwerendpointy list, które różniły się per połączenie
Discoverywywnioskowane z wyniku initializeserver/discover, które serwery muszą implementowaćnic, ale teraz implementacja jest obowiązkowa
Wywołania serwer-do-klientaserwer wysyłał roots/list, sampling/createMessage, elicitation/createInputRequiredResult i ponowna próba klientakażdy serwer, który wypychał żądanie do klienta
Kształt wynikudowolny obiektwymagane resultType: "complete" albo "input_required"nic: brakujące pole trzeba czytać jako "complete"
Subskrypcjestrumień HTTP GET, resources/subscribejeden strumień subscriptions/listen z typami opt-inendpoint GET zniknął
Wznawianie strumieniareplay Last-Event-ID na Streamable HTTPusunięte; przerwany strumień traci żądanie, wyślij ponownie z nowym idklientów, którzy polegali na ponownym dostarczeniu
Rootsfunkcja klienta, o którą serwery mogły prosićdeprecated (SEP-2577); przekazuj ścieżki jako argumenty narzędzi lub URI zasobówjeszcze nic — dwunastomiesięczne okno
Sampling i loggingfunkcje klientadeprecated (SEP-2577)jeszcze nic — dwunastomiesięczne okno
Transport HTTP+SSEdeprecated od 2025-03-26Deprecated w ramach polityki cyklu życia (SEP-2596)migracja do Streamable HTTP
Rejestracja klientaOAuth 2.0 Dynamic Client Registration, RFC 7591deprecated na rzecz Client ID Metadata Documentszachowane dla serwerów autoryzacji bez nich
Kody błędów-32002 dla nieznalezionego zasobu-32602; -32020-32099 zarezerwowane dla specyfikacjinowe 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 przez tasks/get, wejściem w trakcie przez tasks/update i 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 mylone

Oto słownictwo całego bloku w jednym miejscu.

Czym jestKto rozmawia z kimKiedy jest odpowiedzią
Zwykłe APIInterfejs dla programuTwój kod ↔ usługaPiszesz wywołującego. Kontrolujesz schemat, auth i obsługę błędów, a problem discovery nie istnieje.
MCPProtokół do wystawiania narzędzi, danych i szablonów aplikacji AIhost ↔ serwer, po jednym kliencieKtoś inny napisał możliwość i wiele hostów powinno móc jej użyć bez szytej na miarę integracji.
RAGTechnika znajdowania tekstu i wkładania go do promptTwój kod ↔ Twój indeksModel musi coś wiedzieć. Rozdział 19. MCP jest sposobem dostarczenia retrievera; nie jest retrieverem.
Agent skillsFolder z SKILL.md, który model czytamodel ↔ dokumentWiedza jest proceduralna — jak my to robimy — i jest prozą, nie funkcją. Rozdział 28.
A2AProtokół współpracy agentów jako równych sobieagent ↔ agentDruga strona rozumuje, planuje i utrzymuje stan przez długie zadanie, zamiast odpowiadać na wywołanie.
ACPBył osobnym protokołem komunikacji agentówTo 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.

Moż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?


Każ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.

  1. 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 in schema.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

  2. Protokół bazowy, modelcontextprotocol.io/specification/2026-07-28/basic. Źródło ograniczeń JSON-RPC (id inne niż null, brak ponownego użycia id, wymagane resultType); sekcji Statelessness i jej notatki, że otwarty proces stdio nie jest sesją; tabeli zastrzeżonych kluczy _meta i statusu wymaganego/opcjonalnego każdego pola per żądanie; reguły -32602 dla brakującego wymaganego pola; reguły MissingRequiredClientCapability (-32021); oraz polityki przydziału kodów błędów. 2 3 4 5

  3. Transport stdio, modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio. Źródło reguł ramkowania oddzielanego znakami nowej linii, wymogu czystości stdout, dopuszczenia stderr oraz 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

  4. 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

  5. Discovery, modelcontextprotocol.io/specification/2026-07-28/server/discover. Źródło obowiązkowego statusu server/discover, kształtu DiscoverResult oraz pola instructions opisanego jako „optional natural-language guidance for LLMs on how to use this server effectively”.

  6. 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

  7. 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.

  8. 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łówka Mcp-Session-Id (SEP-2567); bezstanowości i usunięcia initialize (SEP-2575); server/discover (SEP-2575); subscriptions/listen (SEP-2575); Multi Round-Trip Requests i resultType (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

  9. 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ów tools/list i tools/call; rozróżnienia isError mię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.

  10. Versioning, modelcontextprotocol.io/specification/versioning. Źródło schematu YYYY-MM-DD, stanów rewizji Draft/Current/Final, potwierdzenia, że 2026-07-28 jest obecna, oraz reguł negocjacji per żądanie. Tabela tierów SDK pod modelcontextprotocol.io/docs/sdk wymienia TypeScript, Python, C#, Go i Rust jako Tier 1, Java i Ruby jako Tier 2 oraz Swift, PHP i Kotlin jako Tier 3.

  11. 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.

  12. 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.

Gotowy, żeby to LIA wybierała za Ciebie?

Twórz ze wszystkimi modelami AI w jednym miejscu — zacznij dziś za darmo.