MCP förklarat mot specifikationen: vad en server egentligen är
En rad JSON till en subprocess ger tretton tool-definitioner tillbaka, läst mot revisionen 2026-07-28 som tog bort handskakningen.
På den här sidan
Installera en publicerad MCP-server, skicka den en rad JSON och läs vad som kommer tillbaka.
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}Tretton tool-definitioner, på en enda rad, från en process som läste en rad från sin standard input. Du har nu talat Model Context Protocol, utan SDK, utan client-bibliotek och utan ramverk. Det är hela saken: en transport, ett meddelandeformat och en liten uppsättning namngivna metoder.
Kapitel 18 definierade ett tool som två saker — ett JSON Schema som modellen ser, och en endpoint i din kod som modellen aldrig ser. Kapitel 23 byggde ett harness som håller en katalog över dem. Inget av dem besvarade frågan som avgör om något av detta går att återanvända: vem skriver schemat, och hur tar det sig från den som skrev det in i din prompt? MCP är ett svar på den frågan, och det är värt att läsa i original, eftersom nästan allt som skrivits om det beskriver en revision som inte längre finns.
Tre saker med kommandot du just körde är fel, och var och en är ett avsnitt i det här kapitlet. Det bar ingen protokollversion, så en conformant server skulle ha vägrat det. Det fick ändå ett svar, av ett skäl som specifikationen kallar en risk snarare än en feature. Och det bad om en av tre primitiver utan att någonsin upptäcka att de andra två finns.
Problemet det löser, och analogin specifikationen själv gör
Länk till avsnittet: Problemet det löser, och analogin specifikationen själv görFöre wire, aritmetiken. Du har AI-applikationer och saker de borde kunna nå — en kalender, ett ärendehanteringssystem, en lagerdatabas, ett designverktyg. Utan ett gemensamt kontrakt skriver någon integrationer, och var och en är ett schema plus en endpoint plus en autentiseringshistoria plus en underhållsbörda. Med ett sådant skriver tool-leverantören en server, applikationsleverantören skriver en client, och totalsumman blir .
Det är ingen ny iakttagelse, och specifikationen säger vems idé det var:
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
Ta den jämförelsen bokstavligt snarare än som en komplimang. Före det protokollet innebar stöd för ett språk i en editor ett plugin per editor; efteråt skeppade ett språkteam en server och varje editor fick det. Måttet på framgång var inte elegans, utan att antalet integrationer slutade multipliceras. Samma sak följer här: värdet ligger i antalet implementationer, inte i designen. Ett protokoll som två produkter talar är ett dataformat med extra ceremoni.
Vad som faktiskt finns på wire
Länk till avsnittet: Vad som faktiskt finns på wireMCP-meddelanden är JSON-RPC 2.0. En request är ett objekt med jsonrpc, ett id, ett method och valfria params; ett response bär samma id och antingen result eller error; en notification är en request utan id och får inget svar. Specifikationen lägger tre begränsningar ovanpå: id måste vara en sträng eller ett tal och får inte vara null, det får inte kollidera med en annan request som fortfarande är in flight, och varje result måste bära ett resultType-fält.2
På stdio-transporten — den som kommandot ovan använde — är framing-regeln en rad per meddelande:
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
Den sista klausulen är det vanligaste sättet en hemmabyggd server går sönder, och den går sönder tyst: en vilsekommen console.log, en progress bar, en deprecation-varning från ett dependency, och clientens radparser träffar något som inte är JSON. Nödutgången finns i samma avsnitt — servern may skriva vad den vill till stderr, och clienten should not behandla det som ett fel. Referensservern ovan skriver Starting default (STDIO) server... vid varje start, på stderr, vilket är skälet till att pipen ändå fungerade.
Den andra standardtransporten är Streamable HTTP: varje meddelande är en POST till en enda endpoint, och svaret är antingen ett JSON-objekt eller en request-scoped ström av Server-Sent Events — wire-formatet som Kapitel 14 parsade för hand. Semantiken är identisk på båda, eftersom en transport är en binding: den definierar framing och leverans, inte betydelse.4
Det första som var fel: det fanns ingen version
Länk till avsnittet: Det första som var fel: det fanns ingen versionKommandot ovan skickade tools/list och ingenting annat. Under den aktuella revisionen är den requesten malformed, och en conformant server måste avvisa den.
Sedan 2026-07-28 är MCP ett stateless protocol, och specifikationen säger det utan förbehåll:
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
Alltså bär varje request sin egen protokollversion och sina egna client capabilities, i ett reserverat _meta-objekt inuti params. Två av de fälten krävs på varje enskild request; en request som saknar något av dem är malformed och servern måste svara -32602:2
_meta key | required | vad det är |
|---|---|---|
io.modelcontextprotocol/protocolVersion | ja | revisionen som denna request talar, t.ex. "2026-07-28" |
io.modelcontextprotocol/clientCapabilities | ja | vad clienten kan göra för servern på denna request |
io.modelcontextprotocol/clientInfo | nej (men bör) | clientens namn och version, endast för visning och loggar |
io.modelcontextprotocol/logLevel | nej | den lägsta loggnivå servern bör emit för denna request |
Utskrivet är en korrekt tools/list så här — och det är sista gången det här kapitlet visar metadatan i full form, eftersom den finns på varje request härifrån:
{"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"}}}}Capability-objektet är förhandlingen. Det finns inte längre något separat förhandlingssteg: clienten deklarerar vad den kan göra på varje request, servern deklarerar vad den kan göra i resultatet, och ingen sida får använda en feature som den andra inte har claim. En server som behöver en capability som clienten inte deklarerade måste svara -32021 och namnge den saknade capability i data.requiredCapabilities. En server som inte talar den begärda versionen måste svara -32022 och lista de versioner den talar.2
Clients som vill ha svaret i förväg kan be om det: server/discover är en obligatorisk RPC som returnerar versioner som stöds, capabilities, identity och ett valfritt block av instructions på en round trip.5 Att anropa den är valfritt. Att implementera den är det inte.
Det andra som var fel: servern var legacy
Länk till avsnittet: Det andra som var fel: servern var legacyKommandot fungerade. Under den aktuella revisionen borde det inte ha gjort det, och skälet till att det gjorde det förtjänar en mätning snarare än ett stycke, eftersom det är hela ekosystemets tillstånd på en rad.
Proba referensservern på det sätt specifikationen säger åt en modern client att proba:
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"}}Det är den tredje grenen i kompatibilitetsregeln: ett DiscoverResult betyder modern, ett erkänt modernt fel betyder modern-men-fel-version, och allt annat — inklusive -32601 — betyder legacy, fall tillbaka till initialize-handskakningen.3 Gör alltså det, och be om den aktuella revisionen:
→ {"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":"…"}}Clienten bad om 2026-07-28 och servern svarade 2025-11-25. Den 7 september 2026 implementerar den officiella referensservern — npm-paketet @modelcontextprotocol/server-everything, version 2026.8.31, publicerat 31 augusti 2026 — inte den aktuella revisionen. Inte heller, enligt datumen, gör TypeScript-SDK:t den är byggd på: release 1.30.0 gick ut 27 juli 2026, dagen före revisionen.
Läs konsekvensen snarare än skvallret. Nästan allt som skrivits om MCP beskriver ett protokoll med en initialize-handskakning, en session, en roots/list-request som servern skickar till clienten, och en HTTP+SSE-transport. Alla fyra är borta eller på väg bort. När du läser något om MCP, inklusive den här sidan, är det första du ska leta efter ett revisionsnummer.
Och skälet till att det allra första kommandot fungerade anges i specifikationen som en risk, inte en feature:
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
Uppmätt: att skicka tools/list till den servern utan någon handskakning alls returnerar hela katalogen. En metod som borde ha nekats blev betjänad, vilket är exakt varför specifikationen säger att man ska proba med server/discover först även när man bara stöder moderna versioner.
Tre roller, och meningen att citera från hela dokumentet
Länk till avsnittet: Tre roller, och meningen att citera från hela dokumentetMCP har tre parter, och skillnaden mellan de två första är den folk brukar slå ihop:
Host. Applikationen: chattprodukten, editorn, agent. Den äger konversationen, modellen, credentials och användarens samtycke. Den skapar clients och upprätthåller säkerhetsgränsen mellan dem.
Client. En connector inuti hosten. Varje client talar med exakt en server — en strikt 1:1-relation — och bifogar protokollversion och capabilities till varje request den routar.
Server. En process eller tjänst som exponerar resurser, tools och prompts. Den kan vara lokal eller remote, den arbetar självständigt, och hela dess uppgift är ett avgränsat område.6
Regeln ”exakt en server” är inte bokföring. Det är vad som gör designprincipen nedan implementerbar, och detta är meningen att ta från specifikationen om du bara tar en:
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
Det välter den mentala modell de flesta kommer in med. En väderserver du kopplar till din assistant ser inte vad du frågade. Den ser ett tools/call med argumenten modellen valde, och inget annat — inte tidigare turns, inte din system prompt, inte resultaten som kalenderservern returnerade för ett ögonblick sedan. Om två servrar behöver samarbeta bär hosten ett värde från den ena till den andra, avsiktligt, eftersom modellen bad den att göra det. Därför är isolation den säkerhetsegenskap Kapitel 30 lutar sig mot: en komprometterad server har en liten, definierad blast radius, och att förstora den kräver att hosten samarbetar.
Det tredje: tre primitiver, sorterade efter vem som bestämmer
Länk till avsnittet: Det tredje: tre primitiver, sorterade efter vem som bestämmerDet första kommandot bad servern om tools och fick tretton. Ställ de två andra frågorna till den och den svarar på dem också: resources/list returnerar sju, prompts/list returnerar fyra. Inget av dem dök upp, eftersom inget frågade. Vilket leder oss till MCP:s pedagogiska ryggrad, som ligger i specifikationen som en tabell nästan ingen citerar:
| Primitive | Control | Description | Example |
|---|---|---|---|
| Prompts | Användarstyrda | Interaktiva templates som åkallas av användarens val | Slash commands, menyalternativ |
| Resources | Applikationsstyrda | Kontextdata som bifogas och hanteras av clienten | Filinnehåll, git-historik |
| Tools | Modellstyrda | Funktioner som exponeras för LLM:en för att utföra actions | API POST requests, filskrivning |
Inte ”tre sätt att exponera en capability”. Tre svar på vem bestämmer att detta händer. Modellen bestämmer att den ska anropa ett tool. Applikationen bestämmer att den ska bifoga en resource. Personen bestämmer att den ska köra en prompt. Får du det fel fungerar featuren fortfarande, men den fungerar vid fel ögonblick och av fel skäl.
Det tydligaste sättet att känna det är en kalender. Här är en server som exponerar samma kalender tre gånger, en gång som varje primitive, på hundra rader vanlig Node utan dependencies:
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" });
}Kör den och fråga den på alla tre sätt. Riktig output, ett meddelande per rad på wire, radbrutet här för sidan, med requestens _meta och serverns identity-block utelämnade:
→ 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}Tre metoder, tre former, en kalender. Nu poängen:
Att läsa veckan är en resource
Länk till avsnittet: Att läsa veckan är en resourceDen adresseras av en URI, den är inert, och applikationen bestämmer om den ska bifogas till konversationen. Inget i protokollet låter modellen sträcka sig efter den på egen hand. Resultatet bär ttlMs och cacheScope, nya i denna revision, så clienten kan cache veckan i en minut i stället för att polla.
Att skapa ett event är ett tool
Länk till avsnittet: Att skapa ett event är ett toolDet har ett schema, det har side effects, och modellen bestämmer när det ska anropas. Resultatet bär isError, vilket är fältet Kapitel 18 argumenterade för: ett validation failure kommer tillbaka som ett tool-resultat som modellen kan läsa och korrigera, inte som ett protocol error.
”Förbered min vecka” är en prompt
Länk till avsnittet: ”Förbered min vecka” är en promptDet är en namngiven template med argument som personen åkallar — slash command i menyn. Den returnerar meddelanden, inte ett svar. Det är ett sätt för en serverförfattare att skeppa formuleringen som fungerar med deras egna tools, vilket är exakt den kunskap serverförfattaren har och användaren inte har.
Nästan alla gör alla tre till tools. Resultatet är en katalog där en read som applikationen borde ha bifogat tyst konkurrerar om modellens attention med en write som behöver approval, och där den enda sak en person ville ha en knapp för ligger begravd i ett schema. Det kostar inget att göra rätt, och det avgörs innan du skriver en rad.
Servern kan inte ringa dig
Länk till avsnittet: Servern kan inte ringa digKalenderverktyget har ett obligatoriskt argument, title, och ett valfritt startsAt. Be det skapa ett event utan datum, och något intressant kommer tillbaka:
→ 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=="}Servern skickade ingen request. Den besvarade den den fick, med resultType: "input_required" och en beskrivning av vad den fortfarande behöver. Clienten samlar in svaret från personen och skickar sedan om det ursprungliga anropet — med ett nytt id, som bär inputResponses och ekar tillbaka det ogenomskinliga 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}Detta är Multi Round-Trip Requests, introducerat i den aktuella revisionen, och det ersatte den äldre designen där servrar skickade JSON-RPC-requests tillbaka till clients. Transportspecifikationen anger nu regeln rakt ut: ”servers do not initiate JSON-RPC requests and clients do not send JSON-RPC responses”.4 Det finns en riktning för initiativ, och den tillhör hosten.
Två client-side features rider på den mekanismen, och en av dem har ett namn som kommer att lura dig.
Elicitation är servern som ber personen om något: ett formulär med ett avsiktligt begränsat JSON Schema — platta objekt, primitive properties, ingen nesting — så att vilken client som helst kan rendera det utan en layout engine. Den bär en hård regel: servrar must not använda form mode för att be om ”passwords, API keys, access tokens, or payment credentials”, och must använda URL mode för sådant, vilket skickar användaren till en sida clienten aldrig läser.7
Sampling är servern som ber hostens modell om en generation, så att en server kan vara intelligent utan att hålla en API key. Och här är vokabulärvarningen, eftersom detta ord redan betyder något annat i den här kursen: detta är inte Kapitel 17:s sampling. Inget här handlar om temperature, top-p eller formen på en probability distribution. Det är ett nästat modellanrop som färdas bakåt genom ett protokoll.
Det finns ett andra skäl att inte sträcka sig efter det: från och med denna revision är sampling deprecated, tillsammans med roots och logging, under SEP-2577, med en rättfram föreslagen migration — ”integrate directly with LLM provider APIs instead of Sampling”.8 Idén misslyckades inte tekniskt; den misslyckades med att motivera sin surface area, och ett protokoll som kan ta bort saker är friskare än ett som inte kan det.
Ha sönder det med flit: anslutningar är inte sessioner
Länk till avsnittet: Ha sönder det med flit: anslutningar är inte sessionerStatelessness låter som en wire-format-detalj tills du testar det. Ta tre-meddelandeutbytet ovan och kör varje meddelande i en separat process — en färsk node calendar.mjs, inget delat minne, inget som bärs över:
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)Process B, som aldrig såg frågan, slutförde ett multi-round-trip-anrop som process A startade. Det är poängen med requestState: continuation färdas i meddelandet, så inget beror på att processen är densamma.
Process C är felet. Eventet skapades och finns inte där — eftersom toy-servern håller EVENTS i en array på modulnivå, och en array på modulnivå är connection state. Specifikationens note namnger misstaget exakt:
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
Den föreskrivna fixen är inte en session. Det är ett explicit handle: ett creation tool returnerar en opaque identifier, och varje senare anrop tar den som ett vanligt argument. Protokollet har inget begrepp om den alls — ”from the wire's perspective a handle is an ordinary string in a tool result and an ordinary argument to subsequent tool calls”.9 Vilket sätter modellen i ansvar för att bära den, och sätter servern i ansvar för att validera att den här anroparen får använda den vid varje enskilt anrop, eftersom ett handle är ett namn och inte en permission.
Vad en server kostar innan den gör något
Länk till avsnittet: Vad en server kostar innan den gör någotVarje tool en server exponerar är ett schema som går in i din prompt på varje request, och Kapitel 24 mätte vad det gör med ett window. MCP lägger till en andra post som är lätt att missa, så båda är värda att räkna på referensservern ovan.
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 tokensTvå observationer. Den första är aritmetik: koppla fem servrar av den här storleken och ungefär åtta tusen token av ditt window är upptagna på varje turn, för alltid, oavsett om modellen använder någon av dem eller inte — vilket är mekanismen bakom minskningen från 150 000 till 2 000 som Kapitel 24 citerade, och skälet till att just-in-time tool discovery finns.
Den andra är en säkerhetsnote i redovisningskostym. instructions är natural-language text, written by the server author, that lands in the host's prompt, och tool-beskrivningarna bredvid den är samma sak. Specifikationen säger vad man ska göra åt det i sina egna säkerhetsprinciper: tool annotations och descriptions ”should be considered untrusted, unless obtained from a trusted server”, och hosts ”must obtain explicit user consent before invoking any tool”.1 Att ansluta en MCP-server är inte att lägga till ett dependency. Det är att ge en främling 1 619 token av din system prompt och rätten att bli anropad. Kapitel 30 är vad som händer när den främlingen är fientlig.
Daterat avsnitt: revisionen 2026-07-28, och vad den bryter
Länk till avsnittet: Daterat avsnitt: revisionen 2026-07-28, och vad den bryterAllt i det här avsnittet är sant för protokollrevision 2026-07-28, den aktuella, läst den 7 september 2026. Revisioner dateras YYYY-MM-DD och datumet är senaste gången en bakåtinkompatibel ändring gjordes.10 Det normativa dokumentet är en TypeScript-fil, schema/2026-07-28/schema.ts; JSON Schema bredvid genereras från den, vilket är varför specifikationen här läses i TypeScript och varför att undervisa MCP från något annat är att undervisa en översättning.
| Vad som ändrades | Var | Är nu | Bryter |
|---|---|---|---|
| Handskakningen | initialize + notifications/initialized, en gång per anslutning | borttagen; varje request bär _meta version och capabilities | varje client skriven före denna revision |
| Sessioner | Mcp-Session-Id-header, connection-scoped state | borttagen; state färdas i explicita, server-mintade handles | list-endpoints som varierade per anslutning |
| Discovery | härledd från initialize-resultatet | server/discover, som servrar måste implementera | ingenting, men den är nu obligatorisk att implementera |
| Server-till-client-anrop | servern skickade roots/list, sampling/createMessage, elicitation/create | InputRequiredResult och en client retry | varje server som pushade en request till en client |
| Result shape | valfritt objekt | krävt resultType: "complete" eller "input_required" | ingenting: ett frånvarande fält måste läsas som "complete" |
| Subscriptions | HTTP GET stream, resources/subscribe | en subscriptions/listen-stream med opt-in-typer | GET-endpointen är borta |
| Stream resumption | Last-Event-ID replay på Streamable HTTP | borttagen; en trasig stream förlorar requesten, skicka om med ett nytt id | clients som litade på redelivery |
| Roots | en client feature servrar kunde be om | deprecated (SEP-2577); skicka paths som tool-argument eller resource URIs | ingenting än — tolv månaders fönster |
| Sampling och logging | client features | deprecated (SEP-2577) | ingenting än — tolv månaders fönster |
| HTTP+SSE-transport | deprecated sedan 2025-03-26 | Deprecated enligt lifecycle policy (SEP-2596) | migrera till Streamable HTTP |
| Client registration | OAuth 2.0 Dynamic Client Registration, RFC 7591 | deprecated till förmån för Client ID Metadata Documents | behålls för authorization servers utan dem |
| Error codes | -32002 för resource not found | -32602; -32020–-32099 reserverade för specen | nya codes -32020, -32021, -32022 |
Governance-ändringen under den tabellen spelar större roll än någon enskild rad. Denna revision antog en feature lifecycle and deprecation policy: features är Active, Deprecated eller Removed, en deprecated feature dokumenterar sin migrationsväg och stannar i specifikationen i minst tolv månader innan den blir eligible for removal, och det finns ett registry som listar allt som för närvarande är i Deprecated state.8 Före den policyn betydde ”deprecated” i ett AI-protokoll vad det senaste blogginlägget än sa. Nu betyder det ett datum.
Visa detaljer
Extensions, som är delen nästan ingen har skrivit om än.
Bortom kärnan definierar MCP valfria extensions — ”always opt-in and require explicit support from both client and server”, deklarerade via ett extensions-fält i clientens och serverns capabilities.1 Tre är värda att känna till vid namn:
- Tasks (
io.modelcontextprotocol/tasks), flyttades ut ur kärnprotokollet till en officiell extension i denna revision: asynkron körning av långvariga operationer, med polling genomtasks/get, mid-flight input genomtasks/update, och durable handles. Det är svaret på ett tool som tar tjugo minuter, vilket Kapitel 23 hanterade med ett progress event och en signal som når tool. - Skills over MCP, en arbetsgrupp som gör agent skills — Kapitel 28:s ämne — discoverable och consumable genom protokollet.
- MCP Apps, interaktiv UI som renderas inline i konversationen: diagram, formulär, videospelare.
Och notera vad ”negotiated” betyder nu: det finns ingen initialization att förhandla vid, så en extension deklareras per request precis som allt annat.
Var MCP hör hemma, jämfört med allt det förväxlas med
Länk till avsnittet: Var MCP hör hemma, jämfört med allt det förväxlas medDet här är vokabulären för hela blocket på ett ställe.
| Vad det är | Vem talar med vem | När det är svaret | |
|---|---|---|---|
| Ett vanligt API | Ett interface för ett program | din kod ↔ en tjänst | Du skriver anroparen. Du kontrollerar schemat, auth och error handling, och det finns inget discovery-problem att lösa. |
| MCP | Ett protokoll för att exponera tools, data och templates för en AI-applikation | host ↔ server, en client vardera | Någon annan skrev capability och många hosts borde kunna använda den utan en bespoke integration. |
| RAG | En teknik för att hitta text och lägga den i prompt | din kod ↔ ditt index | Modellen behöver veta något. Kapitel 19. MCP är ett sätt att leverera en retriever; det är inte en retriever. |
| Agent skills | En folder med en SKILL.md som modellen läser | modell ↔ ett dokument | Kunskapen är proceduriell — hur vi gör detta — och den är prosa, inte en funktion. Kapitel 28. |
| A2A | Ett protokoll för agents att samarbeta som peers | agent ↔ agent | Andra sidan resonerar, planerar och håller state över en lång task, i stället för att besvara ett anrop. |
| ACP | Var ett separat agent-kommunikationsprotokoll | — | Det är inte längre en live-jämförelse. Se nedan. |
Två av dem förtjänar varsin mening, eftersom det är där förvirringen faktiskt bor.
MCP mot A2A är ingen rivalitet, och båda specifikationerna säger det. A2A-dokumentationen drar gränsen efter vad som finns i andra änden: MCP ”defines how an AI agent interacts with and utilizes individual tools and resources, such as a database or an API”, där ett tool utför ”specific, often stateless, functions”; A2A adresserar agents, ”more autonomous systems” som ”reason, plan, use multiple tools, maintain state over longer interactions, and engage in complex, often multi-turn dialogues”. Dess egen sammanfattning är meningen att minnas: ”A2A is about agents partnering on tasks, while MCP is more about agents using capabilities.”11 De två nestar — en applikation använder A2A för att nå andra agents, och varje agent använder MCP för att nå sina egna tools. Kapitel 25 drog den gränsen inuti en process, mellan att fråga en sub-agent och lämna över konversationen till den; A2A drar den mellan organisationer.
MCP mot ACP är en jämförelse med en stale premise, vilket är exakt varför den är värd att besvara. Agent Communication Protocol var en separat öppen standard för agent-to-agent-meddelanden. Dess egen dokumentation öppnar nu med notisen: ”ACP is now part of A2A under the Linux Foundation!”12 Det ärliga svaret på ”MCP eller ACP?” i september 2026 är att frågan har ett alternativ mindre än sidorna som rankar för den antyder.
Och jämförelsen folk oftast ber om, mcp vs api, har det minst intressanta svaret: MCP är ett API. Det den lägger till är inte kraft, utan conventions — en fast uppsättning metodnamn, ett discovery call, en control hierarchy över primitiverna och en isolation model. Du ger upp friheten att designa ditt eget interface och får varje host som talar protokollet, vilket är det trade varje protokoll alltid har erbjudit.
Vart det här går härnäst
Länk till avsnittet: Vart det här går härnästDu kan nu läsa specifikationen utan en översättare, skilja en resource från ett tool från en prompt efter vem som bestämmer över den, skriva en request för hand när ett client-bibliotek ljuger för dig, och datera varje MCP-artikel du läser efter vilka av de deprecated features den fortfarande lär ut som aktuella.
Vad du inte har gjort är att skeppa en. Kapitel 27 skriver samma server två gånger — TypeScript och Python, sida vid sida, eftersom MCP är det enda genuint tvåspråkiga territoriet i den här kursen och siffrorna säger det åt båda hållen. Det täcker de två live-transporterna ordentligt, inspectorn, packaging, och den halva av protokollet som detta kapitel avsiktligt lämnade ifred: authorization. För i samma ögonblick som din server är remote snarare än en subprocess på din egen laptop kommer en främlings client att presentera en token, och specifikationens regel om vad du får göra med den är ovanligt strikt.
Vilket väcker frågan som nästa kapitel måste besvara, och den är inte vänlig: om en token anländer till din server och den utfärdades för någon annans audience, vad exakt hindrar dig från att vidarebefordra den?
Källor och metod
Länk till avsnittet: Källor och metodVarje citat, metodnamn, error code och regel i detta kapitel lästes från Model Context Protocol-specifikationen, revision 2026-07-28, den 7 september 2026. Varje trace producerades lokalt på Node 22: toy-kalenderservern är 101 rader utan dependencies, och referensservern är det publicerade npm-paketet som namnges nedan. Inget betalt API anropades för att skriva detta kapitel — inget här behöver en modell, vilket i sig är poängen.
Mätningarna: @modelcontextprotocol/server-everything@2026.8.31, publicerat 31 augusti 2026, byggt på @modelcontextprotocol/sdk@1.30.0, publicerat 27 juli 2026 — en dag före revisionen detta kapitel beskriver. Det svarar server/discover med -32601, förhandlar 2025-11-25 när det ombeds om 2026-07-28, och serverar tools/list utan någon handskakning alls. Dess katalog är 13 tools i 7 663 byte; token-antal är o200k_base via tiktoken, över name, description och inputSchema i varje definition, vilket är vad en provider renderar in i din prompt och inte vad JSON-RPC-framen väger.
Anthropic, Code execution with MCP: building more efficient agents, 4 november 2025, är källan till siffran 150 000-till-2 000, citerad och använd i Kapitel 24 och bara refererad här.
Referenser
Länk till avsnittet: Referenser-
Specification,
modelcontextprotocol.io/specification/latest(omdirigerar till/2026-07-28), läst 7 september 2026. Källa till jämförelsen med Language Server Protocol; uttalandet att specifikationen är ”based on the TypeScript schema inschema.ts”; base-protocol-sammanfattningen (”Stateless, self-contained requests”, ”Per-request capability negotiation”); extension-listan (Tasks, Skills over MCP, MCP Apps) och uttalandet att extensions ”are always opt-in and require explicit support from both client and server”; samt Security and Trust & Safety-principerna, inklusive ”Hosts must obtain explicit user consent before invoking any tool” och behandlingen av tool annotations som untrusted. ↩ ↩2 ↩3 -
Base Protocol,
modelcontextprotocol.io/specification/2026-07-28/basic. Källa till JSON-RPC-begränsningarna (non-null id, ingen id reuse, obligatorisktresultType); avsnittet Statelessness och dess note om att en öppen stdio-process inte är en session; tabellen med_metareserved-key och required/optional-status för varje per-request-fält; regeln-32602för ett saknat required field; regelnMissingRequiredClientCapability(-32021); och policyn för error-code allocation. ↩ ↩2 ↩3 ↩4 ↩5 -
stdio transport,
modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio. Källa till newline-delimited framing-reglerna,stdoutpurity requirement,stderrallowance och backward-compatibility-proben med tre utfall — inklusive varningen att vissa legacy-servrar processar era-ambiguous methods utan handskakning, vilket mätningen i detta kapitel reproducerar. ↩ ↩2 ↩3 -
Transports overview,
modelcontextprotocol.io/specification/2026-07-28/basic/transports. Källa till formuleringen ”a transport is a binding” och till uttalandet att servrar inte initierar JSON-RPC requests och clients inte skickar JSON-RPC responses. ↩ ↩2 -
Discovery,
modelcontextprotocol.io/specification/2026-07-28/server/discover. Källa till attserver/discoverär obligatorisk, formen påDiscoverResultoch fältetinstructionssom beskrivs som ”optional natural-language guidance for LLMs on how to use this server effectively”. ↩ -
Architecture,
modelcontextprotocol.io/specification/2026-07-28/architecture. Källa till definitionerna av host/client/server, 1:1-regeln client-till-server, de fyra designprinciperna, varav isolation-principen citeras här utan sin femte bullet, ”Host process enforces security boundaries”, och avsnittet om capability negotiation. ↩ ↩2 -
Elicitation,
.../client/elicitation, och Sampling,.../client/sampling. Källa till de två elicitation-lägena och deras begränsade schema; förbudet mot att begära credentials genom form mode; definitionen av sampling, dess human-in-the-loop-krav och deprecation-varningen som är kopplad till den. ↩ -
Key Changes,
modelcontextprotocol.io/specification/2026-07-28/changelog, och Feature lifecycle and deprecation policy,.../community/feature-lifecycle. Källa till varje rad i ändringstabellen: borttagning av sessions ochMcp-Session-Id-headern (SEP-2567); statelessness och borttagningen avinitialize(SEP-2575);server/discover(SEP-2575);subscriptions/listen(SEP-2575); Multi Round-Trip Requests ochresultType(SEP-2322); borttagning av stream resumability (SEP-2575); deprecation av Roots, Sampling och Logging (SEP-2577); omklassificeringen av HTTP+SSE (SEP-2596); deprecation av Dynamic Client Registration till förmån för Client ID Metadata Documents; omnumreringen av error codes; och det tolv månader långa deprecation-fönstret. ↩ ↩2 -
Tools,
modelcontextprotocol.io/specification/2026-07-28/server/tools, och Server Features,.../server. Källa till control-hierarchy-tabellen som återges ovan; formernatools/listochtools/call; distinktionenisErrormellan protocol errors och tool execution errors; tool-name-reglerna och namespace-noten som rekommenderar ”prefixing tool names with a server identifier”; samt den non-normative vägledningen ”Stateful Tools” om explicit handles. ↩ -
Versioning,
modelcontextprotocol.io/specification/versioning. Källa tillYYYY-MM-DD-schemat, revisionslägena Draft/Current/Final, bekräftelsen att 2026-07-28 är current, och per-request-förhandlingsreglerna. SDK tier-tabellen påmodelcontextprotocol.io/docs/sdklistar TypeScript, Python, C#, Go och Rust på Tier 1, Java och Ruby på Tier 2, och Swift, PHP och Kotlin på Tier 3. ↩ -
A2A Protocol, version 1.0.0,
a2a-protocol.org— specifikationen och sidan A2A and MCP: Relationship and Distinction, läst 7 september 2026. Källa till distinktionen tools-mot-agents, uttalandet att de två protokollen ”address distinct but highly complementary needs”, och partnering/using-formuleringen. ↩ -
Agent Communication Protocol,
agentcommunicationprotocol.dev, läst 7 september 2026: ”ACP is now part of A2A under the Linux Foundation!”, en banner tillagd ovanför en specifikation som fortfarande serveras hel — architecture, agent manifest, agent discovery, message structure, stateful agents, run lifecycle och REST-endpoint-listan svarar alla fortfarande 200. Specifikationen försvann inte; projektet gjorde det. ↩