MCP server szállítása: TypeScript és Python, mérve
Ugyanaz a server kétszer: három tool, egy resource, egy prompt. 94 package vs. 28, cold start: 145 ms vs. 709.
Ezen az oldalon
Íme a teljes nyelvi vita, lemérve, mielőtt akár egy szót is mondanánk róla.
node ./incidents.js 144.5 ms
python incidents.py 709.4 ms
npx incidents-mcp 712.6 msAz első két sor az az összehasonlítás, amelyet mindenki akar. A harmadik sor ugyanaz a TypeScript server az első sorból, úgy indítva, ahogyan ténylegesen terjesztenék — és három milliszekundumra érkezik a Pythontól.
A 26. fejezet a Model Context Protocolt a saját specifikációjához mérte nyers JSON-RPC-vel, mert a nyers JSON-RPC-nek nincs nyelve. Ennek a fejezetnek kettő van, és az érv súlya itt dől el: ugyanaz a server, kétszer megírva. Három tool, egy resource, egy prompt, mindkét SDK, egyik oldalon sincs rövidítés. Aztán a transports, az inspector, a 401, és azok a számok, amelyeket még senki sem publikált.
A server, és miért ez az öt dolog van benne
Link a szakaszhoz: A server, és miért ez az öt dolog van benneEgy incidensnapló. Három tool, mert a 18. fejezet olvasások és írások közti felosztásának láthatónak kell lennie: search_incidents olvas, open_incident ír és visszaad egy handle-t, resolve_incident átveszi ezt a handle-t és lezárja. Egy resource, incidents://open, mert az aktuális lista olvasását az alkalmazás csatolja. Egy prompt, postmortem, mert a „írd ezt meg” egy ember slash commandja. Ez a 26. fejezet vezérlési hierarchiája — model, alkalmazás, ember — öt regisztrációvá alakítva.
A handle fontosabb, mint amilyennek látszik. A 26. fejezet úgy törte el a játék naptárat, hogy az állapotát egy modul-szintű tömbben tartotta: a protocolnak nincs sessionje, ezért egy létrehozó tool átlátszatlan azonosítót ad vissza, és minden későbbi hívás ezt közönséges argumentumként kapja meg. Egyik fájlban sincs olyan feltételezés, hogy a hívó ugyanaz a process, amely megnyitotta.
Íme ugyanaz a tool mindkét nyelven, egymás mellett regisztrálva:
server.registerTool(
"resolve_incident",
{
description:
"Close an incident by handle and record its cause.",
inputSchema: {
id: z.string().describe(
"The handle returned by open_incident, e.g. INC-3."),
cause: z.string().describe(
"One sentence. What actually broke."),
},
annotations: {
readOnlyHint: false,
destructiveHint: true,
idempotentHint: true,
},
},
async ({ id, cause }) => {
const at = OPEN.findIndex((i) => i.id === id);
if (at < 0) {
return { isError: true, content: [{ type: "text",
text: `No open incident ${id}. ` +
`Call search_incidents first.` }] };
}
const [done] = OPEN.splice(at, 1);
return { content: [{ type: "text",
text: JSON.stringify({ ...done, cause }) }] };
},
);@server.tool(
description=
"Close an incident by handle and record its cause.",
annotations=ToolAnnotations(
readOnlyHint=False,
destructiveHint=True,
idempotentHint=True,
),
)
def resolve_incident(
id: Annotated[str, Field(description=
"The handle returned by open_incident, e.g. INC-3.")],
cause: Annotated[str, Field(description=
"One sentence. What actually broke.")],
) -> Incident:
for at, i in enumerate(OPEN):
if i["id"] == id:
done = OPEN.pop(at)
return {**done, "cause": cause}
raise ValueError(
f"No open incident {id}. Call search_incidents first.")Először azt olvasd el, ami ugyanaz, mert ez a megállapítás. Mindkettő deklarál egy nevet, egy leírást, két leírt string argumentumot és három annotationt; mindkettő egyetlen function; egyik sem említi a JSON-RPC-t, a framinget, a stdout-t vagy a protocol verziót. A két SDK ugyanarra a formára konvergált, és a „Tier 1”-nek éppen ezt kell jelentenie.1
Két különbség valódi, és mindkettő később visszatér. A TypeScript egy schema libraryvel írja le az argumentumokat — itt Zoddal —, és a schema egy érték, amelyet te írsz meg. A Python a function saját type hintjeivel írja le őket, és importidőben olvassa ki őket, ezért tud olyan dolgokat a functionről, amelyeket a TypeScript fájl sosem mondott el neki. És az error path: a TypeScript tool resultot ad vissza isError-val, a Python exceptiont dob. Ezt tartsd észben.
A másik négy regisztráció strukturálisan semmiben sem különbözik. A resource server.registerResource("open-incidents", "incidents://open", …) a @server.resource("incidents://open", …)-vel szemben; a prompt registerPrompt a @server.prompt-vel szemben. Mindkét fájl utolsó sora a transport: await server.connect(new StdioServerTransport()) a server.run()-vel szemben.
Teljes fájlok: 81 nem üres sor és 3 060 bájt TypeScriptben, szemben 63 sorral és 2 555 bájttal Pythonban. Ezt annyi fenntartással kezeld, amennyit megérdemel — a sorszám éppúgy méri a formattert, mint a nyelvet, ezért egyik szám sincs benne az alábbi fő táblázatban.
Egy client, mindkét server
Link a szakaszhoz: Egy client, mindkét serverAnnak bizonyítéka, hogy a nyelv láthatatlan: egy client kétszer futtatva, tizenegy sorban:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const client = new Client({ name: "incident-cli", version: "1.0.0" });
await client.connect(new StdioClientTransport({
command: process.argv[2], args: process.argv.slice(3) }));
const { tools } = await client.listTools();
console.log("tools:", tools.map((t) => t.name).join(", "));
const opened = await client.callTool({ name: "open_incident",
arguments: { title: "Queue backed up", severity: "sev2" } });
console.log("open_incident ->", JSON.stringify(opened.content));Irányítsd sorban mindkét serverre. Valódi output, rövidítve:
$ node client.ts node incidents.ts
tools: search_incidents, open_incident, resolve_incident
open_incident -> [{"type":"text","text":"{\"id\":\"INC-3\"}"}]
$ node client.ts ./py/.venv/bin/python ./py/incidents.py
tools: search_incidents, open_incident, resolve_incident
open_incident -> [{"type":"text","text":"{\n \"id\": \"INC-3\"\n}"}]Ugyanazok a tools, ugyanaz a sorrend, ugyanaz a handle. Egy TypeScript client nem tudja megmondani, miben írták a servert, és sosem kérdez rá. Ez egy protocol teljes ígérete, és működik.
Most nézd meg a whitespace-t a második eredményben, mert nem kozmetika: a Python SDK pydantic_core.to_json(result, fallback=str, indent=2)-val serializálja a payloadokat. A resource readnél, amikor két incidens van a listában, a TypeScript body 136 karakter és 37 o200k_base token; a Python body 185 és 62. Hatvannyolc százalékkal több token azonos sorokért, amelyet az fizet, aki a resource-t beolvassa egy promptba, minden alkalommal.
A catalogue ugyanazt meséli nagyobb okkal. Mindkét server, ugyanaz a három tool, tools/list kulcsonként mérve:
| key | TypeScript | Python |
|---|---|---|
name | 21 | 21 |
description | 46 | 46 |
annotations | 46 | 46 |
inputSchema | 211 | 192 |
outputSchema | — | 187 |
execution | 27 | — |
| összesen | 342 | 480 |
A Python input schemái olcsóbbak — a TypeScript Zod bridge mindegyikre rábélyegez egy $schema-t és egy additionalProperties-t. A teljes 138-token különbség egy output schema, amelyet senki sem írt meg. A resolve_incident -> Incident-ként van annotálva, ezért az SDK JSON Schemát vezetett le a return type-hoz, és elküldte. Ez tényleg hasznos — ez teszi lehetővé, hogy egy client validálja a structuredContent-t —, és 187 token a context windowdból, amely egy type hint miatt érkezik. A 24. fejezet szabálya arról, hogy a definíciók kiszorítják a lényeges anyagot, azokra a schemákra is érvényes, amelyekről nem is tudtad, hogy vannak.
Törd el szándékosan: a kiszivárgott error message
Link a szakaszhoz: Törd el szándékosan: a kiszivárgott error messageA fenti két error path nem stíluskérdés. Adj mindkét servernek egy toolt, amely úgy hibázik, ahogy egy valódi integráció hibázik, és olvasd el, mi jut el a modelhez.
TypeScript {"content":[{"type":"text","text":
"connect ECONNREFUSED 10.0.3.7:5432 (db-prod-eu, user=reporting)"}],
"isError":true}
Python {"content":[{"text":"Error executing tool boom","type":"text"}],
"isError":true}A TypeScript SDK egy belső címet, egy portot, egy database nevet és egy service accountot tett a model contextjébe. A Python SDK ezekből semmit sem tett oda; a traceback a stderr-ra ment, és a serveren maradt.
Egyik sem bug. Mindkettő döntés, és a Python döntése le van írva a saját docstringjében: a ToolError „egy olyan hiba, amelyre számítottál”, és az üzenete visszakerül „a content-ben, hogy a model olvashassa”; minden más „crashként kezelődik: a model csak Error executing tool <name>-t lát, a server pedig a tracebacket a ERROR-nál logolja”. A crash-eset classa kimondja a maradékot is — „az eredetiből semmi sem jut el a clienthez”.
Mindkét viselkedés az idő felében rossz. A 18. fejezet azt állította, hogy a validation errornek tool resultként kell visszajönnie, amelyet a model elolvashat és javíthat, mert a legtöbb integrációban ez a legnagyobb hatású sor; Python oldalon ehhez explicit ToolError dobása kell, és egy puszta ValueError eldobja a hasznos mondatot. A 30. fejezet érve a másik irányba fut: minden, amit egy tool visszaad, olyan contextbe kerül, amelyből egy későbbi prompt injection megpróbálhatja visszaolvastatni, és egy ellenőrizetlen exception string a rendszered legkevésbé auditált szövege.
A szabály, amely mindkettőt túléli: toolonként döntsd el, hogy egy hiba mit mondhat, és írd meg ezt a stringet te magad. Soha ne engedd, hogy egy exception default szövege döntsön, egyik nyelvben sem.
Törd el szándékosan: egy sor standard outputon
Link a szakaszhoz: Törd el szándékosan: egy sor standard outputonA hivatalos tutorial kertelés nélkül kimondja a szabályt: „STDIO-alapú servereknél: soha ne írj stdout-ra. A stdout-ra írás megrontja a JSON-RPC messageseket és eltöri a servert. A print() function alapból stdout-ra ír, ezért teljesen tartsd távol egy STDIO servertől.”1 A 26. fejezet a normatív verziót idézte — egy server „MUST NOT write anything to its stdout that is not a valid MCP message”.2
Adj egy sort mindkét serverhez, és olvasd el a nyers streamet:
TypeScript incidents server starting
{"result":{"protocolVersion":"2025-11-25", … },"jsonrpc":"2.0","id":1}
Python {"jsonrpc":"2.0","id":1,"result":{ … }}
incidents server startingA Python rosszabb, és az ok nem az MCP. Egy process, amelynek a stdout-je pipe, nem terminal, blokkpufferelt streamet kap, ezért a kóbor sor akkor flusholódik, amikor a buffer úgy dönt — itt kilépéskor, egy olyan response után, amely elé írták. A corruption nem ott jelenik meg, ahol a bug van. Adj hozzá flush=True-t, vagy egy flusholó libraryt, és elmozdul.
Aztán az a rész, amely megmagyarázza, miért kerül ez productionbe. Add oda a törött servert három clientnek:
naive parser, dirty server SyntaxError: Unexpected token 'i',
"incidents "... is not valid JSON
SDK client, dirty server tools: search_incidents, open_incident, resolve_incident
MCP Inspector, dirty server full catalogue, no warningA hétsoros parser azonnal meghal. A hivatalos client és az Inspector vállat von — átugorják a sort, és mennek tovább. Egy szabály, amely csak azokat a clienteket töri el, amelyeket senki sem használ, épen eljut productionbe; ezért érdemes itt szándékosan eltörni, nem egy customer logjában.
Az Inspector CLI módja az a fele, amelyről megfeledkeznek: a npx @modelcontextprotocol/inspector --cli <command> --method tools/list kinyomtat egy catalogue-ot és kilép, ami scriptelhetővé teszi úgy, ahogy a browser UI nem.3
A táblázat
Link a szakaszhoz: A táblázatMindkét SDK tisztán települt, a saját könyvtárába, semmi közös:
| TypeScript | Python | |
|---|---|---|
| package | @modelcontextprotocol/sdk 1.30.0 + zod 3.25.76 | mcp 2.1.1 |
| legújabb implementált protocol revision | 2025-11-25 | 2026-07-28 |
| telepített transitive packages | 94 | 28 |
| telepített méret | 13.9 MiB | 44.3 MiB |
| fájlok a disken | 3,386 | 2,018 |
| stdio kiszolgálásához betöltött third-party packages | 8 / 94 | 18 / 28 |
| csupasz interpreter start, medián | 19.4 ms | 11.1 ms |
spawn → tools/list válaszolt, 25 mérés mediánja | 144.5 ms | 709.4 ms |
tools/list catalogue, o200k_base token | 342 | 480 |
Minden sor más irányból lep meg, ezért érdemes az összehasonlítást lefuttatni, nem feltételezni.
A TypeScript több mint háromszor annyi package-et telepít, és kevesebb mint harmadannyi bájtot. 94 dependency: az npm ecosystem önmaga — fast-deep-equal, es-errors, dunder-proto. A Python 28 darabja kevesebb és óriási: cryptography, pydantic-core és uvicorn compiled artefacts. Ha az ösztönöd szerint a dependency számától kell tartani, ez a sor az ellenpélda.
A Python interpreter gyorsabban indul, mint a Node, és nem kicsivel — 11.1 ms 19.4 ms ellen egy üres programon. Tehát a cold-start sorban lévő 565 ms nem a nyelv. Az SDK az, és a betöltött package-ek sora megmondja, miért:
TypeScript 8 of 94 sdk, zod, zod-to-json-schema, ajv, ajv-formats,
fast-deep-equal, fast-uri, json-schema-traverse
Python 18 of 28 mcp, mcp_types, pydantic, pydantic_core, anyio,
starlette, uvicorn, sse_starlette, httpx2,
cryptography, _cffi_backend, opentelemetry, click, …Egy server, amelynek egyetlen I/O-ja egy pipe, importál egy ASGI web servert, egy HTTP clientet és egy TLS libraryt, mielőtt elolvasná az első sorát. A TypeScript SDK Express-t, Hono-t, jose-t és eventsource-t is szállít — ezek olvasatlanul ülnek a disken, mert a package boundary távol tartja őket egy server/stdio.js importtól. A Python package egyetlen import graph, ezért a import mcp az egészet jelenti: a python -X importtime 727 ms-ot tulajdonít a import mcp.server.mcpserver-nek — ez import profiler alatt mért szám, ezért jön ki magasabbra, mint az a 709 ms, amelyet a profilozatlan futás igényel spawntól válaszig —, és ebből 269-et csak a mcp.types subtree-nek — a wire típusok Pydantic modellek, protokollüzenetenként és revisionönként egy class, és ezek felépítése importkor elvégzett munka. Ez design trade, nem hanyagság — az eager importok miatt tud a Python SDK a következő sorban run(transport="streamable-http")-t adni második install nélkül.
Aztán a nyitó blokk utolsó sora visszavonja az érvet. Csomagold a TypeScript servert rendesen — egy bin entry, egy shebang, npm link, nincs letöltendő dolog —, és indítsd npx-n keresztül --no-install-val, vagyis úgy, ahogy egy publikált stdio server ténylegesen elindul:
node ./incidents.js 144.5 ms
npx incidents-mcp 712.6 ms (+568.1 ms of launcher)
python incidents.py 709.4 msA launcher indulásonként 568 ms-ba kerül — négy és félszer annyiba, mint a teljes TypeScript SDK import —, és minden launchkor fizeted, mert egy MCP host egy stdio servert ennek a commandnak a futtatásával indít. Ezért a „TypeScript ötször gyorsabban indul” becsületes formája: így van, amíg nem a szokásos módon terjeszted. Ugyanez a fenntartás feltehetően a uvx-re is érvényes; ezen a gépen nem volt uv telepítve, ezért az a sor nem létezik. Ami nincs megmérve, nem kerül a táblázatba.
Két transport, és csak kettő
Link a szakaszhoz: Két transport, és csak kettőA 26. fejezet lefedte a stdio framingjét. Két dolgot hagyott ide.
Az első: egy server npx-val vagy uvx-vel futtatva maga a stdio transport. Nincs külön „package mode”. Egy host konfigurációja commandot és argumentumokat nevez meg; a host elindítja, és a pipe-okon beszél vele. Ezért a „hogyan terjesszem ezt” és a „melyik transportot beszéli” helyben ugyanaz a kérdés, és ezért tartozik a launcher költsége egy szállításról szóló fejezetbe.
A második: a stdio egyáltalán nem tartalmaz authorization szakaszt, és a specifikáció ezt egy sorban kimondja — a stdiót használó implementációk „SHOULD NOT follow this specification, and instead retrieve credentials from the environment”.4 A security modellje az operációs rendszeré, és a korlátja is: egy local subprocess pontosan egy gépet és egy usert szolgál ki.
A másik élő transport a Streamable HTTP: egyetlen endpoint, amely POST-ot fogad, JSON-RPC message-enként egy HTTP request, és egy Accept header, amelynek mind a application/json-t, mind a text/event-stream-t listáznia kell, mert a server requestenként választja ki, melyikkel válaszol.5 A 14. fejezet kézzel parse-olta ezt az event streamet, ezért a wire formatban nincs semmi új — csak a köré kerülő réteg. Az aktuális revision három kötelezettségét könnyű kihagyni, és mindhárom tesztelhető:
A version headernek egyeznie kell a bodyval
Link a szakaszhoz: A version headernek egyeznie kell a bodyvalMinden POST viszi a MCP-Protocol-Version-t, és az értékének egyeznie kell a request saját _meta-jában lévő protocolVersion-tel. Az eltérés 400 header-mismatch errorral, nem vállvonás.5
Két további header is kell a compliance-hez
Link a szakaszhoz: Két további header is kell a compliance-hezA Mcp-Method minden requesten tükrözi a methodot; a Mcp-Name a tools/call, resources/read és prompts/get esetén a params.name-t vagy params.uri-t tükrözi. Azért léteznek, hogy egy proxy body parse-olás nélkül route-olhasson.5
A régi formák eltűntek, és elutasítással válaszolnak
Link a szakaszhoz: A régi formák eltűntek, és elutasítással válaszolnakA GET stream, a Mcp-Session-Id és a Last-Event-ID resumption mind el lett távolítva. Egy server, amely csak ezt a revisiont beszéli, GET-re vagy DELETE-re 405 Method Not Allowed-tel válaszoljon, session header esetén ne verjen új sessiont, és hagyja figyelmen kívül a Last-Event-ID-t.5
Most jön a mérés, amely újrakeretezi az egész fejezetet. Küldj current-revision requestet mindkét servernek HTTP-n.
Python 200 {"result":{"resultType":"complete","cacheScope":"private","ttlMs":0,
"tools":[…],"_meta":{"io.modelcontextprotocol/serverInfo":{…}}}}
TypeScript {"error":{"code":-32000,"message":"Bad Request: Unsupported protocol
version: 2026-07-28 (supported versions: 2025-11-25, 2025-06-18,
2025-03-26, 2024-11-05, 2024-10-07)"}}A konstansok egyeznek a viselkedéssel: a Python SDK LATEST_PROTOCOL_VERSION értéke 2026-07-28, a TypeScript SDK-é 2025-11-25. Küldd el a fenti lépés header-mismatch requestjét, és a Python server 400-vel válaszol, -32020 errorral és ezzel az üzenettel: „mcp-protocol-version header does not match the request envelope's protocol version”; a TypeScript SDK-ban nincs ilyen code, mert nem implementálja azt a revisiont, amely definiálja.
Az oldal, amely mindkettőt Tier 1-ként listázza, azt is mondja: „Each SDK provides the same functionality”.1 Az alábbi dátumon, a current revisionre, ez a mondat inkább célkitűzés. Ellenőrizd a LATEST_PROTOCOL_VERSION-t abban az SDK-ban, amelyet telepíteni készülsz; ez egyetlen sor, és az egyetlen állítás ebben a fejezetben, amely egy év múlva is számítani fog.
A 401, és a mondat, amelyet idézni kell
Link a szakaszhoz: A 401, és a mondat, amelyet idézni kellVigyél le egy servert a laptopodról, és megjelenik egy idegen client egy tokennel. Ez az a fele, amelyet a 26. fejezet békén hagyott, és az a fele, amelyet egy multi-user product nem ugorhat át.
A specifikáció OAuth 2.1 szerepbe teszi az MCP servert, és megnevezi: egy védett MCP server resource server, a client OAuth client, az authorization server pedig valaki más gondja.4 Ebből a szerepből négy kötelező klauzula következik, teljes egészében idézve, mert a parafrázisból születik a hiba:
Az OAuth 2.1 resource server szerepében eljáró MCP servereknek az OAuth 2.1 5.2. szakaszában leírtak szerint validálniuk KELL az access tokeneket. Az MCP servereknek az RFC 8707 2. szakasza szerint validálniuk KELL, hogy az access tokeneket kifejezetten számukra, intended audience-ként adták ki. […] Az MCP clientek NEM küldhetnek az MCP servernek olyan tokeneket, amelyeket nem az MCP server authorization servere adott ki. Az MCP serverek CSAK olyan tokeneket fogadhatnak el, amelyek a saját resource-aikkal való használatra érvényesek. Az MCP serverek NEM fogadhatnak el és NEM továbbíthatnak semmilyen más tokent.4
A „nem fogadhat el és nem továbbíthat” az anti-passthrough szabály, és ezért létezik az egész audience apparátus. Az a server, amely egy kapott bearer tokent újrajátszik egy third-party API-nál, confused deputy: a saját bizalmát kölcsönzi annak, aki hívta. A szabály nemcsak a tárolást tiltja, hanem az újrafelhasználást is.
Ennek kikényszeríthetővé tételéhez négy RFC kell, mindegyiknek egy feladata van.6 RFC 9728: így találja meg a client egyáltalán az authorization servert; az MCP server protected-resource-metadata dokumentumot szolgál ki, és egy 401 rámutat. RFC 8707: ez a resource paraméter — a clientnek a server canonical URI-ját kell elküldenie mind az authorization requestben, mind a token requestben, „függetlenül attól, hogy az authorization serverek támogatják-e”, hogy a kiadott token megnevezze az audience-ét. RFC 9207 a másik oldalról zárja a kört: a client rögzíti az issuert redirect előtt, majd pontos stringként összehasonlítja a visszakapott iss-t, normalizálás nélkül — nincs case folding, nincs default port elhagyás, nincs trailing slash. És RFC 7591, a Dynamic Client Registration, ma már deprecated a Client ID Metadata Documents javára, „megtartva backward compatibility miatt azokkal az authorization serverekkel, amelyek nem támogatják” őket.4
Kösd be ezt mindkét serveren egy token verifierrel, amely nem csinál mást, csak az audience-et ellenőrzi. A TypeScript létra:
no token 401 WWW-Authenticate: Bearer error="invalid_token",
error_description="Missing Authorization header",
scope="incidents:read",
resource_metadata="…/.well-known/oauth-protected-resource/mcp"
aud=other server 401 error_description="token audience is not this server"
no exp claim 401 error_description="Token has no expiration time"
right aud, no scope 403 error="insufficient_scope", scope="incidents:read"
right aud + scope 200 {"result":{"tools":[…]}}{"resource":"http://127.0.0.1:8931/mcp",
"authorization_servers":["https://auth.example.com/"],
"scopes_supported":["incidents:read","incidents:write"],
"resource_name":"Incidents"}Mindkét SDK kiszolgálja ezt a dokumentumot, és mindkettő egy 401-t mutat rá, ami a teljes discovery story: egy client, amely sosem látta a serveredet, egy elutasításból megtanulja, hol kell authentikálnia. A 403 más állat — a token rendben van, a scope nem —, és a challenge megnevezi, mi hiányzik, hogy a client feljebb léphessen, ne kezdje elölről.
Két fok különbözik, és egyik különbség sincs a specifikációban. A TypeScript SDK elutasítja a tokent, ha nincs expiry claim; a Python 200-t ad vissza, mert a expires_at optional a AccessToken-jén, és a None azt jelenti: „nincs vélemény”. A Python 403 pedig error_description="Required scope: incidents:read"-t hordoz a specifikáció szerint a servereknek mellékelendő scope paraméter nélkül. Egy verifier nem az a hely, ahol library defaultot fogadsz el: az audience check a te dolgod mindkét nyelven, és az expiry is.
Egy őszinte apróság ugyanebből a futásból. Egy GET az endpointon 404-tel válaszolt az Express wiringon és 400 Bad Request: Missing session ID-cal a Python oldalon, miközben a specifikáció 405 Method Not Allowed-t kér, és miközben a „session ID” olyan szókincs, amelyet ez a revision eltávolított. Egyik sem veszélyes; mindkettő egy mid-migration ecosystem alakja.
Hol élnek valójában a serverek
Link a szakaszhoz: Hol élnek valójában a serverekA shipping utolsó darabja az, hogy hol publikálod, és erre van egy számmal rendelkező válasz. Ma crawlolva, a hivatalos registry minden servere a legújabb verzióján:7
| serverek | |
|---|---|
| összesen (legújabb verzió, nem törölt) | 28,170 |
| aktív / deprecated | 27,853 / 317 |
| legalább egy telepíthető package-et szállít | 13,065 |
| csak remote — egy URL, nincs mit telepíteni | 14,696 |
| npm | 8,275 |
| PyPI | 3,603 |
| OCI images | 867 |
mcpb bundles | 706 |
| NuGet / Cargo | 107 / 43 |
Két olvasat, ellentétes irányba mutatva. Publikált serverek alapján az npm 2,3:1 arányban vezet — ezt a számot idézik, amikor azt mondják, hogy az ecosystem TypeScript. Downloadok alapján a Python vezet: az elmúlt harminc napban a mcp 286,7 milliót ért el, szemben a @modelcontextprotocol/sdk 194,7 milliójával, még a fastmcp 72,1 milliójának hozzáadása előtt.7 Mindkettő Tier 1, a normatív schema egy schema.ts, és a hivatalos „Build an MCP server” tutorial a Python tabon nyílik.1 Bármelyik fele volt a fejedben, a másik fele is igaz.
És az a sor, amely mindkettőnél fontosabb: a registry több mint fele — 14 696 a 28 170-ből — nem igényel telepítést. Ezek web service-ek. A transport összesítések a másik oldalról is egyetértenek: 14 290 package entryből 13 787 deklarál stdiót; 16 640 remote entryből 15 570 deklarál Streamable HTTP-t, és 1 070 még mindig a deprecated HTTP+SSE-t deklarálja. Tehát az „egy MCP server egy subprocess a laptopodon” egy zsugorodó kisebbséget ír le, és a 14 696 mindegyikének a fenti szakasz kell, nem egy environment variable.
Részletek megjelenítése
Szándékosan kétnyelvű, és a precedens hozzá.
Ez a kurzus egyetlen kétnyelvű fejezete, mert az őszinte válasz kettéválik: a registry npm-first, a downloadok Python-first, egyszerre, ma. Ha csak az egyikben írnánk meg, a kérdés felét odaadnánk, és közben félreírnánk az ecosystemet. Van nyílt precedens is — a Hugging Face MCP Course az előfeltételei között ezt sorolja: „Experience with at least one programming language (Python or TypeScript examples will be shown)”, és mindkettőt tanítja.8 Egy protocol, amelynek az egész értéke az implementációk száma, rossz hely az egynyelvűségre.
Dátumozott szakasz: minden fent, aminek van szavatossága
Link a szakaszhoz: Dátumozott szakasz: minden fent, aminek van szavatosságaOlvasva és mérve 2026. szeptember 7-én, a 2026-07-28 protocol revision ellen.
| érték | |
|---|---|
@modelcontextprotocol/sdk | 1.30.0, publikálva 2026. július 27-én; 4,322,438 bájt unpacked, 693 fájl, 17 direct dependency |
| legújabb revision, amelyet implementál | 2025-11-25 |
mcp (PyPI) | 2.1.1, publikálva 2026. augusztus 25-én; 357,912 bájtos wheel, plusz mcp-types 2.1.1 69,656 bájttal |
| legújabb revision, amelyet implementál | 2026-07-28 |
| SDK tiers | TypeScript, Python, C#, Go, Rust Tier 1; Java, Ruby Tier 2; Swift, PHP, Kotlin Tier 3 |
| registry serverek | 28,170 |
| downloadok, utolsó 30 nap | mcp 286,653,871 · fastmcp 72,097,269 · @modelcontextprotocol/sdk 194,679,333 |
Egy migration note, amely nem szám. A mcp 2.x-ben a FastMCP át lett nevezve MCPServer-ra, és szinte minden online tutorial még mindig a régi importtal nyílik. Az SDK szállít egy modult, amelynek egyetlen célja ennek elmagyarázása, ez pedig ennek a fejezetnek a legfigyelmesebb deprecationje:
ModuleNotFoundError: No module named 'mcp.server.fastmcp'. This is mcp 2.x,
where FastMCP was renamed to MCPServer (from mcp.server.mcpserver import
MCPServer) and other APIs changed; see the migration guide … or pin 'mcp<2'
to keep running v1 code.Akkor melyik legyen
Link a szakaszhoz: Akkor melyik legyenA táblázattal előtted az ajánlás unalmas, ami jó jel.
Ha a server egy olyan web applicationön belül él, amelyet már futtatsz, írd TypeScriptben. Ugyanaz a process, ugyanaz a deploy, ugyanaz a request handler; a Streamable HTTP egy endpoint, amelyet a többi mellé adsz; a 13.9 MiB és a 145 ms pedig ingyen van, mert a runtime már futott. Ez a 14 696 remote server többsége.
Ha a server data toolingot csomagol, írd Pythonban. Amit kiteszel, az pandas, egy warehouse client, egy notebooknyi transform, és egy másik nyelvű server valójában schema-t viselő subprocess hívás lenne. Hétszáz milliszekundum import egy egyszer induló service-ben nem költség; egy subprocessben, amelyet egy host egész nap újraindít, az.
És egyelőre a revision sor mindkettőt felülírja. Ha 2026-07-28 kell — multi-round-trip requestek, resultType, cache hints, server/discover —, a két SDK egyikében ma megvan, a másikban nincs.
Merre tovább
Link a szakaszhoz: Merre továbbMost már ugyanazt a servert bármelyik nyelven le tudod szállítani, a választást táblázattal tudod védeni preferencia helyett, mindkét élő transporton futtatni tudod, és olyan tokent tudsz adni neki, amelyet el fog utasítani.
Amit építettél, még mindig function: schema, endpoint, determinisztikus dolog, amelyet a model meghív. A tudás egy egész osztálya nem fér bele ebbe a formába — hogyan írunk mi postmortemet, milyen mezők kellenek az incidensjelentéseinkhez, milyen sorrendben csináljuk a dolgokat és miért. Ez eljárás, ez próza, és tool descriptionbe kényszeríteni az az út, amelyen a system prompts kétezer tokenesre nőnek, minden egyes turnnél fizetve értük, akár incidensekről szól a beszélgetés, akár nem.
A 28. fejezet a másik válasz: egy folder, benne egy SKILL.md, amelyet a model olvas ahelyett, hogy hívna, három szinten betöltve, hogy a referenciaanyag szinte semmibe se kerüljön addig a turnig, amikor szükség van rá. Nincs fő nyelve, és ez az első dolog, amit tanít.
Források és módszer
Link a szakaszhoz: Források és módszerMinden itt szereplő mérés 2026. szeptember 7-én készült, Node 22.22.3 és Python 3.14.4 alatt, @modelcontextprotocol/sdk 1.30.0 + zod 3.25.76 és mcp 2.1.1 ellen, mindegyik saját eldobható könyvtárba telepítve. Az időzítések 25 launch mediánjai, wall clock a spawn-tól a tools/list response-t hordozó sorig; a token számok o200k_base a tiktoken-en keresztül, minden definíció JSON-ján. Fizetős API-t nem hívtunk: ehhez semmihez nem kell model.
A két server 81 és 63 nem üres sor; három tooljuk egyikét fent mindkét nyelven reprodukáltuk, a másik négy regisztráció pedig csak a leírtak szerint különbözik. A Python SDK error-disclosure policyje a ToolError és UnexpectedToolError docstringjeiből van idézve a mcp/server/mcpserver/exceptions.py-ban; a pretty-printing default pydantic_core.to_json(result, fallback=str, indent=2) a mcp/server/mcpserver/resources/types.py-ban és utilities/func_metadata.py-ben. A protocol-version konstansok: LATEST_PROTOCOL_VERSION a mcp_types/version.py-ban és a TypeScript SDK types.js-jában, mindkettő a telepített package-ekből olvasva, nem changelogból.
Hivatkozások
Link a szakaszhoz: Hivatkozások-
SDKs,
modelcontextprotocol.io/docs/sdk, és Build an MCP server,modelcontextprotocol.io/docs/develop/build-server, mindkettő olvasva 2026. szeptember 7-én. A tier table, az „Each SDK provides the same functionality but follows the idioms and best practices of its language” mondat, a tutorial nyelvi tab sorrendje (Python, TypeScript, Java, Kotlin, C#, Ruby, Rust, Go), valamint aprint()-ről ésstdout-ről idézett logging rule forrása. ↩ ↩2 ↩3 ↩4 -
stdio transport,
.../basic/transports/stdio. A newline framing és astdoutpurity rule forrása. A 26. fejezet teljesen elolvassa ezt az oldalt; itt azért van idézve, mert a törött server ezt a sort sérti meg. ↩ -
MCP Inspector,
modelcontextprotocol.io/docs/2026-07-28/tools/inspector, olvasva 2026. szeptember 7-én. Egy package, három client egy binary mögött — web,--cliés--tui—, közös core-ral, közös transport készlettel és egy OAuth state-tel a disken. A CLI állította elő az itteni catalogue trace-eket. ↩ -
Authorization,
modelcontextprotocol.io/specification/2026-07-28/basic/authorization, olvasva 2026. szeptember 7-én. A resource-server szerep forrása; a teljesen idézett négy token-kezelési klauzula; az a követelmény, hogy a serverek implementálják az RFC 9728-at, a clientek pedig discoveryre használják; aresourceparaméter szabályai és a canonical-URI definíció; az issuer-validation table; a Dynamic Client Registration deprecationje; a401/403/400table és ainsufficient_scopechallenge; valamint a stdio kivétel: „Implementations using an STDIO transport SHOULD NOT follow this specification, and instead retrieve credentials from the environment.” ↩ ↩2 ↩3 ↩4 -
Streamable HTTP,
.../basic/transports/streamable-http, és Transports overview,.../basic/transports. Az egy-endpointos POST szabály, a kettősAcceptkövetelmény, aMCP-Protocol-Versionheader és a bodyval-egyeznie-kell szabály, a „REQUIRED for compliance”-ként leírtMcp-MethodésMcp-Nameheaderek, a GET stream, sessions ésLast-Event-IDeltávolítása, a405útmutatás, a kötelezőOriginvalidation, valamint a 2024-11-05 HTTP+SSE transport SEP-2596 alatti Deprecated besorolásának forrása. ↩ ↩2 ↩3 ↩4 -
Az a négy, amelyre a specifikáció támaszkodik, az általa profilozott drafttal: The OAuth 2.1 Authorization Framework,
draft-ietf-oauth-v2-1-13. Campbell, B., Bradley, J. és Tschofenig, H., Resource Indicators for OAuth 2.0, RFC 8707, 2020. február — aresourceparaméter és az általa kötött audience. Jones, M.B., Hunt, P. és Parecki, A., OAuth 2.0 Protected Resource Metadata, RFC 9728, 2025. április — az a dokumentum, amelyre egy401mutat. Meyer zu Selhausen, K. és Fett, D., OAuth 2.0 Authorization Server Issuer Identification, RFC 9207, 2022. március — aissparaméter és a pontos-string összehasonlítás. Richer, J. (ed.) et al., OAuth 2.0 Dynamic Client Registration Protocol, RFC 7591, 2015. július, erre a használatra deprecated. És Jones, M. és Hardt, D., The OAuth 2.0 Authorization Framework: Bearer Token Usage, RFC 6750, 2012. október, 3. szakasz, a fentiWWW-Authenticatechallenge formájához. ↩ -
Hivatalos MCP registry,
registry.modelcontextprotocol.io/v0/servers, crawlolva 2026. szeptember 7-énversion=latest-val: 282 oldal, 28 170 server,registryTypealapján összesítve distinct server names fölött. Download számok:api.npmjs.org/downloads/point/last-montha@modelcontextprotocol/sdk-hez (194 679 333 2026. augusztus 8. – szeptember 6. között) éspypistats.org/api/packages/<name>/recentamcp-hez ésfastmcp-hoz, mind ugyanazon a napon olvasva. A package méretek az npm registry dokumentumból és a PyPI JSON API-ból származnak. ↩ ↩2 -
MCP Course, Hugging Face,
huggingface.co/learn/mcp-course, 0. unit, olvasva 2026. szeptember 7-én: az előfeltételek között „Experience with at least one programming language (Python or TypeScript examples will be shown)”. ↩