MCP Server شپ کریں: TypeScript اور Python، ناپ تول کے ساتھ
ایک ہی server دو بار: تین tools، ایک resource، ایک prompt۔ 94 packages بمقابلہ 28، cold start 145 ms بمقابلہ 709۔
اس صفحے پر
زبان کی پوری بحث، کسی لفظ کے بننے سے پہلے، ناپی ہوئی یہ رہی۔
node ./incidents.js 144.5 ms
python incidents.py 709.4 ms
npx incidents-mcp 712.6 msپہلی دو لائنیں وہ موازنہ ہیں جو سب چاہتے ہیں۔ تیسری لائن پہلی لائن والا وہی TypeScript server ہے، مگر اس طرح launch کیا گیا ہے جیسے اسے حقیقت میں distribute کیا جائے گا — اور یہ Python سے صرف تین milliseconds دور آ کر ٹھہرتا ہے۔
Chapter 26 نے Model Context Protocol کو raw JSON-RPC کے ساتھ اسی کی specification کے مقابل پڑھا، کیونکہ raw JSON-RPC کی کوئی زبان نہیں ہوتی۔ اس chapter میں دو زبانیں ہیں، اور دلیل کا وزن یہاں آتا ہے: وہی server، دو بار لکھا ہوا۔ تین tools، ایک resource، ایک prompt، دونوں SDKs، کسی طرف کوئی shortcut نہیں۔ پھر transports، inspector، 401، اور وہ numbers جو کسی نے publish نہیں کیے۔
The server, and why it has these five things in it
اس حصے کا لنک: The server, and why it has these five things in itایک incident log۔ تین tools، کیونکہ Chapter 18 میں reads اور writes کی تقسیم نظر آنی چاہیے: search_incidents پڑھتا ہے، open_incident لکھتا ہے اور handle واپس دیتا ہے، resolve_incident وہ handle لیتا ہے اور بند کرتا ہے۔ ایک resource، incidents://open، کیونکہ موجودہ list پڑھنا وہ چیز ہے جسے application attach کرتی ہے۔ ایک prompt، postmortem، کیونکہ ”write this up“ کسی شخص کی slash command ہے۔ یہی Chapter 26 کی control hierarchy — model، application، person — پانچ registrations میں بدل گئی ہے۔
Handle جتنا دکھتا ہے اس سے زیادہ اہم ہے۔ Chapter 26 نے ایک toy calendar کو اس کی state module-level array میں رکھ کر توڑا تھا: protocol میں session نہیں، اس لیے creation tool ایک opaque identifier واپس کرتا ہے اور ہر بعد کی call اسے ایک عام argument کے طور پر لیتی ہے۔ دونوں files میں کچھ بھی یہ فرض نہیں کرتا کہ caller وہی process ہے جس نے اسے کھولا تھا۔
دونوں زبانوں میں وہی tool ساتھ ساتھ registered یہ رہا:
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.")پہلے یہ پڑھیں کہ ایک جیسا کیا ہے، کیونکہ finding یہی ہے۔ دونوں ایک name، ایک description، دو described string arguments اور تین annotations declare کرتے ہیں؛ دونوں ایک function ہیں؛ کوئی بھی JSON-RPC، framing، stdout یا protocol version کا ذکر نہیں کرتا۔ دونوں SDKs ایک ہی shape پر آ گئے، اور ”Tier 1“ کا مطلب یہی ہونا چاہیے۔1
دو فرق حقیقی ہیں اور دونوں بعد میں واپس آتے ہیں۔ TypeScript arguments کو schema library — یہاں Zod — سے describe کرتا ہے، اور schema ایک value ہے جو آپ لکھتے ہیں۔ Python انہیں function کی اپنی type hints سے describe کرتا ہے اور import time پر پڑھتا ہے، اسی لیے اسے function کے بارے میں وہ باتیں معلوم ہوتی ہیں جو TypeScript file نے کبھی نہیں بتائیں۔ اور error path: TypeScript isError کے ساتھ tool result واپس کرتا ہے، Python raise کرتا ہے۔ اسے یاد رکھیں۔
باقی چار registrations میں کوئی structural فرق نہیں۔ resource server.registerResource("open-incidents", "incidents://open", …) کے مقابل @server.resource("incidents://open", …) ہے؛ prompt registerPrompt کے مقابل @server.prompt ہے۔ ہر file کی آخری line transport ہے: await server.connect(new StdioServerTransport()) کے مقابل server.run()۔
Whole files: TypeScript کی 81 non-blank lines اور 3,060 bytes، Python کی 63 اور 2,555۔ اسے اتنے ہی نمک کے ساتھ لیں جتنا بنتا ہے — line counts formatter کو بھی اتنا ہی ناپتے ہیں جتنا language کو، اسی لیے نیچے headline table میں کوئی number نہیں۔
One client, both servers
اس حصے کا لنک: One client, both serversزبان کے invisible ہونے کا ثبوت ایک client ہے جو دو بار چلتا ہے، گیارہ lines میں:
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));اسے باری باری ہر server کی طرف point کریں۔ حقیقی output، trimmed:
$ 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}"}]وہی tools، وہی order، وہی handle۔ TypeScript client نہیں بتا سکتا کہ server کس زبان میں لکھا ہے، اور وہ پوچھتا بھی نہیں۔ protocol کا پورا promise یہی ہے، جو قائم ہے۔
اب دوسرے result کی whitespace دیکھیں، کیونکہ یہ cosmetic نہیں: Python SDK payloads کو pydantic_core.to_json(result, fallback=str, indent=2) کے ساتھ serialise کرتا ہے۔ resource read پر جب list میں دو incidents ہیں، TypeScript body 136 characters اور 37 o200k_base tokens ہے؛ Python body 185 اور 62۔ identical rows کے لیے اڑسٹھ فیصد زیادہ tokens، ہر بار جس نے بھی resource کو prompt میں پڑھا، اسی کے خرچ پر۔
Catalogue میں بھی یہی story ہے مگر وجہ بڑی ہے۔ دونوں servers، وہی تین tools، tools/list key by key تولا گیا:
| key | TypeScript | Python |
|---|---|---|
name | 21 | 21 |
description | 46 | 46 |
annotations | 46 | 46 |
inputSchema | 211 | 192 |
outputSchema | — | 187 |
execution | 27 | — |
| total | 342 | 480 |
Python کے input schemas سستے ہیں — TypeScript کا Zod bridge ہر ایک پر $schema اور additionalProperties stamp کرتا ہے۔ پورا 138-token gap ایک output schema ہے جو کسی نے نہیں لکھا۔ resolve_incident کو -> Incident annotate کیا گیا ہے، اس لیے SDK نے return type کے لیے JSON Schema derive کیا اور ship کر دیا۔ یہ واقعی useful ہے — یہی client کو structuredContent validate کرنے دیتا ہے — اور یہ آپ کے context window کے 187 tokens ہیں جو type hint کی وجہ سے آ رہے ہیں۔ Chapter 24 کا rule کہ definitions اہم material کو دھکیل دیتی ہیں، ان schemas پر بھی لاگو ہوتا ہے جن کے ہونے کا آپ کو علم نہیں تھا۔
Break it on purpose: the error message that leaked
اس حصے کا لنک: Break it on purpose: the error message that leakedاوپر کے دونوں error paths style choice نہیں ہیں۔ ہر server کو ایسا tool دیں جو ویسے fail ہو جیسے real integration fail ہوتی ہے، اور دیکھیں model تک کیا پہنچتا ہے۔
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}TypeScript SDK نے internal address، port، database name اور service account کو model کے context میں ڈال دیا۔ Python SDK نے اس میں سے کچھ بھی وہاں نہیں ڈالا؛ traceback stderr پر گیا اور server پر رہا۔
دونوں bug نہیں ہیں۔ دونوں decisions ہیں، اور Python والا اپنی docstring میں لکھا ہے: ToolError ”ایک ایسا failure ہے جس کی آپ نے توقع کی تھی“ اور اس کا message ”model کے پڑھنے کے لیے content میں“ واپس کیا جاتا ہے؛ باقی کچھ بھی ”crash سمجھا جاتا ہے: model صرف Error executing tool <name> دیکھتا ہے، اور server traceback کو ERROR پر log کرتا ہے“۔ crash case کی class باقی بات صاف کہتی ہے — ”اصل میں سے کچھ بھی client تک نہیں پہنچتا“۔
دونوں behaviours آدھے وقت غلط ہیں۔ Chapter 18 نے کہا تھا کہ validation error tool result کے طور پر واپس آنا چاہیے جسے model پڑھ کر correct کر سکے، کیونکہ اکثر integrations میں یہی سب سے زیادہ leverage والی line ہوتی ہے؛ Python side پر اس کے لیے ToolError explicit raise کرنا پڑتا ہے، اور bare ValueError useful sentence پھینک دیتا ہے۔ Chapter 30 کی دلیل الٹی سمت چلتی ہے: tool جو کچھ واپس کرتا ہے وہ context میں land کرتا ہے جسے بعد کا prompt injection واپس پڑھنے کی کوشش کر سکتا ہے، اور unreviewed exception string آپ کے system کا سب سے کم audited text ہے۔
دونوں سے بچنے والا rule: ہر tool کے لیے decide کریں کہ failure کو کیا کہنے کی اجازت ہے، اور وہ string خود لکھیں۔ کسی exception کے default text کو فیصلہ نہ کرنے دیں، کسی بھی زبان میں۔
Break it on purpose: one line on standard output
اس حصے کا لنک: Break it on purpose: one line on standard outputOfficial tutorial rule کو بغیر ہچکچاہٹ statement بناتا ہے: ”For STDIO-based servers: Never write to stdout. Writing to stdout will corrupt the JSON-RPC messages and break your server. The print() function writes to stdout by default, so keep it out of a STDIO server entirely.“1 Chapter 26 نے normative version quote کیا تھا — server ”اپنے stdout پر ایسی کوئی چیز MUST NOT write کرے جو valid MCP message نہ ہو“۔2
ہر server میں ایک line add کریں اور raw stream پڑھیں:
TypeScript incidents server starting
{"result":{"protocolVersion":"2025-11-25", … },"jsonrpc":"2.0","id":1}
Python {"jsonrpc":"2.0","id":1,"result":{ … }}
incidents server startingPython والا بدتر ہے، اور وجہ MCP نہیں۔ جس process کا stdout terminal کے بجائے pipe ہو، اسے block-buffered stream ملتی ہے، اس لیے stray line اس وقت flush ہوتی ہے جب buffer decide کرے — یہاں exit پر، اس response کے بعد جس سے پہلے وہ لکھی گئی تھی۔ corruption وہاں ظاہر نہیں ہوتی جہاں bug ہے۔ flush=True add کریں، یا کوئی library جو flush کرتی ہو، اور یہ move ہو جاتی ہے۔
پھر وہ حصہ جو بتاتا ہے کہ یہ ship کیوں ہو جاتا ہے۔ broken server کو تین clients دیں:
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سات-line parser فوراً مر جاتا ہے۔ official client اور Inspector دونوں shrug کرتے ہیں — line skip کر کے چلتے رہتے ہیں۔ جو rule صرف ان clients کو break کرے جنہیں کوئی استعمال نہیں کرتا، وہ production تک صحیح سلامت پہنچ جاتا ہے، اسی لیے اسے customer کے log کے بجائے یہاں جان بوجھ کر break کرنا worth ہے۔
Inspector کا CLI mode وہ آدھا حصہ ہے جو بھلا دیا جاتا ہے: npx @modelcontextprotocol/inspector --cli <command> --method tools/list catalogue print کر کے exit کرتا ہے، جس سے یہ browser UI کے برخلاف scriptable ہو جاتا ہے۔3
The table
اس حصے کا لنک: The tableدونوں SDKs صاف install ہوئے، اپنی اپنی directories میں، کچھ shared نہیں:
| TypeScript | Python | |
|---|---|---|
| package | @modelcontextprotocol/sdk 1.30.0 + zod 3.25.76 | mcp 2.1.1 |
| latest protocol revision implemented | 2025-11-25 | 2026-07-28 |
| transitive packages installed | 94 | 28 |
| installed size | 13.9 MiB | 44.3 MiB |
| files on disk | 3,386 | 2,018 |
| third-party packages loaded to serve stdio | 8 of 94 | 18 of 28 |
| bare interpreter start, median | 19.4 ms | 11.1 ms |
spawn → tools/list answered, median of 25 | 144.5 ms | 709.4 ms |
tools/list catalogue, o200k_base tokens | 342 | 480 |
ہر row الگ direction میں surprise کرتی ہے، اسی لیے comparison assume کرنے کے بجائے run کرنے کے قابل ہے۔
TypeScript تین گنا سے زیادہ packages install کرتا ہے اور bytes کے ایک تہائی سے بھی کم۔ 94 dependencies npm ecosystem کا اپنی فطرت پر ہونا ہے — fast-deep-equal، es-errors، dunder-proto۔ Python کے 28 fewer اور enormous ہیں: cryptography، pydantic-core اور uvicorn compiled artefacts ہیں۔ اگر آپ کا instinct ہے کہ dependency count فکر کی چیز ہے، یہ row counter-example ہے۔
Python کا interpreter Node سے تیز start ہوتا ہے، اور فرق معمولی نہیں — empty program پر 11.1 ms بمقابلہ 19.4 ms۔ تو cold-start row کے 565 ms language نہیں ہیں۔ یہ SDK ہے، اور loaded-packages row بتاتی ہے کیوں:
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, …ایک server جس کا واحد I/O pipe ہے، اپنی پہلی line پڑھنے سے پہلے ASGI web server، HTTP client اور TLS library import کرتا ہے۔ TypeScript SDK بھی Express، Hono، jose اور eventsource ship کرتا ہے — وہ disk پر unread پڑے رہتے ہیں، کیونکہ package boundary انہیں server/stdio.js import سے باہر رکھتی ہے۔ Python کا package ایک import graph ہے، اس لیے import mcp سب کچھ ہے: python -X importtime 727 ms کو import mcp.server.mcpserver سے attribute کرتا ہے — import profiler کے تحت measured figure، اسی لیے یہ unprofiled run کے spawn سے answer تک 709 ms سے اوپر نکلتا ہے — اور ان میں سے 269 mcp.types subtree alone کو — wire types Pydantic models ہیں، ہر protocol message per revision ایک class، اور انہیں build کرنا import پر کیا جانے والا work ہے۔ یہ design trade ہے، sloppiness نہیں — eager imports ہی وجہ ہیں کہ Python SDK اگلی line پر آپ کو run(transport="streamable-http") دے سکتا ہے بغیر second install کے۔
اور پھر opening block کی آخری row دلیل کو الٹ دیتی ہے۔ TypeScript server کو proper package کریں — bin entry، shebang، npm link، download کرنے کو کچھ نہیں — اور اسے npx کے ذریعے --no-install کے ساتھ launch کریں، یعنی published stdio server حقیقت میں جس طرح start ہوتا ہے:
node ./incidents.js 144.5 ms
npx incidents-mcp 712.6 ms (+568.1 ms of launcher)
python incidents.py 709.4 msLauncher ہر start پر 568 ms خرچ کرتا ہے — پورے TypeScript SDK import سے ساڑھے چار گنا — اور یہ ہر launch پر ادا ہوتا ہے، کیونکہ MCP host stdio server کو وہ command run کر کے start کرتا ہے۔ لہٰذا ”TypeScript پانچ گنا تیز start ہوتا ہے“ کی honest form یہ ہے: ہوتا ہے، جب تک آپ اسے normal way distribute نہیں کرتے۔ یہی caveat غالباً uvx پر بھی apply ہوتی ہے؛ اس machine پر uv installed نہیں تھا، اس لیے وہ row نہیں۔ جو measured نہیں وہ table میں نہیں جاتا۔
Two transports, and only two
اس حصے کا لنک: Two transports, and only twoChapter 26 نے stdio کی framing cover کی۔ دو چیزیں یہاں کے لیے رہ گئی تھیں۔
پہلی: server کو npx یا uvx کے ساتھ run کرنا ہی stdio transport ہے۔ کوئی separate ”package mode“ نہیں۔ host کی configuration command اور arguments name کرتی ہے؛ host اسے spawn کرتا ہے اور pipes پر بات کرتا ہے۔ اسی لیے locally ”میں اسے distribute کیسے کروں“ اور ”یہ کون سا transport بولتا ہے“ ایک ہی سوال ہیں، اور launcher کا cost shipping کے chapter میں belong کرتا ہے۔
دوسری: stdio میں authorization section بالکل نہیں، اور specification ایک line میں کہتی ہے — stdio استعمال کرنے والی implementations ”SHOULD NOT follow this specification, and instead retrieve credentials from the environment“۔4 اس کا security model operating system کا ہے، اور limit بھی: local subprocess عین ایک machine اور ایک user کو serve کرتا ہے۔
دوسرا live transport Streamable HTTP ہے: ایک single endpoint جو POST accept کرتا ہے، ہر JSON-RPC message کے لیے ایک HTTP request، اور Accept header جس میں application/json اور text/event-stream دونوں list ہونے چاہئیں کیونکہ server per request decide کرتا ہے کہ دونوں میں سے کس کے ساتھ answer دے۔5 Chapter 14 نے وہ event stream ہاتھ سے parse کیا تھا، اس لیے wire format میں کچھ نیا نہیں — صرف wrapper نیا ہے۔ current revision کی تین obligations آسانی سے miss ہو جاتی ہیں اور تینوں testable ہیں:
The version header must agree with the body
اس حصے کا لنک: The version header must agree with the bodyہر POST MCP-Protocol-Version carry کرتا ہے، اور اس کی value request کے اپنے _meta کے اندر protocolVersion سے match کرنی چاہیے۔ mismatch 400 ہے header-mismatch error کے ساتھ، shrug نہیں۔5
Two more headers are required for compliance
اس حصے کا لنک: Two more headers are required for complianceMcp-Method ہر request پر method mirror کرتا ہے؛ Mcp-Name tools/call، resources/read اور prompts/get پر params.name یا params.uri mirror کرتا ہے۔ یہ اس لیے ہیں کہ proxy bodies parse کیے بغیر route کر سکے۔5
The old shapes are gone, and answer with a refusal
اس حصے کا لنک: The old shapes are gone, and answer with a refusalGET stream، Mcp-Session-Id اور Last-Event-ID resumption سب remove ہو چکے۔ جو server صرف یہ revision بولتا ہے اسے GET یا DELETE پر 405 Method Not Allowed answer کرنا چاہیے، session header کو mint کیے بغیر ignore کرنا چاہیے، اور Last-Event-ID ignore کرنا چاہیے۔5
اب وہ measurement جو پورے chapter کو reframe کرتی ہے۔ current-revision request ہر server کو HTTP پر بھیجیں۔
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)"}}Constants behaviour سے agree کرتے ہیں: Python SDK کا LATEST_PROTOCOL_VERSION 2026-07-28 پڑھتا ہے، TypeScript SDK کا 2025-11-25۔ اوپر والے step سے header-mismatch request بھیجیں تو Python server 400 error -32020 اور message ”mcp-protocol-version header does not match the request envelope's protocol version“ کے ساتھ answer کرتا ہے؛ TypeScript SDK میں ایسا code نہیں، کیونکہ وہ اسے define کرنے والی revision implement نہیں کرتا۔
جو page دونوں کو Tier 1 list کرتا ہے وہ یہ بھی کہتا ہے ”Each SDK provides the same functionality“۔1 نیچے دی گئی تاریخ پر، current revision کے لیے، یہ sentence aspirational ہے۔ جس SDK کو آپ install کرنے والے ہیں اس میں LATEST_PROTOCOL_VERSION check کریں؛ یہ ایک line ہے، اور اس chapter کا واحد claim ہے جو ایک سال بعد بھی matter کرے گا۔
The 401, and the sentence to quote
اس حصے کا لنک: The 401, and the sentence to quoteserver کو اپنے laptop سے ہٹائیں تو کسی اجنبی کا client token کے ساتھ آتا ہے۔ یہ وہ half ہے جسے Chapter 26 نے چھوڑ دیا تھا، اور وہ half جسے multi-user product skip نہیں کر سکتا۔
Specification MCP server کو OAuth 2.1 role میں رکھتی ہے اور اس کا نام دیتی ہے: protected MCP server ایک resource server ہے، client ایک OAuth client ہے، اور authorization server کسی اور کا مسئلہ ہے۔4 اس role سے چار mandatory clauses، پوری quote کی گئی ہیں کیونکہ paraphrase کرنا ہی mistake بناتا ہے:
MCP servers, acting in their role as an OAuth 2.1 resource server, MUST validate access tokens as described in OAuth 2.1 Section 5.2. MCP servers MUST validate that access tokens were issued specifically for them as the intended audience, according to RFC 8707 Section 2. […] MCP clients MUST NOT send tokens to the MCP server other than ones issued by the MCP server's authorization server. MCP servers MUST only accept tokens that are valid for use with their own resources. MCP servers MUST NOT accept or transit any other tokens.4
”Must not accept or transit“ anti-passthrough rule ہے، اور یہی وجہ ہے کہ audience apparatus موجود ہے۔ جو server bearer token کو کسی third-party API پر replay کرتا ہے، وہ confused deputy ہے: وہ جس نے اسے call کیا اسے اپنا trust lend کرتا ہے۔ rule reuse کو forbid کرتا ہے، صرف storage کو نہیں۔
اسے enforceable بنانے کے لیے چار RFCs چاہئیں، ہر ایک کا ایک کام۔6 RFC 9728 وہ طریقہ ہے جس سے client authorization server کو سرے سے find کرتا ہے: MCP server protected-resource-metadata document serve کرتا ہے اور 401 اس کی طرف point کرتا ہے۔ RFC 8707 resource parameter ہے — client کو authorization request اور token request دونوں میں server کی canonical URI بھیجنی چاہیے، ”regardless of whether authorization servers support it“، تاکہ issued token اپنی audience name کرے۔ RFC 9207 دوسری side سے loop close کرتا ہے: client redirect سے پہلے issuer record کرتا ہے اور returned iss کو exact string سے compare کرتا ہے، no normalisation — نہ case folding، نہ default-port elision، نہ trailing slash۔ اور RFC 7591، Dynamic Client Registration، اب Client ID Metadata Documents کے حق میں deprecated ہے، ”retained for backwards compatibility with authorization servers that do not support“ them۔4
دونوں servers پر اسے ایسے token verifier کے ساتھ wire کریں جو audience check کے سوا کچھ نہ کرے۔ TypeScript ladder:
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"}دونوں SDKs وہ document serve کرتے ہیں اور دونوں 401 کو اس کی طرف point کرتے ہیں، جو discovery کی پوری story ہے: ایسا client جس نے آپ کا server کبھی نہیں دیکھا، refusal سے سیکھتا ہے کہ authenticate کہاں کرنا ہے۔ 403 مختلف جانور ہے — token ٹھیک ہے، scope نہیں — اور challenge وہ missing چیز name کرتا ہے تاکہ client start over کے بجائے step up کر سکے۔
دو rungs مختلف ہیں، اور کوئی فرق specification میں نہیں۔ TypeScript SDK بغیر expiry claim والے token کو refuse کرتا ہے؛ Python والا 200 واپس کرتا ہے، کیونکہ expires_at اس کے AccessToken پر optional ہے اور None کا مطلب ”no opinion“ ہے۔ اور Python 403 error_description="Required scope: incidents:read" carry کرتا ہے، مگر specification کے کہنے کے باوجود scope parameter نہیں۔ verifier library default accept کرنے کی جگہ نہیں: audience check دونوں زبانوں میں آپ کو لکھنا ہے، expiry بھی۔
اسی run سے ایک honest nit۔ endpoint پر GET نے Express wiring پر 404 اور Python والے پر 400 Bad Request: Missing session ID answer کیا، جہاں specification 405 Method Not Allowed مانگتی ہے اور جہاں ”session ID“ وہ vocabulary ہے جسے یہ revision remove کر چکی ہے۔ کوئی خطرناک نہیں؛ دونوں mid-migration ecosystem کی shape ہیں۔
Where servers actually live
اس حصے کا لنک: Where servers actually liveshipping کا آخری piece یہ ہے کہ آپ publish کہاں کرتے ہیں، اور اس کا answer ایک number کے ساتھ ہے۔ آج official registry کے ہر server کو latest version پر crawl کیا گیا:7
| servers | |
|---|---|
| total (latest version, not deleted) | 28,170 |
| active / deprecated | 27,853 / 317 |
| ship at least one installable package | 13,065 |
| remote only — a URL, nothing to install | 14,696 |
| npm | 8,275 |
| PyPI | 3,603 |
| OCI images | 867 |
mcpb bundles | 706 |
| NuGet / Cargo | 107 / 43 |
دو readings، opposite directions میں۔ published servers کے لحاظ سے npm 2.3 to 1 lead کرتا ہے — وہ number جو لوگ ecosystem کو TypeScript کہنے پر quote کرتے ہیں۔ downloads کے لحاظ سے Python lead کرتا ہے: گزشتہ تیس دنوں میں mcp نے 286.7 million لیے، @modelcontextprotocol/sdk کے 194.7 million کے مقابل، fastmcp کے 72.1 million add کرنے سے پہلے۔7 دونوں Tier 1 ہیں، normative schema schema.ts ہے، اور official ”Build an MCP server“ tutorial Python tab پر کھلتا ہے۔1 آپ کے ذہن میں جس بھی half کی picture تھی، دوسرا half بھی سچ ہے۔
اور وہ row جو دونوں سے زیادہ matter کرتی ہے: registry کے آدھے سے زیادہ — 28,170 میں سے 14,696 — میں install کرنے کو کچھ نہیں۔ یہ web services ہیں۔ transport tallies دوسری طرف سے agree کرتے ہیں: 14,290 package entries میں سے 13,787 stdio declare کرتی ہیں؛ 16,640 remote entries میں سے 15,570 Streamable HTTP declare کرتی ہیں اور 1,070 اب بھی deprecated HTTP+SSE declare کرتی ہیں۔ لہٰذا ”MCP server آپ کے laptop پر subprocess ہے“ ایک shrinking minority کو describe کرتا ہے، اور 14,696 میں سے ہر ایک کو environment variable کے بجائے اوپر والا section چاہیے۔
تفصیلات دکھائیں
جان بوجھ کر bilingual، اور اس کی precedent۔
یہ course کا اکلوتا bilingual chapter ہے، کیونکہ honest answer split ہوتا ہے: registry npm-first ہے اور downloads Python-first، ایک ہی وقت میں، آج۔ دونوں میں سے صرف ایک لکھنا سوال کا آدھا حصہ چھوڑ دینا اور ecosystem کو غلط describe کرنا ہوتا۔ open میں precedent موجود ہے — Hugging Face MCP Course اپنی prerequisites میں ”Experience with at least one programming language (Python or TypeScript examples will be shown)“ list کرتا ہے، اور دونوں پڑھاتا ہے۔ جس protocol کی پوری value implementations کی تعداد ہو، وہاں monolingual ہونا بری جگہ ہے۔
Dated section: everything above that has a shelf life
اس حصے کا لنک: Dated section: everything above that has a shelf life7 September 2026 کو protocol revision 2026-07-28 کے مقابل پڑھا اور measured کیا گیا۔
| value | |
|---|---|
@modelcontextprotocol/sdk | 1.30.0, published 27 July 2026; 4,322,438 bytes unpacked, 693 files, 17 direct dependencies |
| latest revision it implements | 2025-11-25 |
mcp (PyPI) | 2.1.1, published 25 August 2026; 357,912-byte wheel, plus mcp-types 2.1.1 at 69,656 bytes |
| latest revision it implements | 2026-07-28 |
| SDK tiers | TypeScript, Python, C#, Go, Rust at Tier 1; Java, Ruby at Tier 2; Swift, PHP, Kotlin at Tier 3 |
| registry servers | 28,170 |
| downloads, last 30 days | mcp 286,653,871 · fastmcp 72,097,269 · @modelcontextprotocol/sdk 194,679,333 |
ایک migration note جو number نہیں۔ mcp 2.x میں، FastMCP کا نام MCPServer رکھا گیا، اور تقریباً ہر online tutorial اب بھی old import سے کھلتا ہے۔ SDK ایک module ship کرتا ہے جس کا واحد مقصد یہی سمجھانا ہے، جو اس chapter کی سب سے considerate deprecation ہے:
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.So which one
اس حصے کا لنک: So which onetable سامنے ہو تو recommendation boring ہے، جو اچھی sign ہے۔
اگر server ایسی web application کے اندر رہتا ہے جسے آپ پہلے ہی run کرتے ہیں، اسے TypeScript میں لکھیں۔ وہی process، وہی deploy، وہی request handler؛ Streamable HTTP ایک endpoint ہے جو آپ دوسروں کے ساتھ add کرتے ہیں؛ اور 13.9 MiB اور 145 ms free ہیں کیونکہ runtime پہلے ہی up تھا۔ 14,696 remote servers میں سے اکثر یہی ہیں۔
اگر server data tooling wrap کرتا ہے، اسے Python میں لکھیں۔ جسے آپ expose کر رہے ہیں وہ pandas، warehouse client، notebook بھر transforms ہیں، اور دوسری language میں server ایک subprocess call ہوگا جس نے schema پہن رکھا ہے۔ ایسے service میں جو once start ہوتی ہے import کے سات سو milliseconds cost نہیں؛ subprocess میں جسے host سارا دن relaunch کرے، یہ cost ہے۔
اور فی الحال revision row دونوں پر override کرتی ہے۔ اگر آپ کو 2026-07-28 چاہیے — multi-round-trip requests، resultType، cache hints، server/discover — تو دونوں SDKs میں سے ایک کے پاس آج ہے اور دوسرے کے پاس نہیں۔
Where this goes next
اس حصے کا لنک: Where this goes nextاب آپ وہی server دونوں زبانوں میں ship کر سکتے ہیں، choice کو preference کے بجائے table سے defend کر سکتے ہیں، اسے دونوں live transports پر run کر سکتے ہیں، اور اسے ایسا token دے سکتے ہیں جسے وہ refuse کرے گا۔
جو آپ نے بنایا وہ اب بھی ایک function ہے: schema، endpoint، deterministic چیز جسے model invoke کرتا ہے۔ knowledge کی ایک پوری class اس shape میں fit نہیں ہوتی — ہم postmortem کیسے لکھتے ہیں، ہمارے incident reports کو کون سے fields چاہئیں، ہم چیزیں کس order میں کرتے ہیں اور کیوں۔ یہ procedure ہے، prose ہے، اور اسے tool description میں force کرنا وہ طریقہ ہے جس سے system prompts دو ہزار tokens تک grow کرتے ہیں جو ہر single turn پر paid ہوتے ہیں، چاہے conversation incidents کے بارے میں ہو یا نہیں۔
Chapter 28 دوسرا answer ہے: ایک folder جس میں SKILL.md ہے جسے model call کرنے کے بجائے read کرتا ہے، تین levels میں loaded تاکہ reference material کی cost تقریباً کچھ نہ ہو جب تک وہ turn نہ آئے جس میں اس کی ضرورت ہے۔ اس کی کوئی main language نہیں، اور یہی پہلی چیز ہے جو یہ سکھاتا ہے۔
Sources and method
اس حصے کا لنک: Sources and methodیہ سب 7 September 2026 کو Node 22.22.3 اور Python 3.14.4 پر measured کیا گیا، @modelcontextprotocol/sdk 1.30.0 with zod 3.25.76 and mcp 2.1.1 کے against، ہر ایک کو اپنی throwaway directory میں install کر کے۔ Timings 25 launches کی medians ہیں، wall clock spawn سے اس line تک جو tools/list response carry کرتی ہے؛ token counts ہر definition کے JSON پر tiktoken کے ذریعے o200k_base ہیں۔ کوئی paid API call نہیں ہوئی: یہاں کسی model کی ضرورت نہیں۔
دو servers 81 اور 63 non-blank lines ہیں؛ ان کے تین tools میں سے ایک اوپر دونوں زبانوں میں reproduce کیا گیا ہے، اور باقی چار registrations صرف ویسے differ کرتے ہیں جیسے describe کیا گیا۔ Python SDK کی error-disclosure policy mcp/server/mcpserver/exceptions.py میں ToolError اور UnexpectedToolError کی docstrings سے quote کی گئی ہے؛ pretty-printing default mcp/server/mcpserver/resources/types.py اور utilities/func_metadata.py میں pydantic_core.to_json(result, fallback=str, indent=2) ہے۔ protocol-version constants installed packages سے پڑھے گئے، changelog سے نہیں: mcp_types/version.py میں LATEST_PROTOCOL_VERSION اور TypeScript SDK کے types.js میں۔
حوالہ جات
اس حصے کا لنک: حوالہ جات-
SDKs,
modelcontextprotocol.io/docs/sdk، اور Build an MCP server,modelcontextprotocol.io/docs/develop/build-server، دونوں read 7 September 2026۔ tier table، sentence ”Each SDK provides the same functionality but follows the idioms and best practices of its language“، tutorial کے language-tab order (Python, TypeScript, Java, Kotlin, C#, Ruby, Rust, Go)، اورprint()اورstdoutکے بارے میں quoted logging rule کا source۔ ↩ ↩2 ↩3 ↩4 -
stdio transport,
.../basic/transports/stdio۔ newline framing اورstdoutpurity rule کا source۔ Chapter 26 اس page کو پورا پڑھتا ہے؛ یہاں اسے اس line کے لیے cite کیا گیا ہے جسے broken server violate کرتا ہے۔ ↩ -
MCP Inspector,
modelcontextprotocol.io/docs/2026-07-28/tools/inspector, read 7 September 2026۔ ایک package، ایک binary کے پیچھے تین clients — web،--cliاور--tui— جو one core، transports کا one set اور disk پر one OAuth state share کرتے ہیں۔ CLI نے یہاں catalogue traces produce کیے۔ ↩ -
Authorization,
modelcontextprotocol.io/specification/2026-07-28/basic/authorization, read 7 September 2026۔ resource-server role کا source؛ چار token-handling clauses جو پوری quote کی گئیں؛ یہ requirement کہ servers RFC 9728 implement کریں اور clients اسے discovery کے لیے use کریں؛resourceparameter rules اور canonical-URI definition؛ issuer-validation table؛ Dynamic Client Registration کی deprecation؛401/403/400table اورinsufficient_scopechallenge؛ اور stdio exemption، ”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، اور Transports overview,.../basic/transports۔ single-endpoint POST rule، dualAcceptrequirement،MCP-Protocol-Versionheader اور اس کے must-match-the-body rule،Mcp-MethodاورMcp-Nameheaders جنہیں ”REQUIRED for compliance“ کہا گیا، GET stream، sessions اورLast-Event-IDکا removal،405guidance، mandatoryOriginvalidation، اور 2024-11-05 HTTP+SSE transport کو SEP-2596 کے تحت Deprecated classify کرنے کا source۔ ↩ ↩2 ↩3 ↩4 -
وہ چار جن پر specification lean کرتی ہے، اس draft کے ساتھ جسے یہ profile کرتی ہے: The OAuth 2.1 Authorization Framework,
draft-ietf-oauth-v2-1-13۔ Campbell, B., Bradley, J. and Tschofenig, H., Resource Indicators for OAuth 2.0, RFC 8707, February 2020 —resourceparameter اور وہ audience جس سے یہ bind کرتا ہے۔ Jones, M.B., Hunt, P. and Parecki, A., OAuth 2.0 Protected Resource Metadata, RFC 9728, April 2025 — وہ document جس کی طرف401point کرتا ہے۔ Meyer zu Selhausen, K. and Fett, D., OAuth 2.0 Authorization Server Issuer Identification, RFC 9207, March 2022 —issparameter اور exact-string comparison۔ Richer, J. (ed.) et al., OAuth 2.0 Dynamic Client Registration Protocol, RFC 7591, July 2015, deprecated for this use۔ اور Jones, M. and Hardt, D., The OAuth 2.0 Authorization Framework: Bearer Token Usage, RFC 6750, October 2012, section 3، اوپر والےWWW-Authenticatechallenge shape کے لیے۔ ↩ -
Official MCP registry,
registry.modelcontextprotocol.io/v0/servers, crawled 7 September 2026 withversion=latest: 282 pages, 28,170 servers, tallied byregistryTypeover distinct server names۔ Download figures:api.npmjs.org/downloads/point/last-monthfor@modelcontextprotocol/sdk(194,679,333 for 8 August – 6 September 2026) andpypistats.org/api/packages/<name>/recentformcpandfastmcp, both read the same day۔ Package sizes npm registry document اور PyPI JSON API سے آئے۔ ↩ ↩2