MCP forklaret op mod specifikationen: hvad en server egentlig er
Én linje JSON ind i en subprocess, tretten tool-definitioner tilbage — læst op mod 2026-07-28-revisionen uden handshake.
På denne side
Installer en udgivet MCP-server, send den én linje JSON, og læs hvad der kommer tilbage.
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}Tretten tool-definitioner, på én enkelt linje, fra en proces der læste én linje fra sin standard input. Du har nu talt Model Context Protocol, uden SDK, uden client-bibliotek og uden framework. Det er det hele: en transport, et beskedformat og et lille sæt navngivne metoder.
Kapitel 18 definerede et værktøj som to ting — et JSON Schema, som modellen ser, og et endpoint i din kode, som modellen aldrig ser. Kapitel 23 byggede et harness, der holder et katalog over dem. Ingen af dem besvarede det spørgsmål, der afgør, om noget af det kan genbruges: hvem skriver schemaet, og hvordan kommer det fra den, der skrev det, ind i din prompt? MCP er ét svar på det spørgsmål, og det er værd at læse i originalen, fordi næsten alt, der er skrevet om det, beskriver en revision, der ikke længere findes.
Tre ting ved den kommando, du lige kørte, er forkerte, og hver af dem er et afsnit i dette kapitel. Den havde ingen protocol version med, så en conformant server ville have afvist den. Den fik alligevel et svar af en grund, som specifikationen kalder en hazard snarere end en feature. Og den bad om én af tre primitiver uden nogensinde at opdage, at de to andre findes.
Problemet den løser, og analogien specifikationen selv bruger
Link til afsnittet: Problemet den løser, og analogien specifikationen selv brugerFør wiren, regnestykket. Du har AI-applikationer og ting, de burde kunne nå — en kalender, et ticket-system, en warehouse-database, et designværktøj. Uden en fælles kontrakt skriver nogen integrationer, og hver eneste af dem er et schema plus et endpoint plus en autentificeringshistorie plus en vedligeholdelsesbyrde. Med én skriver tool-leverandøren en server, applikationsleverandøren skriver en client, og totalen er .
Det er ikke en ny observation, og specifikationen siger, hvis 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
Tag den sammenligning bogstaveligt, ikke som en kompliment. Før den protokol betød understøttelse af et sprog i en editor et plugin per editor; bagefter udgav et sprogteam én server, og alle editorer fik den. Succeskriteriet var ikke elegance, men at antallet af integrationer holdt op med at multiplicere. Det samme gælder her: værdien ligger i antallet af implementeringer, ikke i designet. En protokol, som to produkter taler, er et dataformat med ekstra ceremoni.
Hvad der faktisk ligger på wiren
Link til afsnittet: Hvad der faktisk ligger på wirenMCP-beskeder er JSON-RPC 2.0. En request er et objekt med jsonrpc, et id, en method og valgfri params; et response bærer det samme id og enten result eller error; en notification er en request uden id og får intet svar. Specifikationen tilføjer tre begrænsninger ovenpå: id skal være en string eller et tal og må ikke være null, det må ikke kollidere med en anden request, der stadig er in flight, og hvert result skal bære et resultType-felt.2
På stdio-transporten — den kommandoen ovenfor brugte — er framing-reglen én linje per besked:
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 sidste sætning er den mest almindelige måde, en hjemmelavet server går i stykker på, og den går i stykker lydløst: et tilfældigt console.log, en progress bar, en deprecation warning fra en dependency, og clientens line parser rammer noget, der ikke er JSON. Nødudgangen står i samme afsnit — serveren må skrive hvad som helst til stderr, og clienten bør ikke behandle det som en fejl. Reference-serveren ovenfor printer Starting default (STDIO) server... ved hver start, på stderr, og derfor virkede pipen stadig.
Den anden standardtransport er Streamable HTTP: hver besked er en POST til et enkelt endpoint, og svaret er enten et JSON-objekt eller en request-scoped stream af Server-Sent Events — wire-formatet Kapitel 14 parsede i hånden. Semantikken er identisk på begge, fordi en transport er en binding: den definerer framing og levering, ikke betydning.4
Den første ting der var forkert: der var ingen version
Link til afsnittet: Den første ting der var forkert: der var ingen versionKommandoen ovenfor sendte tools/list og intet andet. Under den nuværende revision er den request malformed, og en conformant server skal afvise den.
Siden 2026-07-28 har MCP været en stateless protokol, og specifikationen siger det uden forbehold:
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
Så hver request bærer sin egen protocol version og sine egne client capabilities i et reserveret _meta-objekt inde i params. To af de felter er påkrævet på hver eneste request; en request, der mangler et af dem, er malformed, og serveren skal svare -32602:2
_meta key | påkrævet | hvad det er |
|---|---|---|
io.modelcontextprotocol/protocolVersion | ja | den revision denne request taler, fx "2026-07-28" |
io.modelcontextprotocol/clientCapabilities | ja | hvad clienten kan gøre for serveren på denne request |
io.modelcontextprotocol/clientInfo | nej (men bør) | clientnavn og version, kun til visning og logs |
io.modelcontextprotocol/logLevel | nej | det mindste logniveau serveren bør udsende for denne request |
Skrevet ud er en korrekt tools/list sådan her — og det er sidste gang, kapitlet viser metadataene i fuld længde, fordi de er på hver request herfra:
{"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 er forhandlingen. Der er ikke længere et separat negotiation step: clienten erklærer, hvad den kan på hver request, serveren erklærer, hvad den kan i resultatet, og ingen af siderne må bruge en feature, den anden ikke har claimed. En server, der har brug for en capability, clienten ikke erklærede, skal svare -32021 og navngive den manglende capability i data.requiredCapabilities. En server, der ikke taler den efterspurgte version, skal svare -32022 og liste de versioner, den taler.2
Clients, der vil have svaret på forhånd, kan bede om det: server/discover er en obligatorisk RPC, der returnerer understøttede versioner, capabilities, identity og en valgfri blok med instructions i én round trip.5 Det er valgfrit at kalde den. Det er ikke valgfrit at implementere den.
Den anden ting der var forkert: serveren var legacy
Link til afsnittet: Den anden ting der var forkert: serveren var legacyKommandoen virkede. Under den nuværende revision burde den ikke have gjort det, og grunden til, at den gjorde, er værd at måle frem for at beskrive i et afsnit, fordi det er hele økosystemets tilstand på én linje.
Probe reference-serveren sådan som specifikationen siger, at en modern client skal probe:
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 er den tredje gren af kompatibilitetsreglen: en DiscoverResult betyder modern, en genkendt modern error betyder modern-men-forkert-version, og alt andet — inklusive -32601 — betyder legacy, fald tilbage til initialize-handshaket.3 Så gør det, og bed om den nuværende revision:
→ {"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, og serveren svarede 2025-11-25. Den 7. september 2026 implementerer den officielle reference-server — npm-pakken @modelcontextprotocol/server-everything, version 2026.8.31, udgivet 31. august 2026 — ikke den nuværende revision. Det gør TypeScript-SDK'et, den er bygget på, heller ikke på datoerne: release 1.30.0 udkom 27. juli 2026, dagen før revisionen gjorde.
Læs konsekvensen, ikke sladderen. Næsten alt, der er skrevet om MCP, beskriver en protokol med et initialize-handshake, en session, en roots/list-request serveren sender til clienten, og en HTTP+SSE-transport. Alle fire er væk eller på vej væk. Når du læser noget om MCP, inklusive denne side, er det første du skal lede efter et revisionsnummer.
Og grunden til, at den allerførste kommando virkede, står i specifikationen som en hazard, ikke 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
Målt: at sende tools/list til den server uden noget handshake overhovedet returnerer hele kataloget. En metode, der burde være blevet afvist, blev serveret, og det er præcis derfor specifikationen siger, at man skal probe med server/discover først, selv når man kun understøtter modern versions.
Tre roller, og sætningen du skal citere fra hele dokumentet
Link til afsnittet: Tre roller, og sætningen du skal citere fra hele dokumentetMCP har tre parter, og skellet mellem de første to er det, folk kollapser:
Host. Applikationen: chatproduktet, editoren, agenten. Den ejer samtalen, modellen, credentials og brugerens consent. Den opretter clients og håndhæver sikkerhedsgrænsen mellem dem.
Client. En connector inde i hosten. Hver client taler med præcis én server — et strengt 1:1-forhold — og vedhæfter protocol version og capabilities til hver request, den router.
Server. En proces eller en service, der eksponerer ressourcer, værktøjer og prompts. Den kan være lokal eller remote, den opererer uafhængigt, og hele dens job er ét fokuseret område.6
Reglen om "præcis én server" er ikke bogholderi. Det er det, der gør designprincippet nedenfor implementerbart, og dette er sætningen, du skal tage med fra specifikationen, hvis du kun tager én:
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 mentale model, de fleste kommer med. En vejrserver, du forbinder til din assistant, ser ikke hvad du spurgte om. Den ser et tools/call med de argumenter, modellen valgte, og intet andet — ikke de foregående turns, ikke din system prompt, ikke de resultater kalender-serveren returnerede et øjeblik tidligere. Hvis to servere skal samarbejde, bærer hosten en værdi fra den ene til den anden, bevidst, fordi modellen bad om det. Derfor er isolation den sikkerhedsegenskab, Kapitel 30 læner sig op ad: en compromised server har en lille, defineret blast radius, og at udvide den kræver, at hosten samarbejder.
Den tredje ting: tre primitiver, sorteret efter hvem der bestemmer
Link til afsnittet: Den tredje ting: tre primitiver, sorteret efter hvem der bestemmerDen første kommando bad serveren om værktøjer og fik tretten. Stil den de to andre spørgsmål, og den svarer også på dem: resources/list returnerer syv, prompts/list returnerer fire. Ingen af dem dukkede op, fordi intet spurgte. Det bringer os til MCP's pædagogiske rygrad, der ligger i specifikationen som en tabel, næsten ingen citerer:
| Primitiv | Kontrol | Beskrivelse | Eksempel |
|---|---|---|---|
| Prompts | Brugerstyret | Interaktive templates udløst af brugerens valg | Slash commands, menuindstillinger |
| Ressourcer | Applikationsstyret | Kontekstuelle data vedhæftet og administreret af clienten | Filindhold, git-historik |
| Værktøjer | Modelstyret | Funktioner eksponeret for LLM'en for at udføre handlinger | API POST requests, filskrivning |
Ikke "tre måder at eksponere en capability på". Tre svar på hvem der beslutter, at dette sker. Modellen beslutter at kalde et værktøj. Applikationen beslutter at vedhæfte en ressource. Personen beslutter at køre en prompt. Får du det forkert, virker featuren stadig, men den virker på det forkerte tidspunkt og af den forkerte grund.
Den klareste måde at mærke det på er en kalender. Her er en server, der eksponerer den samme kalender tre gange, én gang som hver primitiv, i hundrede linjer plain Node uden 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, og spørg den på alle tre måder. Rigtigt output, én besked per linje på wiren, ombrudt her til siden, med request _meta og serverens identity block udeladt:
→ 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, én kalender. Nu pointen:
At læse ugen er en ressource
Link til afsnittet: At læse ugen er en ressourceDen adresseres af en URI, den er inert, og applikationen beslutter, om den skal vedhæftes samtalen. Intet i protokollen lader modellen række ud efter den på egen hånd. Resultatet bærer ttlMs og cacheScope, nye i denne revision, så clienten kan cache ugen i et minut i stedet for at poll'e.
At oprette en begivenhed er et værktøj
Link til afsnittet: At oprette en begivenhed er et værktøjDet har et schema, det har side effects, og modellen beslutter, hvornår det skal kaldes. Dets resultat bærer isError, som er feltet Kapitel 18 argumenterede for: en valideringsfejl kommer tilbage som et tool-result, modellen kan læse og rette, ikke som en protokolfejl.
"Forbered min uge" er en prompt
Link til afsnittet: "Forbered min uge" er en promptDet er en navngivet template med argumenter, som personen udløser — slash commanden i menuen. Den returnerer beskeder, ikke et svar. Det er en måde for en serverforfatter at sende den formulering, der virker med deres egne værktøjer, hvilket præcis er den viden, serverforfatteren har, og brugeren ikke har.
Næsten alle gør alle tre af disse til værktøjer. Resultatet er et katalog, hvor en læsning, applikationen burde have vedhæftet lydløst, konkurrerer om modellens attention med en skrivning, der kræver approval, og hvor den ene ting, en person ville have en knap til, er begravet i et schema. Det koster ingenting at gøre rigtigt, og det afgøres, før du skriver en linje.
Serveren kan ikke kalde dig
Link til afsnittet: Serveren kan ikke kalde digKalenderværktøjet har ét påkrævet argument, title, og et valgfrit startsAt. Bed den om at oprette en begivenhed uden en dato, og noget interessant kommer tilbage:
→ 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=="}Serveren sendte ikke en request. Den besvarede den, den fik, med resultType: "input_required" og en beskrivelse af, hvad den stadig mangler. Clienten indsamler svaret fra personen og sender derefter det oprindelige call igen — med et nyt id, med inputResponses og med det opaque requestState ekkoet tilbage:
→ 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}Dette er Multi Round-Trip Requests, introduceret i den nuværende revision, og det erstattede det ældre design, hvor servere sendte JSON-RPC requests tilbage til clients. Transport-specifikationen fastslår nu reglen direkte: "servers do not initiate JSON-RPC requests and clients do not send JSON-RPC responses".4 Der er én retning for initiativ, og den tilhører hosten.
To client-side features rider på den mekanisme, og den ene har et navn, der vil få dig til at snuble.
Elicitation er serveren, der beder personen om noget: en formular med et bevidst begrænset JSON Schema — flade objekter, primitive properties, ingen nesting — så enhver client kan render den uden en layout engine. Den bærer en hård regel: servere må ikke bruge form mode til at bede om "passwords, API keys, access tokens, or payment credentials", og skal bruge URL mode til det, som sender brugeren til en side, clienten aldrig læser.7
Sampling er serveren, der beder hostens model om en generation, så en server kan være intelligent uden at holde en API key. Og her er ordforrådsadvarslen, fordi dette ord allerede betyder noget andet i dette kursus: det er ikke Kapitel 17's sampling. Intet her handler om temperature, top-p eller formen på en probability distribution. Det er et nested model call, der rejser baglæns gennem en protokol.
Der er en anden grund til ikke at række ud efter det: fra denne revision er sampling deprecated, sammen med roots og logging, under SEP-2577, med en direkte foreslået migration — "integrate directly with LLM provider APIs instead of Sampling".8 Idéen fejlede ikke teknisk; den fejlede i at retfærdiggøre sit surface area, og en protokol, der kan fjerne ting, er sundere end en, der ikke kan.
Ødelæg det med vilje: forbindelser er ikke sessions
Link til afsnittet: Ødelæg det med vilje: forbindelser er ikke sessionsStatelessness lyder som en wire-format-detalje, indtil du tester den. Tag trebeskeds-udvekslingen ovenfor og kør hver besked i en separat proces — en frisk node calendar.mjs, ingen delt hukommelse, intet båret videre:
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, som aldrig så spørgsmålet, fuldførte et multi-round-trip call, som proces A startede. Det er pointen med requestState: continuation rejser i beskeden, så intet afhænger af, at processen er den samme.
Proces C er fejlen. Begivenheden blev oprettet, og den er ikke der — fordi toy-serveren holder EVENTS i et module-level array, og et module-level array er connection state. Specifikationens note navngiver fejlen præcist:
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 foreskrevne løsning er ikke en session. Det er et explicit handle: et creation tool returnerer en opaque identifier, og hvert senere call tager den som et almindeligt argument. Protokollen har slet intet begreb om det — "from the wire's perspective a handle is an ordinary string in a tool result and an ordinary argument to subsequent tool calls".9 Det sætter modellen til at bære den, og serveren til at validere, at denne caller har lov til at bruge den på hvert eneste call, fordi et handle er et navn og ikke en tilladelse.
Hvad en server koster, før den gør noget
Link til afsnittet: Hvad en server koster, før den gør nogetHvert værktøj, en server eksponerer, er et schema, der går ind i din prompt på hver request, og Kapitel 24 målte, hvad det gør ved et window. MCP tilføjer en anden linjepost, der er let at overse, så begge er værd at tælle på reference-serveren ovenfor.
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 tokensTo observationer. Den første er regning: forbind fem servere af denne størrelse, og omtrent otte tusind tokens af dit window er optaget på hvert turn, for altid, uanset om modellen bruger nogen af dem eller ej — hvilket er mekanismen bag reduktionen fra 150.000 til 2.000, som Kapitel 24 citerede, og grunden til at just-in-time tool discovery findes.
Den anden er en sikkerhedsnote i regnskabskostume. instructions er natural-language text, skrevet af serverforfatteren, der lander i hostens prompt, og tool-beskrivelserne ved siden af er det samme. Specifikationen siger, hvad man skal gøre ved det i sine egne sikkerhedsprincipper: tool annotations og descriptions "should be considered untrusted, unless obtained from a trusted server", og hosts "must obtain explicit user consent before invoking any tool".1 At forbinde en MCP-server er ikke at tilføje en dependency. Det er at give en fremmed 1.619 tokens af din system prompt og retten til at blive kaldt. Kapitel 30 er, hvad der sker, når den fremmede er fjendtlig.
Dateret afsnit: 2026-07-28-revisionen, og hvad den ødelægger
Link til afsnittet: Dateret afsnit: 2026-07-28-revisionen, og hvad den ødelæggerAlt i dette afsnit er sandt for protocol revision 2026-07-28, den nuværende, læst 7. september 2026. Revisioner dateres YYYY-MM-DD, og datoen er sidste gang, der blev lavet en bagud-inkompatibel ændring.10 Det normative dokument er en TypeScript-fil, schema/2026-07-28/schema.ts; JSON Schemaet ved siden af genereres fra den, og derfor læses specifikationen her i TypeScript, og derfor er det at undervise i MCP fra noget andet at undervise i en oversættelse.
| Hvad ændrede sig | Var | Er nu | Ødelægger |
|---|---|---|---|
| Handshaket | initialize + notifications/initialized, én gang per forbindelse | fjernet; hver request bærer _meta version og capabilities | hver client skrevet før denne revision |
| Sessions | Mcp-Session-Id header, connection-scoped state | fjernet; state rejser i explicit, server-minted handles | list endpoints der varierede per forbindelse |
| Discovery | udledt fra initialize-resultatet | server/discover, som servere skal implementere | intet, men det er nu obligatorisk at implementere |
| Server-to-client calls | server sendte roots/list, sampling/createMessage, elicitation/create | InputRequiredResult og et client retry | hver server der pushed en request til en client |
| Result shape | ethvert objekt | påkrævet resultType: "complete" eller "input_required" | intet: et manglende felt skal læses som "complete" |
| Subscriptions | HTTP GET stream, resources/subscribe | én subscriptions/listen stream med opt-in types | GET-endpointet er væk |
| Stream resumption | Last-Event-ID replay på Streamable HTTP | fjernet; en brudt stream mister requesten, udsted igen med et nyt id | clients der var afhængige af redelivery |
| Roots | en client feature servere kunne bede om | deprecated (SEP-2577); send paths som tool arguments eller resource URIs | intet endnu — tolv måneders vindue |
| Sampling og logging | client features | deprecated (SEP-2577) | intet endnu — tolv måneders vindue |
| HTTP+SSE transport | deprecated siden 2025-03-26 | Deprecated under lifecycle policy (SEP-2596) | migrér til Streamable HTTP |
| Client registration | OAuth 2.0 Dynamic Client Registration, RFC 7591 | deprecated til fordel for Client ID Metadata Documents | beholdt for authorization servers uden dem |
| Error codes | -32002 for resource not found | -32602; -32020–-32099 reserveret til specifikationen | nye codes -32020, -32021, -32022 |
Governance-ændringen under den tabel betyder mere end nogen enkelt række. Denne revision indførte en feature lifecycle and deprecation policy: features er Active, Deprecated eller Removed, en deprecated feature dokumenterer sin migration path og bliver i specifikationen i mindst tolv måneder, før den kan fjernes, og der er et registry, der lister alt, der aktuelt er i Deprecated state.8 Før den policy betød "deprecated" i en AI-protokol det, det seneste blogindlæg sagde. Nu betyder det en dato.
Vis detaljer
Extensions, som er den del ingen rigtig har skrevet om endnu.
Ud over kernen definerer MCP valgfri extensions — "always opt-in and require explicit support from both client and server", erklæret gennem et extensions-felt i clientens og serverens capabilities.1 Tre er værd at kende ved navn:
- Tasks (
io.modelcontextprotocol/tasks), flyttet ud af core protocol til en officiel extension i denne revision: asynkron eksekvering af langvarige operationer, med polling gennemtasks/get, mid-flight input gennemtasks/updateog durable handles. Det er svaret på et værktøj, der tager tyve minutter, hvilket Kapitel 23 håndterede med en progress event og et signal, der når værktøjet. - Skills over MCP, en working group der gør agent skills — Kapitel 28's emne — discoverable og consumable gennem protokollen.
- MCP Apps, interaktiv UI renderet inline i samtalen: diagrammer, formularer, video players.
Og bemærk hvad "negotiated" nu betyder: der er ingen initialization at negotiate ved, så en extension erklæres per request ligesom alt andet.
Hvor MCP ligger i forhold til alt det, det forveksles med
Link til afsnittet: Hvor MCP ligger i forhold til alt det, det forveksles medDette er ordforrådet for hele blokken samlet ét sted.
| Hvad det er | Hvem taler med hvem | Hvornår det er svaret | |
|---|---|---|---|
| En plain API | Et interface for et program | din kode ↔ en service | Du skriver calleren. Du styrer schema, auth og error handling, og der er ikke et discovery problem at løse. |
| MCP | En protokol til at eksponere værktøjer, data og templates for en AI-applikation | host ↔ server, én client hver | En anden skrev capabilityen, og mange hosts bør kunne bruge den uden en bespoke integration. |
| RAG | En teknik til at finde tekst og lægge den i prompt | din kode ↔ dit index | Modellen skal vide noget. Kapitel 19. MCP er en måde at levere en retriever på; det er ikke en retriever. |
| Agent skills | En mappe med en SKILL.md som modellen læser | model ↔ et dokument | Viden er procedural — hvordan vi gør dette — og den er prose, ikke en funktion. Kapitel 28. |
| A2A | En protokol for agents til at samarbejde som peers | agent ↔ agent | Den anden side ræsonnerer, planlægger og holder state gennem en lang task, i stedet for at besvare et call. |
| ACP | Var en separat agent-communication protocol | — | Det er ikke længere en live comparison. Se nedenfor. |
To af dem fortjener en sætning hver, fordi det er der, forvirringen faktisk bor.
MCP mod A2A er ikke en rivalisering, og begge specifikationer siger det. A2A-dokumentationen trækker grænsen efter, hvad der er i den anden ende: MCP "defines how an AI agent interacts with and utilizes individual tools and resources, such as a database or an API", hvor et værktøj udfører "specific, often stateless, functions"; A2A adresserer agents, "more autonomous systems" der "reason, plan, use multiple tools, maintain state over longer interactions, and engage in complex, often multi-turn dialogues". Dens egen opsummering er sætningen at huske: "A2A is about agents partnering on tasks, while MCP is more about agents using capabilities."11 De to kan nestes — en applikation bruger A2A til at nå andre agents, og hver agent bruger MCP til at nå sine egne værktøjer. Kapitel 25 trak den grænse inde i én proces, mellem at spørge en sub-agent og overdrage den samtalen; A2A trækker den mellem organisationer.
MCP mod ACP er en sammenligning med en forældet præmis, og netop derfor er den værd at besvare. Agent Communication Protocol var en separat åben standard for agent-to-agent messaging. Dens egen dokumentation åbner nu med beskeden: "ACP is now part of A2A under the Linux Foundation!"12 Det ærlige svar på "MCP or ACP?" i september 2026 er, at spørgsmålet har én mulighed færre, end de sider, der ranker for det, antyder.
Og den sammenligning, folk oftest beder om, mcp vs api, har det mindst interessante svar: MCP er en API. Det, den tilføjer, er ikke power, men conventions — et fast sæt method names, et discovery call, et kontrolhierarki over primitiverne og en isolation model. Du giver friheden til at designe dit eget interface væk, og du får hver host, der taler protokollen, hvilket er den handel, enhver protokol altid har tilbudt.
Hvor det går hen nu
Link til afsnittet: Hvor det går hen nuDu kan nu læse specifikationen uden en oversætter, skelne en resource fra et værktøj fra en prompt efter hvem der styrer den, skrive en request i hånden når et client library lyver for dig, og datere enhver MCP-artikel, du læser, efter hvilke deprecated features den stadig underviser som aktuelle.
Det, du ikke har gjort, er at shippe én. Kapitel 27 skriver den samme server to gange — TypeScript og Python side om side, fordi MCP er det ene reelt tosprogede territorium i dette kursus, og tallene siger det i begge retninger. Det dækker de to live transports ordentligt, inspectoren, packaging og den halvdel af protokollen, dette kapitel bevidst lod ligge: authorization. For i det øjeblik din server er remote i stedet for en subprocess på din egen laptop, vil en fremmeds client præsentere et token, og specifikationens regel om, hvad du må gøre med det, er usædvanligt streng.
Det rejser spørgsmålet, næste kapitel skal besvare, og det er ikke venligt: hvis et token ankommer til din server, og det blev udstedt til en andens audience, hvad stopper dig så præcist fra at videresende det?
Kilder og metode
Link til afsnittet: Kilder og metodeHvert citat, method name, error code og regel i dette kapitel blev læst fra Model Context Protocol-specifikationen, revision 2026-07-28, den 7. september 2026. Hver trace blev produceret lokalt på Node 22: toy-kalender-serveren er 101 linjer uden dependencies, og reference-serveren er den udgivne npm-pakke navngivet nedenfor. Ingen betalt API blev kaldt for at skrive dette kapitel — intet her kræver en model, hvilket i sig selv er pointen.
Målingerne: @modelcontextprotocol/server-everything@2026.8.31, udgivet 31. august 2026, bygget på @modelcontextprotocol/sdk@1.30.0, udgivet 27. juli 2026 — én dag før den revision, dette kapitel beskriver. Den svarer server/discover med -32601, forhandler 2025-11-25 når den bliver spurgt om 2026-07-28, og serverer tools/list uden noget handshake overhovedet. Dens katalog er 13 tools i 7.663 bytes; token counts er o200k_base via tiktoken, over name, description og inputSchema i hver definition, hvilket er det, en provider render ind i din prompt, og ikke hvad JSON-RPC-framen vejer.
Anthropic, Code execution with MCP: building more efficient agents, 4. november 2025, er kilden til 150.000-til-2.000-tallet, citeret og brugt i Kapitel 24 og kun refereret her.
Referencer
Link til afsnittet: Referencer-
Specification,
modelcontextprotocol.io/specification/latest(redirecting to/2026-07-28), læst 7. september 2026. Kilde til sammenligningen med Language Server Protocol; udsagnet om, at specifikationen er "based on the TypeScript schema inschema.ts"; base-protocol-opsummeringen ("Stateless, self-contained requests", "Per-request capability negotiation"); extension-listen (Tasks, Skills over MCP, MCP Apps) og udsagnet om, at extensions "are always opt-in and require explicit support from both client and server"; samt Security and Trust & Safety-principperne, inklusive "Hosts must obtain explicit user consent before invoking any tool" og behandlingen af tool annotations som untrusted. ↩ ↩2 ↩3 -
Base Protocol,
modelcontextprotocol.io/specification/2026-07-28/basic. Kilde til JSON-RPC-begrænsningerne (non-null id, ingen id reuse, påkrævetresultType); Statelessness-afsnittet og dets note om, at en åben stdio-proces ikke er en session; tabellen over reserverede_meta-keys og required/optional-status for hvert per-request-felt;-32602-reglen for et manglende required field;MissingRequiredClientCapability(-32021)-reglen; og error-code allocation policy. ↩ ↩2 ↩3 ↩4 ↩5 -
stdio transport,
modelcontextprotocol.io/specification/2026-07-28/basic/transports/stdio. Kilde til newline-delimited framing rules,stdoutpurity requirement,stderrallowance og den tre-udfalds backward-compatibility probe — inklusive advarslen om, at nogle legacy servers processer era-ambiguous methods uden handshake, hvilket målingen i dette kapitel reproducerer. ↩ ↩2 ↩3 -
Transports overview,
modelcontextprotocol.io/specification/2026-07-28/basic/transports. Kilde til "a transport is a binding"-framing og udsagnet om, at servers ikke initierer JSON-RPC requests, og clients ikke sender JSON-RPC responses. ↩ ↩2 -
Discovery,
modelcontextprotocol.io/specification/2026-07-28/server/discover. Kilde til den obligatoriske status forserver/discover, formen påDiscoverResultoginstructions-feltet beskrevet som "optional natural-language guidance for LLMs on how to use this server effectively". ↩ -
Architecture,
modelcontextprotocol.io/specification/2026-07-28/architecture. Kilde til host/client/server-definitionerne, 1:1-reglen fra client til server, de fire designprincipper, hvor isolation-princippet citeres her uden dets femte bullet, "Host process enforces security boundaries", og capability-negotiation-afsnittet. ↩ ↩2 -
Elicitation,
.../client/elicitation, og Sampling,.../client/sampling. Kilde til de to elicitation modes og deres restricted schema; forbuddet mod at anmode om credentials gennem form mode; sampling-definitionen, dens human-in-the-loop-krav og deprecation warning knyttet til den. ↩ -
Key Changes,
modelcontextprotocol.io/specification/2026-07-28/changelog, og Feature lifecycle and deprecation policy,.../community/feature-lifecycle. Kilde til hver række i ændringstabellen: fjernelsen af sessions ogMcp-Session-Id-headeren (SEP-2567); statelessness og fjernelsen afinitialize(SEP-2575);server/discover(SEP-2575);subscriptions/listen(SEP-2575); Multi Round-Trip Requests ogresultType(SEP-2322); fjernelsen af stream resumability (SEP-2575); deprecation af Roots, Sampling og Logging (SEP-2577); omklassificeringen af HTTP+SSE (SEP-2596); deprecation af Dynamic Client Registration til fordel for Client ID Metadata Documents; error-code-renummereringen; og det tolv måneders deprecation window. ↩ ↩2 -
Tools,
modelcontextprotocol.io/specification/2026-07-28/server/tools, og Server Features,.../server. Kilde til kontrolhierarkitabellen gengivet ovenfor;tools/list- ogtools/call-formerne;isError-skellet mellem protocol errors og tool execution errors; tool-name-reglerne og namespace-noten, der anbefaler "prefixing tool names with a server identifier"; og den non-normative "Stateful Tools"-vejledning om explicit handles. ↩ -
Versioning,
modelcontextprotocol.io/specification/versioning. Kilde tilYYYY-MM-DD-schemet, Draft/Current/Final-revisionstilstandene, bekræftelsen af at 2026-07-28 er current, og per-request negotiation rules. SDK tier-tabellen påmodelcontextprotocol.io/docs/sdklister TypeScript, Python, C#, Go og Rust på Tier 1, Java og Ruby på Tier 2, og Swift, PHP og Kotlin på Tier 3. ↩ -
A2A Protocol, version 1.0.0,
a2a-protocol.org— specifikationen og siden A2A and MCP: Relationship and Distinction, læst 7. september 2026. Kilde til tools-against-agents-skellet, udsagnet om at de to protokoller "address distinct but highly complementary needs", og partnering/using-formuleringen. ↩ -
Agent Communication Protocol,
agentcommunicationprotocol.dev, læst 7. september 2026: "ACP is now part of A2A under the Linux Foundation!", et banner tilføjet over en specifikation, der stadig serveres hel — architecture, agent manifest, agent discovery, message structure, stateful agents, run lifecycle og REST endpoint-listen svarer alle stadig 200. Specifikationen forsvandt ikke; projektet gjorde. ↩