Ugrás a tartalomra
27/3027/30. fejezet

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.

spawn → tools/list answered, median of 25 launchesTEXT
node ./incidents.js         144.5 ms
python incidents.py         709.4 ms
npx incidents-mcp           712.6 ms

Az 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 benne

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

incidents.tsTS
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 }) }] };
  },
);
incidents.pyPYTHON
@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.

Annak bizonyítéka, hogy a nyelv láthatatlan: egy client kétszer futtatva, tizenegy sorban:

client.tsTS
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:

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

keyTypeScriptPython
name2121
description4646
annotations4646
inputSchema211192
outputSchema187
execution27
összesen342480

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 message

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

tools/call on a tool that raisesTEXT
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 outputon

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

raw stdout, first two linesTEXT
TypeScript  incidents server starting
            {"result":{"protocolVersion":"2025-11-25", … },"jsonrpc":"2.0","id":1}

Python      {"jsonrpc":"2.0","id":1,"result":{ … }}
            incidents server starting

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

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

A 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

Mindkét SDK tisztán települt, a saját könyvtárába, semmi közös:

TypeScriptPython
package@modelcontextprotocol/sdk 1.30.0 + zod 3.25.76mcp 2.1.1
legújabb implementált protocol revision2025-11-252026-07-28
telepített transitive packages9428
telepített méret13.9 MiB44.3 MiB
fájlok a disken3,3862,018
stdio kiszolgálásához betöltött third-party packages8 / 9418 / 28
csupasz interpreter start, medián19.4 ms11.1 ms
spawn → tools/list válaszolt, 25 mérés mediánja144.5 ms709.4 ms
tools/list catalogue, o200k_base token342480

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:

third-party modules loaded to answer one tools/list over stdioTEXT
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:

median of 25, spawn → tools/list answeredTEXT
node ./incidents.js       144.5 ms
npx incidents-mcp         712.6 ms      (+568.1 ms of launcher)
python incidents.py       709.4 ms

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

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 bodyval

Minden 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-hez

A 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álaszolnak

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

POST /mcp, MCP-Protocol-Version: 2026-07-28TEXT
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 kell

Vigyé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:

POST /mcp — TypeScript, with requireBearerAuthTEXT
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":[…]}}
GET /.well-known/oauth-protected-resource/mcpTEXT
{"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.

A 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 / deprecated27,853 / 317
legalább egy telepíthető package-et szállít13,065
csak remote — egy URL, nincs mit telepíteni14,696
npm8,275
PyPI3,603
OCI images867
mcpb bundles706
NuGet / Cargo107 / 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ága

Olvasva és mérve 2026. szeptember 7-én, a 2026-07-28 protocol revision ellen.

érték
@modelcontextprotocol/sdk1.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ál2025-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ál2026-07-28
SDK tiersTypeScript, Python, C#, Go, Rust Tier 1; Java, Ruby Tier 2; Swift, PHP, Kotlin Tier 3
registry serverek28,170
downloadok, utolsó 30 napmcp 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:

from mcp.server.fastmcp import FastMCPTEXT
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.

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

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


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

  1. 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 a print()-ről és stdout-ről idézett logging rule forrása. 2 3 4

  2. stdio transport, .../basic/transports/stdio. A newline framing és a stdout purity 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.

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

  4. 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; a resource paraméter szabályai és a canonical-URI definíció; az issuer-validation table; a Dynamic Client Registration deprecationje; a 401/403/400 table és a insufficient_scope challenge; 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

  5. Streamable HTTP, .../basic/transports/streamable-http, és Transports overview, .../basic/transports. Az egy-endpointos POST szabály, a kettős Accept követelmény, a MCP-Protocol-Version header és a bodyval-egyeznie-kell szabály, a „REQUIRED for compliance”-ként leírt Mcp-Method és Mcp-Name headerek, a GET stream, sessions és Last-Event-ID eltávolítása, a 405 útmutatás, a kötelező Origin validation, valamint a 2024-11-05 HTTP+SSE transport SEP-2596 alatti Deprecated besorolásának forrása. 2 3 4

  6. 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 — a resource paramé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 egy 401 mutat. Meyer zu Selhausen, K. és Fett, D., OAuth 2.0 Authorization Server Issuer Identification, RFC 9207, 2022. március — a iss paramé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 fenti WWW-Authenticate challenge formájához.

  7. Hivatalos MCP registry, registry.modelcontextprotocol.io/v0/servers, crawlolva 2026. szeptember 7-én version=latest-val: 282 oldal, 28 170 server, registryType alapján összesítve distinct server names fölött. Download számok: api.npmjs.org/downloads/point/last-month a @modelcontextprotocol/sdk-hez (194 679 333 2026. augusztus 8. – szeptember 6. között) és pypistats.org/api/packages/<name>/recent a mcp-hez és fastmcp-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

  8. 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)”.


Készítette

David Vicente Campos

A NeuraLIA Labs alapítója és a MyRealFood társalapítója

Mérnökinformatikus vagyok, a Leóni Egyetemen végeztem. Társalapítottam a MyRealFoodot, ahol CTO-ként felépítettem azt az alkalmazást, amelyet emberek milliói használtak arra, hogy egészségesebben táplálkozzanak, és megalapítottam a NeuraLIA Labst, ahol AI-termékeket fejlesztek. Itt arról írok, amit menet közben meg kellett értenem, úgy, ahogy szerettem volna, hogy valaki elmagyarázza nekem.

Továbbiak a szerzőről

Közzétette a NeuraLIA Labs.

Kapj új bejegyzéseket a postaládádba

AI-hírek, útmutatók és termékfrissítések — rövid email, amikor valami igazán hasznosat publikálunk.

Kurzusindex

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev11 perc olvasás

A Jev AI-modell döntésekre készült, nem prózára

A TypeSafe AI Jev modellje azért kap figyelmet, mert a szoftveres intelligenciát valószínűségi problémaként kezeli: válaszd ki a megfelelő ágat, rendelj hozzá bizalmi szintet, és ne fizess egy LLM-nek szövegírásért, amikor a kódnak döntésre van szüksége.

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering11 perc olvasás

Kontextustervezés hosszú távú AI-ügynökökhöz

A hosszú ideig futó ügynökök nem csak azért vallanak kudarcot, mert kicsi az ablak. Akkor hibáznak, amikor a fájlok, eszközkimenetek és elavult előzmények kiszorítják azt a feladatot, amelyet az ügynöknek be kellett volna fejeznie.

Készen állsz, hogy a LIA válasszon helyetted?

Építs az összes AI-modellel egy helyen – kezdd el ma, ingyen.