
वेब UI, CLI या TUI से Model Context Protocol (MCP) सर्वरों का निरीक्षण, डिबग और दृश्य रूप से परीक्षण करें, जिसमें टूल/संसाधन अन्वेषण, अनुरोध लॉगिंग और OAuth समर्थन शामिल है।
Model Context Protocol (MCP) सर्वरों का निरीक्षण करने के लिए एक डेवलपर टूल। यह एक ही पैकेज, @modelcontextprotocol/inspector, के रूप में आता है, जो सर्वर का निरीक्षण करने के तीन तरीके प्रदान करता है:
तीनों एक ही वैश्विक mcp-inspector बाइनरी के माध्यम से चलते हैं:```bash
npx @modelcontextprotocol/inspector # web UI (default)
npx @modelcontextprotocol/inspector --cli # CLI
npx @modelcontextprotocol/inspector --tui # TUI
> **v1 से अपग्रेड कर रहे हैं?** [v1 → v2 माइग्रेशन गाइड](https://github.com/modelcontextprotocol/inspector/blob/HEAD/docs/v1-to-v2-migration.md) पढ़ें — CLI फ़्लैग, नया `--config` बनाम `--catalog` विभाजन, Node इंजन बंप, और अब क्या शिप नहीं होता।
> **रिपो स्थिति।** यह Inspector की **v2** लाइन है। सक्रिय विकास **`v2/main`** पर होता है (डेवलप ब्रांच — सभी v2 PR इसी को लक्षित करते हैं), जिसे माइलस्टोन रिलीज़ पर **`main`** में मर्ज किया जाता है; `main` डिफ़ॉल्ट ब्रांच है और इसमें नवीनतम रिलीज़ हुई v2 होती है, जो npm `latest` टैग पर प्रकाशित होती है। लीगेसी **v1** लाइन **`v1/main`** पर रहती है — केवल सुरक्षा फिक्स, सीधे उसी ब्रांच से npm `v1-latest` टैग पर प्रकाशित (`npx @modelcontextprotocol/inspector@v1-latest`)। ब्रांच/बोर्ड परंपराओं के लिए [`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md) देखें।
## प्रोजेक्ट लेआउट
v2 एक npm वर्कस्पेस **नहीं** है। `clients/*` के अंतर्गत प्रत्येक क्लाइंट अपना स्वयं का `package.json` और `node_modules` रखता है; साझा कोड `core/` में रहता है और उसे `@inspector/core` बिल्ड-टाइम alias के माध्यम से उपभोग किया जाता है (इसका अपना कोई `package.json` नहीं है)। रूट पर एक ही `npm install` हर क्लाइंट में इंस्टॉल को कैस्केड करता है ([सेटअप](#setup) देखें)।```
inspector/
├── clients/
│ ├── web/ # Web client (Vite + React + Mantine). src/ = browser app; server/ = Node dev/prod backend
│ ├── cli/ # CLI client (tsup bundle, @inspector/core alias)
│ ├── tui/ # TUI client (Ink + React, tsup bundle)
│ └── launcher/ # Shared launcher — provides the `mcp-inspector` bin, dispatches to web/cli/tui
├── core/ # Shared code consumed via the `@inspector/core` alias (no package.json)
│ ├── auth/ # OAuth: providers, discovery, storage, endpoint overrides, mid-session recovery (browser/node/remote backends)
│ ├── client/ # Install-level client config (`client.json`): browser-safe parse/validate + Node load/save, remote backend, secrets
│ ├── json/ # JSON + parameter/argument conversion utilities, and the nullable-union
│ │ # schema collapse shared by the web and TUI form builders
│ ├── logging/ # Silent pino logger singleton
│ ├── mcp/ # InspectorClient runtime, state stores, transports, config import,
│ │ # and the RFC 6570 URI-template helpers the web form and TUI expand through
│ ├── node/ # Node-only shared helpers: version reader, hostUrl (host normalize/canonicalize + all-interfaces/loopback detection)
│ ├── react/ # React hooks over the state stores
│ └── storage/ # File I/O helpers for the OAuth persist backends
├── test-servers/ # Composable MCP test servers + fixtures used by integration tests
├── scripts/ # Root build/verify tooling (install cascade, smokes, verify-build-gate, verify-format-coverage, verify-dep-lockstep, pack:verify)
├── docs/ # Task-oriented guides (v1→v2 migration, server configuration, MCP App review, launcher/config plan)
├── specification/ # Design/build specifications
├── AGENTS.md # Contribution rules for agents AND humans (see below)
└── README.md # You are here
प्रत्येक क्लाइंट के पास क्लाइंट-विशिष्ट विवरण के साथ अपना स्वयं का README है: web · cli · tui · launcher.
कार्य-उन्मुख मार्गदर्शिकाएँ docs/ में उपलब्ध हैं:
--config बनाम --catalog सेमेन्टिक्स पहले/बाद के उदाहरणों के साथ, Node इंजन संस्करण वृद्धि (>=22.7.5 → >=22.19.0), env-var पुनर्नामकरण, और वे सब-पैकेज जो अब रिलीज़ नहीं किए जाते।--catalog बनाम --config, एड-हॉक लक्ष्य, -- विभाजक, फ़ाइल प्रारूप और इसके Inspector-विशिष्ट प्रति-सर्वर फ़ील्ड। यह तीनों क्लाइंट द्वारा साझा किया जाता है; cli और tui के README अपने सर्वर-विकल्प अनुभागों को इसी पर सौंपते हैं।--app-info प्रोब → डीप-लिंक नेविगेशन → रेंडर किया गया विजेट, साथ ही OAuth हैंडऑफ़ और प्रॉक्सी समर्थन।Node >=22.19.0 आवश्यक है।```bash
npm install # root install; postinstall cascades into every client
- **नया क्लोन:** रिपॉज़िटरी रूट पर `npm install` चलाएँ।
- **ऐसे पुल के बाद जो किसी क्लाइंट की निर्भरताएँ बदलता है:** हर क्लाइंट को फिर से सिंक करने के लिए रूट पर `npm install` दोबारा चलाएँ।
कैस्केड (`scripts/install-clients.mjs`) केवल डेव-मोड के लिए है — यह तब जल्दी बाहर निकल जाता है जब पैकेज किसी निर्भरता के रूप में इंस्टॉल होता है, और प्रकाशित टारबॉल में केवल प्रत्येक क्लाइंट का `build/` शामिल होता है, इसलिए अंतिम उपयोगकर्ता प्रभावित नहीं होते। इसे छोड़ने के लिए `INSPECTOR_SKIP_CLIENT_INSTALL=1` सेट करें।
**निर्भरता कहाँ घोषित की जाती है।** MCP SDK पैकेज (`@modelcontextprotocol/client`, `core`, `server`, `server-legacy`, `ext-apps`) केवल **रूट** `package.json` में रहते हैं — कभी किसी क्लाइंट में नहीं। Node रिज़ॉल्यूशन ऊपर की ओर बढ़ता है, इसलिए रूट इंस्टॉल हर क्लाइंट की चेन पर होता है, और प्रकाशित टारबॉल पहले से ही रूट मैनिफेस्ट के विरुद्ध रिज़ॉल्व होता है। उन्हें प्रति क्लाइंट घोषित करने से दूसरी कॉपी इंस्टॉल होती है जो रूट की कॉपी से विचलित हो सकती है — यही कारण है कि [#1970](https://github.com/modelcontextprotocol/inspector/issues/1970) से पहले `ext-apps` के दो संस्करण (और ट्रांज़िटिव v1 `@modelcontextprotocol/sdk` के भी) ट्री में समाप्त हुए थे — और `client`/`core` की दूसरी कॉपी वह विफलता है जिसके लिए `vitest.shared.mts` में `dedupe` वर्कअराउंड मौजूद है। यही रूट-केवल स्थान उन सभी चीज़ों के लिए लागू होता है जो केवल रूट-स्वामित्व वाले कोड के माध्यम से पहुँची जाती हैं और जिनका अपना कोई मैनिफेस्ट नहीं होता (`test-servers/src`, `core/`), और `vitest.shared.mts` उन्हें रिपॉज़िटरी रूट पर अलियास करता है — `express` और `yaml`, दोनों `test-servers/src` से पहुँचे जाते हैं, आज ये दो ही हैं। **ऐसा पैकेज `dependency` है या `devDependency`, यह इस बात से तय होता है कि उसे रनटाइम पर कौन उपभोग करता है, न कि वह कहाँ घोषित है:** `core/` जो भी रनटाइम पर इम्पोर्ट करता है, उसे रूट **`dependency`** होना चाहिए, क्योंकि क्लाइंट बिल्ड्स npm पैकेजों को एक्सटरनलाइज़ करते हैं और एक प्रकाशित इंस्टॉल उन्हें रूट मैनिफेस्ट से रिज़ॉल्व करता है, जहाँ devDependencies अनुपस्थित होते हैं। `express` केवल परीक्षण के लिए है और यह एक devDependency है; `yaml` वर्तमान में `dependencies` में है। **`vite` और `@vitejs/plugin-react` इसी कारण से रूट `dependencies` हैं, यह कोई भूल नहीं है** — ये बिल्ड टूलिंग जैसे दिखते हैं, लेकिन `clients/web/server/start-vite-dev-server.ts` इन्हें `mcp-inspector --web --dev` के लिए रनटाइम पर इम्पोर्ट करता है, और `clients/web/tsup.runner.config.ts` दोनों को `external` के रूप में सूचीबद्ध करता है, इसलिए एक प्रकाशित इंस्टॉल इन्हें रूट मैनिफेस्ट से रिज़ॉल्व करता है। इन्हें `devDependencies` में ले जाने से उपभोक्ताओं के लिए `--web --dev` टूट जाएगा (और `ensure-web-build.ts` में ऑन-डिमांड `vite build` भी), जबकि सभी स्थानीय जाँचें पास हो जाएँगी। इसका मतलब है कि ये `npm audit --omit=dev` के अंतर्गत दिखाई देते हैं, जो एक विशेषता है: वे वास्तव में प्रोडक्शन ट्री में हैं।
## विकास के दौरान चलाना
रोज़मर्रा के वेब पुनरावृत्ति के लिए, Vite को सीधे वेब क्लाइंट से चलाएँ (तेज़ HMR, किसी लॉन्चर बिल्ड की आवश्यकता नहीं):```bash
cd clients/web && npm run dev
नीचे दिए गए लॉन्चर-संचालित स्क्रिप्ट निर्मित लॉन्चर को चलाते हैं, इसलिए पहले बिल्ड करें (npm run build):```bash
npm run web # prod web launcher against clients/web/dist
npm run web:dev # web launcher in --dev mode (Vite)
## `@inspector/core` साझा पैकेज

`core/` में तीनों क्लाइंट्स द्वारा साझा किया गया तर्क (logic) होता है ताकि वेब, CLI और TUI समान व्यवहार करें। इसका प्रवेश बिंदु **`InspectorClient`** क्लास (`core/mcp/`) है, जिसके पास MCP सर्वर से कनेक्शन, request/response जीवनचक्र और स्टेट स्टोर्स का एक सेट है; `core/react/` उन स्टोर्स पर React हुक उजागर करता है, जिन्हें वेब और TUI (Ink) दोनों के React ट्री उपभोग करते हैं। OAuth (`core/auth/`) को isomorphic तर्क तथा browser/node/remote बैकएंड में विभाजित किया गया है, ताकि वही प्रवाह ब्राउज़र, Node और रिमोट बैकएंड पर काम करें।
`core/` में जानबूझकर **कोई `package.json` नहीं है** — यह अपने आप प्रकाशित नहीं होता। प्रत्येक क्लाइंट इसे `@inspector/core` alias के माध्यम से बंडल करता है:
- **CLI / TUI:** उनके `tsup.config.ts` में `esbuildOptions.alias` `@inspector/core` → repo `core/` निर्देशिका को मैप करता है, और `noExternal: [/^@inspector\/core/]` इसे बंडल में इनलाइन करता है।
- **Web:** ब्राउज़र ऐप और Node बैकएंड runner के लिए `clients/web/vite.config.ts` में वही alias।
`core/` को अपने स्वयं के पैकेज के रूप में प्रकाशित करना (जैसे कि तीसरे पक्ष द्वारा इसे आधार बनाने के लिए) जानबूझकर स्थगित किया गया है — देखें issue [#1636](https://github.com/modelcontextprotocol/inspector/issues/1636)।
## वेब क्लाइंट: "dumb components" + Storybook
v2 वेब क्लाइंट **presentational ("dumb") घटकों** से बना है — वे डेटा और कॉलबैक को props के रूप में स्वीकार करते हैं और केवल display तर्क रखते हैं, बिना किसी सीधे डेटा फ़ेचिंग या क्लाइंट स्टेट के। स्टेट `@inspector/core` हुक से आता है, जो ट्री के लगभग शीर्ष पर जुड़ा होता है। यह घटकों को पृथक, परीक्षण योग्य और प्रलेखन योग्य बनाए रखता है।
यही दृष्टिकोण **Storybook** को यहाँ प्रथम श्रेणी बनाता है: हर स्क्रीन और एलिमेंट घटक के पास `*.stories.tsx` फ़ाइल (96+ stories) है जो उसे fixture props के विरुद्ध रेंडर करती है। Storybook **play functions** इंटरैक्शन परीक्षणों का काम करते हैं, जो CI में हेडलेस चलते हैं (`npm run ci:storybook`, Playwright के माध्यम से Chromium)।
स्टाइलिंग सख्त Mantine-first परंपरा का पालन करती है (CSS क्लासों के ऊपर theme variants और component props, कच्चे रंग literals के ऊपर `--inspector-*` CSS custom properties)। पूर्ण नियम [`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md) में **React instructions** के अंतर्गत हैं — वेब UI को छूने से पहले उन्हें पढ़ें। एलिमेंट घटक `clients/web/src/components/elements/` में रहते हैं; theme variants `clients/web/src/theme/` में।
## परीक्षण सर्वर
`test-servers/` **composable MCP servers** प्रदान करता है, जिनका उपयोग integration और smoke सूट करते हैं, ताकि परीक्षण मॉक के बजाय वास्तविक transport पर वास्तविक सर्वर को आज़माएँ। एक सर्वर **presets** से इकट्ठा किया जाता है (`test-servers/src/preset-registry.ts` में fixture factories — tools, resources, prompts, tasks, elicitation, sampling, OAuth, …) और इसे दो तरीकों से चलाया जा सकता है:
- **In-process** — factories (`createTestServerHttp`, `createEchoTool`, …) आयात करें और सर्वर को परीक्षण के event loop के अंदर चलाएँ (HTTP integration पथों द्वारा उपयोग)।
- **As a subprocess** — `test-servers/build/test-server-stdio.js` एक वास्तविक stdio चाइल्ड के रूप में स्पॉन किया जाता है (CLI smoke और stdio integration परीक्षणों द्वारा उपयोग)।
किसी सर्वर को घोषणात्मक रूप से JSON कॉन्फ़िग (देखें `test-servers/configs/*.json`) के साथ कॉन्फ़िगर करें जो presets चुनता है, फिर इसे `--config` के माध्यम से लोड करें। क्योंकि सर्वर वास्तविक subprocesses के रूप में स्पॉन होते हैं, build आउटपुट पहले मौजूद होना चाहिए:```bash
npm run test-servers:build # (from clients/web) → tsc -p test-servers, emits test-servers/build/
Vite alias @modelcontextprotocol/inspector-test-server (clients/web/vite.config.ts में) test-servers/build/index.js की ओर इंगित करता है, ताकि getTestMcpServerPath() एक वास्तविक .js पथ पर हल हो जाए।
एक streamable-HTTP सर्वर SDK के createMcpHandler के माध्यम से आधुनिक (2026-07-28) प्रोटोकॉल युग की भी सेवा कर सकता है:
transport.modern सेट करें — द्वि-युग (dual-era) स्टेटलेस सेवा के लिए true, या केवल-आधुनिक सख्त (modern-only strict) के लिए { "legacy": "reject" }।createTestServerHttp के लिए ServerConfig पर modern पास करें।यही वह चीज़ है जो protocolEra: "auto" | "modern" पर बातचीत करने वाले Inspector कनेक्शन को आधुनिक चरण (populated server/discover, sessionless) तक पहुँचने देती है। test-servers/configs/modern-http.json देखें।
नीचे दिया गया प्रत्येक कॉन्फ़िग एक विशेषता को हाथ से आज़माने के लिए तैयार सर्वर है। इसे --config के साथ लोड करें, और जब तक अन्यथा न कहा जाए, Protocol Era = Modern से कनेक्ट करें।
mcp-app-http.json अपने mcp_app_demo_widget UI संसाधन के साथ mcp_app_demo टूल (_meta.ui.resourceUri) की सेवा करता है, ताकि Apps टैब के पास रेंडर करने के लिए एक वास्तविक App हो। यह एक साधारण streamable-HTTP सर्वर है — डिफ़ॉल्ट (legacy) प्रोटोकॉल युग से कनेक्ट करें, Modern से नहीं।
Apps टैब खोलें, mcp_app_demo चुनें, इसे एक शीर्षक दें और Open App पर क्लिक करें: विजेट सैंडबॉक्स iframe के अंदर रेंडर होता है और होस्ट-पक्षीय UI प्रोटोकॉल सतह — host-context render, size-changed, ui/message, और App logs पैनल में एक लॉग पंक्ति — का अभ्यास करता है। चूँकि विजेट सैंडबॉक्स प्रॉक्सी पेज के माध्यम से परोसा जाता है, यह कॉन्फ़िग वही है जो #1859 को भी दोहराता है (एक लापता clients/web/static/sandbox_proxy.html यहाँ विजेट के स्थान पर "Sandbox not loaded" संदेश के रूप में उभरता है) — एक ऐसी विफलता जो केवल किसी स्थापित पैकेज में दिखाई दी, रेपो में कभी नहीं।
उसी प्रवाह के स्क्रिप्टेड संस्करण के लिए (--app-info जाँच → डीप लिंक → रेंडर किया गया विजेट), MCP App की समीक्षा देखें।
modern-mrtr-http.json आधुनिक चरण पर mrtr_confirm टूल (प्रीसेट mrtr_confirm, createMrtrTool) की सेवा करता है। इसका हैंडलर एक फ़ॉर्म इलिसिटेशन एम्बेड करते हुए inputRequired(...) लौटाता है, इसलिए इसे आमंत्रित करने से एक वास्तविक राउंड-ट्रिप उत्पन्न होती है: input_required → क्लाइंट एम्बेडेड इलिसिटेशन को पूरा करता है और नए id के साथ पुनः प्रयास करता है → complete।
Inspector MRTR को मैन्युअल रूप से चलाता है (inputRequired: { autoFulfill: false }), इसलिए एम्बेडेड इलिसिटेशन आपके उत्तर देने के लिए पेंडिंग-रिक्वेस्ट मोडल ("input_required" टैग किया हुआ) पर रुकता है, फिर पुनः प्रयास पूरा होता है। उस पेंडिंग-रिक्वेस्ट UX और Protocol दृश्य के MRTR वार्तालाप समूहन दोनों की जाँच करने के लिए उपयोगी है।
mrtr-showcase-http.json एक सर्वर में हर MRTR प्रीसेट को एकत्रित करता है:
mrtr_empty चलाएँ और उसकी एकमात्र इलिसिटेशन का उत्तर दें: Protocol टैब आदान-प्रदान को COMPLETE पर समाप्त होने वाली MRTR वार्तालाप के रूप में समूहित करता है, और Results पैनल "Empty result — The tool call completed successfully and returned no content." कहता है। टूटे हुए बिल्ड पर वही परिणाम "No results yet" के रूप में रेंडर होता था, जो पैनल का चलाने-से-पहले का प्लेसहोल्डर है (#1860) — इसलिए जिस कॉल को उपयोगकर्ता ने अभी सफल होते देखा था, वह ऐसी कॉल के रूप में पढ़ी जाती थी जो कभी चली ही नहीं। बिना structuredContent के एक खाली content सरणी एक वैध CallToolResult है, और पैनल केवल तभी माउंट होता है जब कोई परिणाम मौजूद हो, इसलिए प्लेसहोल्डर का शब्दांकन वहाँ सत्य नहीं हो सकता था। (उसी अंतराल का पड़ोसी आधा हिस्सा — एक ऐसा परिणाम जिसका पेलोड केवल structuredContent में रहता है — #1908 द्वारा बंद कर दिया गया था।)
विरासती
collect_elicitationप्रीसेटserver.elicitInputको कॉल करता है, जो 2026-07-28 चरण पर त्रुटि देता है — वहाँ सर्वर→क्लाइंट अनुरोधों की अनुमति नहीं है। MRTR आधुनिक प्रतिस्थापन है।
modern-network-http.json SEP-2243 / SEP-2575 को कवर करता है। यह एक get_weather टूल की सेवा करता है जिसके city तर्क में x-mcp-header: "City" एनोटेशन है, इसलिए एक आधुनिक क्लाइंट इसे Mcp-Param-City में मिरर करता है।
यह चार trigger_* टूल भी परोसता है जिन्हें आधुनिक चरण का स्पेक-त्रुटि इंजेक्टर (transport.modern.injectSpecErrors: true) वास्तविक HTTP स्थिति और JSON-RPC त्रुटि बॉडी के साथ उत्तर देता है:
मिरर किए गए Mcp-* हेडरों को हाइलाइट किया हुआ, सेंटिनल मान डीकोड किए हुए, और प्रत्येक त्रुटि को अलग ढंग से रेंडर किया हुआ देखने के लिए Network टैब खोलें।
Mcp-Param-*मिररिंग Inspector द्वारा बनाई जाती है, SDK द्वारा नहीं। SDK केवलclient.callTool()के अंदर मिरर करता है, और ब्राउज़र में इसे छोड़ देता है (detectProbeEnvironment() !== "browser")। Inspector MRTR को मैन्युअल रूप से चलाने के लिएtools/callकोclient.request()के माध्यम से रूट करता है, इसलिए यह मिरर किए गए हेडर स्वयं बनाता है (#1846) — हर क्लाइंट पर, web सहित, क्योंकि web क्लाइंट का अपस्ट्रीम अनुरोध ब्राउज़र के बजाय Node बैकएंड द्वारा जारी किया जाता है। इसलिएget_weatherweb, CLI और TUI तीनों से बुलाने योग्य है, सादे और "Run as task" दोनों रूपों में।
x-mcp-headerxmcpheader-modern-http.json ये परोसता है:
echo — सादा टूल।get_weather — इसके city तर्क पर एक मान्य x-mcp-header: "City" एनोटेशन।invalid_header_tool — हेडर नाम "Bad Header" का उपयोग करने वाला एक एनोटेशन। स्पेस इसे एक अमान्य RFC 9110 टोकन बनाता है, इसलिए पूरी टूल परिभाषा अमान्य है।trigger_invalid_params — एक वास्तविक -32602 Invalid params त्रुटि के साथ उत्तर दिया गया जिसका संदेश किसी लापता टूल के बारे में नहीं है।Tools टैब खोलें: get_weather का विवरण पैनल एक "Mirrored request headers (SEP-2243)" अनुभाग (city → Mcp-Param-City) दिखाता है, और invalid_header_tool "Excluded (SEP-2243)" विभाजक के नीचे काटा हुआ दिखाई देता है, जिसका कारण होवर पर दिखता है। एक अनुरूप Streamable HTTP क्लाइंट को इसे tools/list से हटाना ही MUST है; Inspector क्यों को उजागर करता है।
SDK v2 के अंतर्गत -32602 के साथ अस्वीकार करने वाला tools/call isError परिणाम के बजाय एक अलग त्रुटि पैनल के रूप में रेंडर होता है — जब संदेश किसी लापता टूल का नाम लेता है तो शीर्षक "Unknown Tool" होता है, अन्यथा "Invalid Parameters" (trigger_invalid_params चलाएँ)।
pagination-http.json 12 टूल, 12 संसाधन और 12 प्रॉम्प्ट (प्रीसेट numbered_tools / numbered_resources / numbered_prompts, count: 12) परोसता है, प्रत्येक का maxPageSize 4 है, इसलिए हर सूची तीन पृष्ठों में विभाजित होती है।
"Fetch Lists One Page at a Time" चालू करें (Server Settings — paginatedLists सेटिंग, या किसी सूची साइडबार में Paginated स्विच) और सूचियाँ केवल पृष्ठ 1 लोड करती हैं (4 आइटम) साथ में एक Load next page नियंत्रण और N pages loaded स्थिति। प्रत्येक क्लिक अगले 4 लाता है और उन्हें जोड़ देता है; Refresh पृष्ठ 1 पर रीसेट करता है। स्विच बंद होने पर (डिफ़ॉल्ट), वही सूचियाँ कनेक्ट होने पर तीनों पृष्ठों को स्वतः एकत्र कर लेती हैं।
structured-output-http.json list_items (नेस्टेड structuredContent — किसी ऑब्जेक्ट के अंदर सरणियों के अंदर ऑब्जेक्ट, #1908 से आकार), get_temp (एक सपाट तीन-कुंजी पेलोड) और echo (बिल्कुल कोई outputSchema नहीं) परोसता है। यह एक साधारण streamable-HTTP सर्वर है — डिफ़ॉल्ट (legacy) प्रोटोकॉल युग से कनेक्ट करें।
Tools टैब से list_items चलाएँ: परिणाम पैनल content[] टेक्स्ट सारांश ("Found 2 items.") और एक संक्षेपणीय Structured Output अनुभाग दिखाता है जो स्कीमा-सत्यापित पेलोड को सुंदर-मुद्रित, प्रतिलिपि-योग्य JSON के रूप में रेंडर करता है। v2 इसी अनुभाग को छोड़ रहा था — outputSchema घोषित करने वाला एक टूल अपना वास्तविक डेटा वहाँ लौटाता है, और टेक्स्ट ब्लॉक आमतौर पर केवल उसका सारांश देता है। यह पुष्टि करने के लिए echo चलाएँ कि जब किसी परिणाम में structuredContent नहीं होता तो यह अनुभाग अनुपस्थित रहता है।
duplicate-tool-names-http.json get_weather, get_temp, echo और add परोसता है, फिर tools/list के अंत में get_weather और echo को समान name और (duplicate) शीर्षक के साथ दोहराता है (duplicateToolNames)। कोई भी प्रीसेट यह आकार उत्पन्न नहीं कर सकता — SDK का registerTool दोहराए गए नाम को अस्वीकार करता है — लेकिन एक वास्तविक सर्वर कर सकता है और करता है, और Inspector को इसे ईमानदारी से रेंडर करना होता है।
कनेक्ट करें (डिफ़ॉल्ट legacy युग), Tools टैब खोलें, और Search tools में get टाइप करें: सूची को ठीक तीन get_* पंक्तियों तक सीमित होना चाहिए। टूटे हुए बिल्ड पर यह एक पुरानी echo पंक्ति बनाए रखता था, क्योंकि साइडबार पंक्तियों को केवल tool.name द्वारा कुंजीबद्ध करता था और टकराने वाली कुंजियों ने पुनर्मिलन के दौरान एक चाइल्ड को अनाथ कर दिया था (#1957)।
डुप्लिकेट प्रतियाँ जानबूझकर अपने जुड़वाँ के बगल में नहीं, बल्कि अंत में जोड़ी जाती हैं। React पहले समान-कुंजी वाले बच्चों की अग्रणी श्रृंखला का मिलान करता है, इसलिए शीर्ष के निकट का डुप्लिकेट संयोग से संरेखित हो जाता है और दोष छिप जाता है; जोड़े को अलग करना ही इसे देखने योग्य बनाता है — और यह यथार्थवादी आकार भी है, दो टूल स्रोत जुड़े हुए।
nullable-fields-http.json record_shipment परोसता है, जिसके चार तर्कों में से प्रत्येक को Zod के .nullish() के साथ घोषित किया गया है — "वैकल्पिक और स्पष्ट रूप से nullable"। यह anyOf: [<branch>, { "type": "null" }] में संकलित होता है, इसलिए वास्तविक प्रकार (और, enum के लिए, उसकी enum सूची) शीर्ष स्तर के बजाय एक शाखा पर बैठती है। तुलना के लिए get_temp इसके साथ एक सादा, गैर-nullable units enum लेकर बैठा है। सादा streamable-HTTP — डिफ़ॉल्ट (legacy) प्रोटोकॉल युग से कनेक्ट करें।
Tools टैब खोलें और record_shipment चुनें: direction को एक Select (envio / recebimento) के रूप में रेंडर होना चाहिए जिसमें एक साफ़ बटन हो जो इसे वापस null पर सेट करता है, reference एक टेक्स्ट इनपुट के रूप में, quantity एक संख्या इनपुट के रूप में, और express एक चेकबॉक्स के रूप में। टूटे हुए बिल्ड पर उनमें से प्रत्येक raw-JSON टेक्स्टएरिया पर गिर जाता था, जो प्रत्येक कीस्ट्रोक पर अपनी सामग्री को फिर से एस्केप करता था जब तक कि मान अनुपयोगी न हो जाए (#1928)। टूल उसे प्राप्त तर्कों को प्रतिध्वनित करता है, इसलिए परिणाम पैनल ठीक वही दिखाता है जो भेजा गया था।
TUI में भी यही अंतराल था और इसे उसी सर्वर के विरुद्ध जाँचना उचित है (--tui, फिर record_shipment का परीक्षण करें): direction एक select है, quantity एक पूर्णांक क्षेत्र है, express एक boolean है। दोनों क्लाइंट अब एक साझा संक्षेपण चरण साझा करते हैं — core/json/nullableUnion.ts में normalizeNullableUnion — ठीक इसलिए ताकि वे इस बात पर विचलित न हो सकें कि वे कौन सी स्कीमा रेंडर कर सकते हैं।
rfc6570-templates-http.json #1919 से सीधे दो संसाधन टेम्पलेट परोसता है — events_by_topic (foobar://events/{topic}) और events_by_query (foobar://events{?topic}) — प्रत्येक उस URI को प्रतिध्वनित करता है जिसके विरुद्ध उसका मिलान हुआ था, साथ ही एक सादा foobar://events संसाधन (नीचे देखें)। सादा streamable-HTTP; डिफ़ॉल्ट (legacy) प्रोटोकॉल युग से कनेक्ट करें।
Resources टैब खोलें और events_by_topic चुनें, फिर foo/bar दर्ज करें। अनुरोध foobar://events/foo%2Fbar के रूप में जाना चाहिए, और परिणाम उस URI को प्रतिध्वनित करता है जिसका सर्वर ने मिलान किया। टूटे हुए बिल्ड पर मान को कच्चा जोड़ दिया गया था, इसलिए स्लैश ने एक दूसरा पथ खंड बनाया और SDK का मैचर -32602 Resource not found: foobar://events/foo/bar का उत्तर देता था — मुद्दे में वही सटीक विफलता। यही बात ?, #, %, स्पेस और गैर-ASCII टेक्स्ट पर भी लागू होती है।
events_by_query वह आधा भाग है जो अदृश्य था: पुराना /\{(\w+)\}/g स्कैन किसी ऑपरेटर वाले एक्सप्रेशन को देख ही नहीं सकता था, इसलिए कोई topic इनपुट बिल्कुल रेंडर नहीं होता था। अब यह दिखाई देता है, जिसे Optional चिह्नित किया गया है — RFC 6570 पूरे एक्सप्रेशन को हटा देता है जब वेरिएबल undefined होता है, इसलिए फ़ील्ड को खाली छोड़कर पढ़ने पर foobar://events का अनुरोध होता है, और इसे भरने पर foobar://events?topic=foo%2Fbar का अनुरोध होता है। शीर्षक के बगल में URI पूर्वावलोकन टाइप करते समय आंशिक रूप से विस्तारित रूप दिखाता है, अधूरे एक्सप्रेशनों को ज्यों-का-त्यों छोड़ देता है।
सादा
foobar://eventsसंसाधन जानबूझकर पंजीकृत किया गया है, भराव के रूप में नहीं। SDK काUriTemplate.match(){?topic}को एक आवश्यक\?topic=([^&]+)में संकलित करता है, इसलिए अकेला टेम्पलेट खाली पठन की सेवा नहीं कर सकता —match("foobar://events")nullलौटाता है। एक वास्तविक सर्वर असंसाधित संग्रह को अपने स्वयं के संसाधन के रूप में उजागर करता है; शोकेस भी ऐसा ही करता है ताकि वह चरण वास्तव में हल हो सके।
web क्लाइंट और TUI एक साझा हेल्पर, core/mcp/uriTemplate.ts के माध्यम से विस्तार करते हैं — web Resources फ़ॉर्म सीधे, TUI InspectorClient.readResourceFromTemplate के माध्यम से — और दोनों अपने फ़ॉर्म फ़ील्ड भी इसके पार्सर से प्राप्त करते हैं, जो वह आधा हिस्सा है जो साझाकरण को वास्तविक बनाता है: एक फ़ॉर्म मानों को उन नामों के अंतर्गत जमा करता है जिन्हें उसने रेंडर किया, इसलिए एक पार्सर जो नाम को बिगाड़ता है, विस्तार के समय मान को चुपचाप गिरा देता है। (CLI उपभोक्ता नहीं है: इसके पास कोई टेम्पलेट फ़ॉर्म नहीं है, और इसका resources/read पहले से विस्तारित --uri को सीधे पार कर देता है।)
SDK का UriTemplate अभी भी उपयोग किया जाता है, लेकिन केवल किसी टेम्पलेट को मान्य करने के लिए (इसे निर्मित करना ही वह चीज़ है जो एक बंद-रहित एक्सप्रेशन को अस्वीकार करती है)। इसका विस्तारक नहीं, क्योंकि यह पाँच तरीकों से अधूरा है — प्रत्येक को pinned SDK के विरुद्ध मापा गया है, अनुमानित नहीं:
; और :3 पंक्तियाँ वे हैं जिन्हें उपयोगकर्ता सीधे देखता है: SDK के पार्स पर फ़ॉर्म शाब्दिक रूप से ;id और id:3 लेबल वाले फ़ील्ड रेंडर करता है। +/# पंक्ति अति-एस्केपिंग के बजाय मूक भ्रष्टाचार है — एक IPv6 लिटरल या पहले से एन्कोडेड पथ सर्वर तक परिवर्तित होकर पहुँचता है।
एक ऐसा टेम्पलेट जिसे बिल्कुल विस्तारित नहीं किया जा सकता — एक व्याकरण-बाह्य संशोधक ({id:abc}), या कोई ऐसा एक्सप्रेशन जो कोई वेरिएबल घोषित नहीं करता ({}, {a,}, {?}) — कुछ भेजने के बजाय पठन को रोक देता है। इसे देखने के लिए events_malformed (foobar://events/{topic:abc}) चुनें: Read Resource अक्षम है, कारण फ़ॉर्म के नीचे मुद्रित होता है, और पूर्वावलोकन टेम्पलेट को वैसे ही दिखाता है जैसे सर्वर ने घोषित किया। विकल्प जितना दिखता है उससे बदतर है: x://{} अन्यथा कोई इनपुट रेंडर किए बिना x:// में विस्तारित होगा, इसलिए फ़ॉर्म की "सभी आवश्यक चीज़ें भरी हुई हैं" जाँच स्वतः ही सत्य हो जाती है और यह एक ऐसा URI पढ़ता है जो सर्वर द्वारा प्रकाशित टेम्पलेट नहीं है।
लिटरल भी विस्तार पर pct-एन्कोडेड होते हैं (RFC 6570 §3.1): café/{var} पथ में कच्चा UTF-8 नहीं, बल्कि caf%C3%A9/value भेजता है — ऐसी चीज़ जो SDK का विस्तारक भी नहीं करता। और किसी टेम्पलेट द्वारा उपयोग किए जा सकने वाले नाम RFC 6570 के varchar हैं, साथ ही - और ~ के लिए एक लेबलित सहनशीलता है: अनुरूपता सुइट {default-graph-uri} को अस्वीकार करता है, लेकिन वास्तविक सर्वर ऐसे नाम प्रकाशित करते हैं और SDK का मैचर उन्हें राउंड-ट्रिप करता है, इसलिए Inspector उन्हें विस्तारित करता है और वेरिएबल को conforming: false चिह्नित करता है, बजाय किसी ऐसे संसाधन को अस्वीकार करने के जो प्रदर्शनतः काम करता है।
एक undefined वेरिएबल ही वह चीज़ है जो अपने एक्सप्रेशन को छोड़ देता है — खाली स्ट्रिंग के रूप में परिभाषित एक वेरिएबल विस्तारित होता है (x{?q} से x?q= मिलता है, x{;q} से x;q मिलता है, RFC 6570 §3.2.7 के अनुसार)। विस्तारक उस भेद का सम्मान करता है, इसलिए readResourceFromTemplate जैसा कॉलर दोनों में से किसी भी URI का अनुरोध कर सकता है। दोनों को एक में मिलाना एक फ़ॉर्म चिंता है, टेम्पलेट की नहीं: दोनों क्लाइंट हर घोषित वेरिएबल को "" के साथ सीड करते हैं और एक टेक्स्ट इनपुट "परिभाषित लेकिन खाली" व्यक्त नहीं कर सकता, इसलिए प्रत्येक फ़ॉर्म अपनी खाली जगहों (definedValues) को अंदर आते समय हटा देता है।
आवश्यकता (Requiredness) एक्सप्रेशन का गुण है, वेरिएबल का नहीं: RFC 6570 एक बहु-नाम एक्सप्रेशन से undefined नामों को हटा देता है, इसलिए केवल a भरा हुआ {a,b} विस्तारणीय है और एक फ़ॉर्म को इसे अवरुद्ध नहीं करना चाहिए। requiredGroups प्रत्येक गैर-छोड़ने-योग्य एक्सप्रेशन के लिए एक प्रविष्टि लौटाता है और hasRequiredValues पूछता है कि उनमें से प्रत्येक उसके किसी भी एक नाम से संतुष्ट हो — जिसे कोई भी प्रति-वेरिएबल फ़्लैग व्यक्त नहीं कर सकता, एक बार जब कोई नाम एक्सप्रेशनों में दोहराया जाता है ({a,b}{a,c} b और c भरने से संतुष्ट होता है)।#### विज्ञापित एक्सटेंशन
advertised-extensions-http.json एक echo (हमेशा) और एक get_weather टूल io.modelcontextprotocol/tasks एक्सटेंशन पर निर्भर (extensionGatedTools) के साथ सेवा करता है: टूल पंजीकृत है लेकिन प्रारंभ में अक्षम है, और सर्वर इसे notifications/initialized पर तभी सक्षम करता है जब क्लाइंट ने उस एक्सटेंशन को अपने capabilities.extensions में घोषित किया हो।
echo और get_weather दोनों दिखाती है।get_weather को कभी सक्षम नहीं करता, और Tools सूची केवल echo दिखाती है।यह उस सर्वर के लिए डिबगिंग नॉब है जो क्लाइंट द्वारा विज्ञापित चीज़ों के आधार पर टूल पंजीकरण को वैध रूप से बदलता है। केवल लिगेसी स्टेटफुल लेग — आधुनिक प्रति-अनुरोध लेग में कोई स्थायी oninitialized नहीं होता।
logging-legacy-http.json और logging-modern-http.json दोनों logging: true के साथ-साथ एक send_notification टूल प्रदान करते हैं जो चुने गए स्तर पर notifications/message उत्सर्जित करता है। लिगेसी वाला एक साधारण streamable-HTTP सर्वर है; आधुनिक वाला transport.modern: true सेट करता है।
send_notification को कॉल करने पर लॉग पैनल में स्ट्रीम होता है।_meta["io.modelcontextprotocol/logLevel"] स्टैम्प करता है (Network टैब के अनुरोध बॉडी में जाँचें)। send_notification को कॉल करने पर लॉग अनुरोध के SSE प्रतिक्रिया पर स्ट्रीम होता है। इसे वापस Off पर सेट करें और वही कॉल चुपचाप रोक दी जाती है — अनुरोध logLevel कुंजी को छोड़ देता है, इसलिए लॉग कभी नहीं आता।वह गेटिंग स्पेक के अनुरूप है ("एक सर्वर को उस अनुरोध के लिए notifications/message उत्सर्जित नहीं करना चाहिए जिसने ऑप्ट-इन नहीं किया") क्योंकि send_notification SDK के अनुरोध-स्कोप्ड, थ्रेशोल्ड-जागरूक extra.log (ctx.mcpReq.log) के माध्यम से उत्सर्जित होता है। आधुनिक लेग पर यह अनुरोध लिफाफे से प्रति-अनुरोध logLevel ऑप्ट-इन पढ़ता है और संदेश को तब छोड़ देता है जब क्लाइंट ने ऑप्ट-इन नहीं किया हो या स्तर अनुरोधित गंभीरता से नीचे हो; लिगेसी पर यह logging/setLevel से सत्र स्तर का सम्मान करता है। क्योंकि यह अनुरोध के notify के माध्यम से उत्सर्जित होता है, आधुनिक प्रतिक्रिया SSE में अपग्रेड होती है और लॉग मूल अनुरोध की स्ट्रीम पर सवार होता है।
subscriptions-legacy-http.json और subscriptions-modern-http.json दोनों subscriptions: true के साथ तीन numbered_resources प्रदान करते हैं। लिगेसी वाला साथ ही एक update_resource टूल भी प्रदान करता है; आधुनिक वाला transport.modern: true सेट करता है।
resources/subscribe भेजता है और Subscriptions अनुभाग URI को बिना किसी स्ट्रीम क्रोम के सूचीबद्ध करता है। उस URI के साथ update_resource को कॉल करें और सर्वर सामग्री अपडेट करता है और notifications/resources/updated उत्सर्जित करता है, जिससे सब्सक्राइब्ड टाइल का अंतिम-अपडेट समय अंकित होता है।subscriptions/listen भेजता है (इसका फ़िल्टर resourceSubscriptions और resourcesListChanged ऑप्ट-इन दोनों ले जाता है) और notifications/subscriptions/acknowledged पर पूर्ण होता है। Subscriptions अनुभाग तब अपने हेडर में एक स्ट्रीम-स्थिति बैज (Connecting… → Listening) दिखाता है, और यदि लंबे समय तक चलने वाली स्ट्रीम गिर जाती है तो पुनः सूचीबद्ध करके फिर से कनेक्ट होता है।आधुनिक कॉन्फ़िगरेशन जानबूझकर update_resource को छोड़ देता है। SDK का आधुनिक लेग स्टेटलेस/प्रति-अनुरोध है (createMcpHandler(() => createMcpServer(config))), इसलिए टूल एक फेंक-दिए जाने वाले सर्वर इंस्टेंस के विरुद्ध चलेगा — सामग्री परिवर्तन अगले resources/read के लिए स्थायी नहीं रहेगा, और उसका resources/updated अलग लिसन स्ट्रीम तक नहीं पहुँचेगा। यह उपयोगी से अधिक भ्रामक है।
इसलिए लाइव अपडेट-अधिसूचना राउंड-ट्रिप लिगेसी (स्टेटफुल-सत्र) सर्वर पर प्रदर्शित की जाती है, और आधुनिक सर्वर subscribe/listen/badge व्यवहार के लिए है। Inspector का receive पथ युग-पारदर्शी है, इसलिए एक वास्तविक स्टेटफुल आधुनिक सर्वर जो resources/updated को लिसन स्ट्रीम पर रूट करता है, सब्सक्राइब्ड टाइल को उसी तरह चलाता है।
Legacy (tasks-legacy-http.json) capabilities.tasks (tasks: { list, cancel }) का विज्ञापन करता है, साथ में simple_task / progress_task / elicitation_task प्रीसेट। उनमें से किसी एक टूल को Run as task चालू करके चलाएँ, और Tasks टैब इसे सूचीबद्ध करता है (tasks/list के माध्यम से भरा जाता है), tasks/get को पोल करता है, ब्लॉकिंग tasks/result के साथ पेलोड प्राप्त करता है, और tasks/cancel के साथ रद्द करता है।
Modern (tasks-modern-http.json) transport.modern: true और tasksExtension: true सेट करता है, जो io.modelcontextprotocol/tasks एक्सटेंशन (SEP-2663) का विज्ञापन करता है और modern_task / modern_input_task प्रदान करता है। Tasks टैब capabilities.tasks पर नहीं, बल्कि बातचीत किए गए एक्सटेंशन पर निर्भर है।
modern_task को एक कार्य के रूप में चलाएँ — tools/call एक CreateTaskResult (resultType: "task", Protocol/Network टैब में दिखाई देता है) लौटाता है, क्लाइंट tasks/get को पोल करता है (tasks/list नहीं), और पूर्ण हुआ कार्य अपना परिणाम इनलाइन करता है (कोई ब्लॉकिंग tasks/result नहीं)।modern_input_task चलाएँ — कार्य input_required में चला जाता है, जो लंबित-अनुरोध मोडल के माध्यम से एक एम्बेडेड इलिसिटेशन सतह पर लाता है। उत्तर देने पर tasks/update को inputResponses के साथ भेजा जाता है, और अगला पोल पूर्ण होता है।SDK v2 ने सभी tasks समर्थन हटा दिया और दोनों ओर tasks/* स्पेक विधियों को आधुनिक युग से बाहर युग-गेट करता है। इसलिए Inspector स्वयं एक्सटेंशन चलाता है — resultType: "task" फ्रेम को ट्रांसपोर्ट पर हैंडल ले जाने वाले CallToolResult में पुनः लिखा जाता है, और tasks/get / update / cancel पूर्ण आधुनिक एनवेलप के साथ रॉ-वायर अनुरोध चैनल पर सवार होते हैं। टेस्ट सर्वर tasks/* को SDK हैंडलर से पहले एक Express इंटरसेप्टर से सर्व करता है, क्योंकि SDK का आधुनिक लेग उन्हें -32601 उत्तर देगा।
Tasks टैब का Refresh क्लाइंट को पहले से ज्ञात हैंडल्स को फिर से पोल करता है — आधुनिक में कोई सर्वर-साइड कार्य सूची नहीं होती।
npm run build # builds all clients: web → cli → tui → launcher
व्यक्तिगत क्लाइंट: `build:web`, `build:cli`, `build:tui`, `build:launcher`। वेब बिल्ड ब्राउज़र SPA (`clients/web/dist`, Vite) और Node prod-server रनर (`clients/web/build`, tsup) दोनों का निर्माण करता है।
## परीक्षण और गुणवत्ता गेट
प्रत्येक क्लाइंट अपने स्वयं के फ़ोल्डर से स्व-मान्य करता है; रूट स्क्रिप्ट्स उन्हें श्रृंखलाबद्ध करती हैं। कोई **समग्र** रूट `test` स्क्रिप्ट नहीं है — `validate` (तेज़) या `coverage` (गेट) का उपयोग करें।
| स्क्रिप्ट | यह क्या करता है |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `npm run validate` | पहले तीन स्थायी गार्ड चलाता है — `verify:format-coverage` (हर ट्रैक की गई सोर्स फ़ाइल format-गेटेड है), `verify:typecheck-coverage` (हर एक किसी tsconfig प्रोजेक्ट में आती है), `verify:dep-lockstep` (कोई डिपेंडेंसी दो इंस्टॉल से एक `tsc` प्रोग्राम तक पहुँचकर उनके बीच skewed नहीं होती) — फिर `test:scripts` (गार्डों के स्वयं के parser यूनिट टेस्ट), फिर `validate:core` (साझा `core/` `format:check` + `lint` गेट), फिर प्रति क्लाइंट: `format:check` + `lint` + **`typecheck`** (cli/tui/launcher; वेब अपने `build` के अंदर `tsc -b` के ज़रिए typecheck करता है) + `build` + तेज़ यूनिट टेस्ट। त्वरित inner-loop जाँच। |
| `npm run coverage` | v8 इंस्ट्रूमेंटेशन के अंतर्गत प्रति क्लाइंट **प्रति-फ़ाइल ≥90% गेट** (lines/statements/functions/branches)। CI-लागू। वेब के लिए यह इंटीग्रेशन प्रोजेक्ट भी चलाता है और साझा `core/` रनटाइम (जिसमें `core/json` और `core/client` शामिल हैं) को कवर करता है। |
| `npm run smoke` | निर्मित लॉन्चर (`--help` डिस्पैच + prod cli/tui/web) के माध्यम से end-to-end स्मोक, साथ ही दो headless-Chromium स्मोक: एक बूट स्मोक जो prod वेब बंडल चलाता है और एक साफ पहले रेंडर की पुष्टि करता है (कोई अनकॉट एरर नहीं — sync exception या unhandled rejection, यह कि एक Node built-in ब्राउज़र बंडल तक पहुँचने पर कैसे प्रकट होता है), और एक **MCP Apps** स्मोक (`smoke:web:app`) जो एक composable App सर्वर के विरुद्ध connect → open app → `data-app-status="ready"` को ड्राइव करता है, सैंडबॉक्स प्रॉक्सी और UI-प्रोटोकॉल ब्रिज को कवर करते हुए। |
| `npm run verify:build-gate` | ब्राउज़र ग्राफ़ में एक Node built-in ज़बरदस्ती डालकर एक वास्तविक `vite build` चलाता है और #1769 गेट के ज़रिए दावा करता है कि बिल्ड **विफल** होता है (जो Vite की browser-externalization चेतावनी को एक हार्ड एरर में बदल देता है)। Vite बंप में चेतावनी की शब्दावली बहकर गेट को चुपचाप निष्क्रिय कर देने से सुरक्षा करता है। `npm run ci` का हिस्सा। |
| `npm run verify:format-coverage` | हर `package.json` से `format:check` globs को पार्स करता है (केवल वे जो `validate` से पहुँच योग्य हैं), सभी ट्रैक की गई सोर्स फ़ाइलों की गणना करता है, और किसी भी ऐसी फ़ाइल को सूचीबद्ध करते हुए **विफल** होता है जो किसी glob से कवर नहीं होती — “हर first-party सोर्स फ़ाइल format-गेटेड है” invariant (#1792) के लिए स्थायी गार्ड। `validate` में सबसे पहले चलता है। |
| `npm run test:scripts` | गार्डों के स्वयं के शुद्ध पार्सर (`scripts/lib/npm-scripts.mjs`, `scripts/lib/tsc-program.mjs` + `verify-typecheck-coverage.mjs` और `verify-dep-lockstep.mjs` के exported हेल्पर) के लिए टेबल-संचालित यूनिट टेस्ट (`node --test`), उनके द्वारा एन्कोड किए गए प्रत्येक नियम के लिए एक केस, साथ ही `scripts/lib/resolve-node-bin.test.mjs` — क्रॉस-प्लेटफ़ॉर्म bin रिज़ॉल्वर (#1939), उन पैकेजों के वास्तविक `bin`/`exports` आकारों के विरुद्ध पिन किया गया जिन्हें स्क्रिप्ट्स वास्तव में स्पॉन करती हैं। `validate` में चलता है — और `verify:typecheck-coverage` बदले में *इस* गेट की रक्षा करता है (`validate` से पहुँच योग्य, गैर-खाली टेस्ट सेट, हर टेस्ट फ़ाइल `test:scripts` glob द्वारा मिलान की गई), क्योंकि `node --test` चुपचाप उस फ़ाइल को छोड़ देता है जिसे उसका glob मिस करता है और फिर भी 0 से बाहर निकलता है। |
| `npm run verify:typecheck-coverage` | उपरोक्त का typecheck-coverage एनालॉग (#1791): प्रत्येक Node क्लाइंट के लिए (डिस्क से स्वतः खोजा गया — अपने `typecheck` स्क्रिप्ट के प्रोजेक्ट्स के ज़रिए नामांकित, या `clients/web` जैसे `tsc -b` क्लाइंट के लिए उसके `tsconfig.json` `references` के ज़रिए) वह उन प्रोजेक्ट्स को `tsc --listFilesOnly` के साथ चलाता है, उन्हें एकत्रित करता है, और क्लाइंट के अंतर्गत किसी भी ट्रैक किए गए `.ts`/`.tsx`/`.mts`/`.cts` को सूचीबद्ध करते हुए **विफल** होता है जो किसी प्रोजेक्ट में नहीं आता (ताकि कोई नया टॉप-लेवल कॉन्फ़िग/हेल्पर चुपचाप untypechecked न जा सके)। यह deny-by-default रूप से यह भी आवश्यक करता है कि जो first-party TS किसी क्लाइंट की नहीं है (`test-servers/src`, रूट `vitest.shared.mts`, सारा `core/`, और कोई भी नया टॉप-लेवल स्थान) किसी क्लाइंट प्रोजेक्ट के tsc पास में आए — ताकि `core` का ऐसा `*.tsx` जो वेब के प्रोजेक्ट्स तक नहीं पहुँचता, वह भी पकड़ा जाए। यह यह भी दावा करता है कि गेट वायर्ड है (प्रत्येक क्लाइंट का typecheck पास — उसकी `typecheck` स्क्रिप्ट, या वेब का `tsc -b` — उसके `validate` से पहुँच योग्य है, और रूट श्रृंखला प्रत्येक क्लाइंट का `validate` चलाती है)। `validate` में चलता है। |
| `npm run verify:dep-lockstep` | “प्रति इंस्टॉल-क्रॉसिंग डिपेंडेंसी एक संस्करण” invariant (#1896) की रक्षा करता है। v2 एक वर्कस्पेस नहीं है, इसलिए क्लाइंट का टेस्ट प्रोजेक्ट साझा first-party TypeScript — `core/`, `test-servers/src`, और रूट-स्वामित्व वाला `vitest.shared.mts`, जिनमें से सभी अपनी डिपेंडेंसी **रूट** इंस्टॉल से हल करते हैं — को क्लाइंट के अपने स्रोतों के साथ संकलित करता है, उसी पैकेज को एक `tsc` प्रोग्राम में दो बार डालता है। समान संस्करण पर यह हानिरहित है; skewed होने पर, TypeScript को हर प्रकार की दो संरचनात्मक रूप से भिन्न प्रतियों को संबंधित करना पड़ता है, जो recursive-generic सतह के लिए exponential है (zod `4.3.6` बनाम `4.4.3` ने `clients/web` में 4GB tsc हीप समाप्त कर दी)। अपना उम्मीदवार सेट **इससे प्राप्त करता है कि वास्तव में प्रत्येक प्रोग्राम में क्या प्रवेश करता है** (#1965) — साझा `scripts/lib/tsc-program.mjs` के ज़रिए `tsc --listFilesOnly` के साथ सूचीबद्ध हर क्लाइंट tsconfig प्रोजेक्ट, प्रत्येक हल की गई `node_modules` फ़ाइल उसके स्वामित्व वाले इंस्टॉल में मैप की गई, उन पैकेजों को रखते हुए जो दो इंस्टॉल से एक प्रोग्राम तक पहुँचते हैं (एक पैकेज जिसकी घोषणाएँ केवल किसी अन्य पैकेज के `.d.ts` के माध्यम से आती हैं, जैसे `@modelcontextprotocol/sdk` की, first-party imports की स्कैन के लिए अदृश्य है)। प्रत्येक प्रति की कीमत उस सटीक इंस्टॉल पथ के लिए lockfile प्रविष्टि से लगाता है जिसे प्रोग्राम ने हल किया, केवल उन इंस्टॉल्स की तुलना करता है जो एक प्रोग्राम में मिले थे, और एनोटेटेड `TOLERATED_SKEW` allowlist — आज खाली — में न होने वाली किसी भी असहमति पर **deny-by-default विफल** होता है, जिसमें allowlisted पैकेज केवल *एक major संस्करण के भीतर* सहन किया जाता है। `validate` में चलता है।
| `npm run ci` | **अनिवार्य pre-push कमांड।** `validate` → `coverage` → `verify:build-gate` → `smoke` → Storybook। GitHub CI का सच्चा सुपरसेट। |
| `npm run pack:verify` | प्रकाशन स्मोक — [प्रकाशन](#publishing) देखें। |
प्रति-क्लाइंट स्क्रिप्ट्स भी मौजूद हैं (`validate:web`, `coverage:cli`, `smoke:tui`, …), साथ ही साझा `core/` पैकेज के लिए रूट `validate:core` / `format:core`, रूट `scripts/` टूलिंग के लिए `format:scripts`, और रूट "shared" सतह (`test-servers/src/**`, `vitest.shared.mts`, रूट `eslint.config.js`) के लिए `format:shared` / `lint:shared`। कमिट करने से पहले `npm run format` चलाएँ — रूट `format` `core/`, रूट `scripts/`, साझा सतह, और हर क्लाइंट को ठीक करता है; `validate` गैर-सुधारात्मक `format:check` चलाता है और किसी भी unformatted फ़ाइल पर CI विफल करता है।
**लिंटिंग type-जागरूक है।** सभी पाँच ESLint स्कोप्स (`clients/{web,cli,tui,launcher}` के साथ रूट `core/` + साझा गेट) `@typescript-eslint/no-floating-promises` को `error` पर सक्षम करते हैं, इसलिए ऐसा promise जो न awaited है, न return किया गया है, न `.catch(…)` से समाप्त किया गया है, और न ही `void` के साथ स्पष्ट रूप से छोड़ा गया है, `lint` में विफल होता है — और इसलिए `validate` में भी ([#1959](https://github.com/modelcontextprotocol/inspector/issues/1959))। नियम को type जानकारी चाहिए, इसलिए प्रत्येक स्कोप का कॉन्फ़िग एक parser प्रोजेक्ट का नाम देता है; रूट स्कोप का है **`tsconfig.lint.json`**, एक lint-केवल प्रोजेक्ट जो `core/**`, `test-servers/src/**`, और `vitest.shared.mts` को कवर करता है, जिनके पास अपना कोई tsconfig नहीं है। यह कुछ भी उत्सर्जित नहीं करता और कोई typecheck नहीं बदलता — लेकिन रूट lint स्कोप में जोड़ा गया एक नया first-party TS स्थान उसके `include` में जोड़ा जाना चाहिए। देखें **TypeScript निर्देश** [`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md) में कि `void` कब स्वीकार्य है।
पूर्ण परीक्षण नियमों के लिए — ≥90% प्रति-फ़ाइल गेट, टेस्ट फ़ाइलें कहाँ रहती हैं, unit बनाम integration बनाम storybook प्रोजेक्ट्स, और `v8 ignore` नीति — देखें [`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md)।
## प्रकाशन
रूट `@modelcontextprotocol/inspector` पैकेज **एकल संस्करण संख्या वाले एक tarball** के रूप में जारी होता है — कोई अलग `-web` / `-cli` / `-tui` / `-core` पैकेज नहीं। `npm run build` हर क्लाइंट बनाता है, फिर `npm publish` से पहले `prepack` चलता है। रनटाइम डिपेंडेंसी रूट `package.json` पर घोषित की जाती हैं; क्लाइंट बिल्ड `@inspector/core` को बंडल करते हैं और रूट इंस्टॉल से हल किए गए npm पैकेजों को externalize करते हैं।
### क्या जारी होता है, और पैकेजिंग इनवेरिएंट्स
रूट `package.json` की `"files"` allowlist tarball के लिए स्रोत-सत्य है। कुछ गैर-स्पष्ट प्रविष्टियाँ मौजूद हैं क्योंकि वे **रनटाइम पर** पढ़ी जाती हैं या npm की packlist द्वारा चुपचाप हटा दी गई थीं — `npm run pack:verify` दोबारा चलाए बिना उन्हें न हटाएँ:
- **कोई source maps नहीं।** क्लाइंट बंडलर `sourcemap: false` सेट करते हैं (`clients/{cli,tui}/tsup.config.ts`, `clients/web/tsup.runner.config.ts`); Vite और लॉन्चर का `tsc` पहले से कोई उत्सर्जित नहीं करते। Maps अनपैक किए गए आकार का ~आधा होते हैं और रनटाइम पर आवश्यक नहीं हैं — सोर्स पर `npm run dev` के माध्यम से डीबग करें।
- **`clients/web/build` `clients/web/.npmignore` के ज़रिए जारी होता है।** `clients/web/.gitignore` `build/` को सूचीबद्ध करता है, और npm की packlist रूट `"files"` allowlist के ऊपर उस नेस्टेड `.gitignore` का सम्मान करती है — इसलिए prod web-server रनर चुपचाप tarball से गायब था जबकि `clients/web/dist` आ गया (उसका `.gitignore` केवल `dist-ssr` सूचीबद्ध करता है)। `clients/web/.npmignore` प्रकाशन के लिए `.gitignore` को ओवरराइड करता है ताकि `build/` (रनर) और `dist/` (SPA) दोनों जारी हों। अन्य क्लाइंट्स को इसकी आवश्यकता नहीं है — कोई भी नेस्टेड `.gitignore` जारी नहीं करता।
- **`clients/web/static` MCP Apps सैंडबॉक्स प्रॉक्सी जारी करता है।** `clients/web/static/sandbox_proxy.html` एक committed सोर्स फ़ाइल है (बिल्ड आर्टिफैक्ट नहीं), जिसे रनटाइम पर `clients/web/server/sandbox-controller.ts` द्वारा `<runner dir>/../static/sandbox_proxy.html` के रूप में डिस्क से पढ़ा जाता है। यह रूट `"files"` allowlist से पूरी तरह गायब था, इसलिए हर प्रकाशित बिल्ड Apps टैब में **"Sandbox not loaded"** ([#1859](https://github.com/modelcontextprotocol/inspector/issues/1859)) के साथ विफल हुआ जबकि रिपो में ठीक काम करता था। क्योंकि पथ `clients/web/build` के _सापेक्ष_ हल किया जाता है, निर्देशिका को उसी सटीक स्थान पर जारी होना चाहिए — `pack:verify` tarball प्रविष्टि और डिस्क-पर-स्थापित पथ दोनों की पुष्टि करता है।
- **React रेंडर करने वाली डिपेंडेंसी बंडल की जाती है, externalize नहीं।** एक externalized पैकेज अपना `react` उस स्थान से हल करता है जहाँ npm ने उसे उपभोक्ता के ट्री में रखा है, जो ज़रूरी नहीं कि वही हो जहाँ बंडल हमारा हल करता है — npm एक पैकेज को ऐसे React के बगल में रखता है जो *उसके* peer range को संतुष्ट करता है, और वे ranges हमारे से अधिक ढीले होते हैं। `ink-form` और `ink-scroll-view` `">=18"` घोषित करते हैं, इसलिए React 18 रखने वाला प्रोजेक्ट उन्हें संतुष्ट करता है और उन्हें hoisted कर लेता है जबकि Inspector का React 19 नीचे nest हो जाता है: React की दो प्रतियाँ, और जिस क्षण कोई tool test फ़ॉर्म या scroll view mount होता है TUI `TypeError: Cannot read properties of null (reading 'useState')` के साथ मर जाता है ([#1952](https://github.com/modelcontextprotocol/inspector/issues/1952))। इसलिए दोनों को `clients/tui/tsup.config.ts` द्वारा inline किया जाता है और वे **नहीं** रूट डिपेंडेंसी हैं: tarball उनका कोड `clients/tui/build/index.js` के अंदर जारी करता है बजाय उपभोक्ताओं द्वारा उन्हें इंस्टॉल करने के। बंडलिंग उनकी transitive डिपेंडेंसी को इस रिपो के इंस्टॉल द्वारा हल की गई चीज़ों पर भी पिन करती है (विशेष रूप से `overrides` के ज़रिए `ink-select-input@6`, जिसे npm डिपेंडेंसी के रूप में स्थापित पैकेज के लिए अनदेखा करता है)। **`ink` एकमात्र अपवाद है, लागत के आधार पर:** इसे बंडल करना काम करता है लेकिन ~1.4 MB जोड़ता है (`react-reconciler` और `yoga-layout` साथ आते हैं, साथ ही inline CJS के लिए एक `createRequire` बैनर), इसलिए यह external रहता है — *नहीं* क्योंकि इसका `">=19"` peer इसे सुरक्षित बनाता है, जो यह नहीं करता। उसे सहनीय बनाए रखता है रूट `react` range: `"^19.0.0"` जानबूझकर पूरे major के लिए खुला है ताकि npm हमारे React को किसी भी React 19 के साथ dedupe कर सके जिसे उपभोक्ता पिन करता है, जिससे external `ink` उसी प्रति पर रहता है जिसका उपयोग बंडल करता है। **उस range को संकीर्ण करना renderer के लिए बग को फिर से खोल देता है** — `clients/tui/__tests__/tsupConfig.test.ts` इसे `ink` की peer floor पर पिन करता है, और बाकी विभाजन की रक्षा करता है; देखें [TUI README](https://github.com/modelcontextprotocol/inspector/blob/HEAD/clients/tui/README.md#bundling-react-rendering-dependencies-must-be-inlined-1952)।
- **एकल संस्करण संख्या, रूट `package.json` से पढ़ी जाती है।** Inspector एक पैकेज के रूप में एक संस्करण के साथ जारी होता है, इसलिए केवल **रूट** `package.json` एक `version` रखता है — चारों `clients/*/package.json` में जानबूझकर कोई नहीं है। हर Node क्लाइंट (CLI, TUI, और वेब बैकएंड) संस्करण को `core/node/version.ts` में साझा `readInspectorVersion()` रीडर के माध्यम से हल करता है, जो रूट मैनिफेस्ट (tarball में हमेशा मौजूद) तक चलता है। कोई भी क्लाइंट `package.json` रनटाइम पर नहीं पढ़ा जाता, इसलिए किसी को जारी होने की आवश्यकता नहीं है। वेब **ब्राउज़र** फ़ाइल सिस्टम नहीं पढ़ सकता; वह अपना संस्करण बैकएंड से `GET /api/config` के ज़रिए प्राप्त करता है (देखें [#1639](https://github.com/modelcontextprotocol/inspector/issues/1639))।
### `npm run pack:verify` — वास्तविक tarball के विरुद्ध publish स्मोक`smoke:*` स्क्रिप्ट्स रेपो-आंतरिक बिल्ड ट्री के विरुद्ध चलती हैं, जो **प्रकाशित पैकेज नहीं** है। `npm run pack:verify` (`scripts/pack-and-verify.mjs`) उस अंतर को बंद करता है: यह बिल्ड करता है, प्रकाशन योग्य टारबॉल को `npm pack` करता है (यह सुनिश्चित करते हुए कि कोई सोर्स मैप शिप न हो और रनटाइम-आवश्यक फ़ाइलें मौजूद हों), टारबॉल को एक **स्वच्छ डिस्पोज़ेबल कंज़्यूमर** में स्थापित करता है — एक नई अस्थायी निर्देशिका जहाँ यह एक वास्तविक `npm install <tgz>` चलाता है (रनटाइम डिप्स खींचता है, `postinstall` चलाता है), बिल्कुल वैसे जैसे `npx @modelcontextprotocol/inspector` करता है — और स्थापित `mcp-inspector` बिन को एंड-टू-एंड चलाता है: `--help` डिस्पैच, stdio पर एक वास्तविक `--cli tools/list`, और एक प्रोड `--web` बूट जिसे शिप किए गए `dist` से `/` सर्व करना होता है। यह "works in `--dev`, breaks under `npx …`" पाथ/पैकेजिंग विफलताओं को पकड़ता है। इसे नेटवर्क एक्सेस की आवश्यकता होती है (इंस्टॉल डिप्स खींचता है), इसलिए यह एक स्थानीय / रिलीज़ जाँच है, **न कि** तेज़ `validate`/`ci` लूप का हिस्सा।
### रिलीज़ जारी करना
प्रकाशन दो रिलीज़-गेटेड जॉब्स द्वारा स्वचालित किया जाता है [`.github/workflows/main.yml`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/.github/workflows/main.yml) में (`github.event_name == 'release'`, दोनों `needs: build`):
- **`publish`** — npm पैकेज। प्री-पब्लिश गेट के रूप में `npm run pack:verify` चलाता है, यह सुनिश्चित करता है कि रिलीज़ टैग रूट `package.json` संस्करण से मेल खाता है, फिर `npm publish --access public --provenance` — एक एकल `npm publish` (v2 कोई npm वर्कस्पेस नहीं है, इसलिए कोई v1-शैली `publish-all`/`--workspaces` नहीं है), GitHub OIDC के माध्यम से हस्ताक्षरित प्रोवेनेंस अटेस्टेशन के साथ (`id-token: write`, `environment: release`, `NPM_TOKEN`)।
- **`publish-github-container-registry`** — कंटेनर इमेज ([Docker](#docker) देखें)।
एक v2 रिलीज़ **`main`** से बनाई जाती है, जब माइलस्टोन का काम `v2/main` से वहाँ मर्ज हो चुका हो — स्वयं `v2/main` से नहीं। (v1 लाइन स्वतंत्र रूप से `v1/main` से `v1-latest` टैग पर रिलीज़ होती है और `main` को कभी स्पर्श नहीं करती; [रेपो स्थिति](#mcp-inspector) देखें।)
क्योंकि **एक ही संस्करण संख्या** है (केवल रूट `package.json` के पास एक है — क्लाइंट कोई नहीं रखते, इसलिए सिंक में रखने के लिए कुछ नहीं है और कोई `check-version` चरण नहीं है), रिलीज़ प्रवाह तीन चरणों का है।
**1. माइलस्टोन मर्ज से पहले `v2/main` पर बंप करें।** बंप माइलस्टोन के काम का हिस्सा है, इसलिए यह डेवलप ब्रांच पर होता है और बाकी सबके साथ `main` में प्रवाहित होता है:
नीचे वास्तविक इश्यू नंबर और रिलीज़ डालें — कमांड्स यथास्थिति कॉपी-पेस्ट करने योग्य लिखे गए हैं (एक `2.2.0` → `2.3.0` माइनर बंप):```bash
git checkout -b v2/chore/2010-bump-2-3-0 v2/main
npm version minor --no-git-tag-version # or major / patch; bump only, no tag
# PR → v2/main
⚠️ --no-git-tag-version महत्वपूर्ण है। एक सादा npm version टैग भी बनाता है, और टैग v2/main कमिट पर लैंड करेगा — लेकिन रिलीज़ को main से कटना होता है, इसलिए टैग को वहाँ मर्ज कमिट (चरण 3) पर पॉइंट करना पड़ता है। यहाँ टैग करने से ऐसे कमिट पर टैग बनता है जो कभी रिलीज़ नहीं होता।
2. v2/main → main को मर्ज करें सामान्य milestone-merge ब्रांच के ज़रिए। अब इसमें bump मौजूद है, इसलिए रिलीज़ पहले से सही संस्करण के साथ main पर पहुँचती है।
चरण 1 और 2 के बीच दोनों ब्रांच वास्तव में अलग होते हैं, और यह अपेक्षित है, drift नहीं: v2/main उस संस्करण को पढ़ता है जो बनाया जा रहा है, जबकि main अभी भी वर्तमान में रिलीज़ किया गया संस्करण पढ़ता है। यह क्रम जिस चीज़ को हटाता है वह है पोस्ट-रिलीज़ drift — एक बार milestone merge लैंड हो जाने पर वे फिर से एकमत हो जाते हैं, और v2/main कभी भी main से पीछे नहीं रहता। यदि आप v2/main को main से आगे देखते हैं, तो रिलीज़ चल रही है; यदि आप इसे पीछे देखते हैं, तो कुछ गलत हो गया।
3. main कमिट को टैग करें और Release का ड्राफ्ट बनाएं:```bash
git fetch origin main
git tag 2.3.0 origin/main && git push origin 2.3.0
publish⚠️ **`origin/main` को टैग करें, अपने स्थानीय `HEAD` को नहीं।** `git checkout main && git pull` आपके कॉन्फ़िगर की गई merge-or-rebase रणनीति के माध्यम से हल करता है, इसलिए एक divergent स्थानीय `main` चुपचाप स्थानीय commits उत्पन्न या replay कर सकता है। वहाँ `HEAD` को टैग करने से एक ऐसा commit टैग होता है जो `origin/main` पर नहीं है, और `git push origin <tag>` केवल टैग को push करता है — एक ऐसा release छोड़ देता है जिसका commit कभी प्रकाशित नहीं हुआ। `origin/main` को स्पष्ट रूप से नामित करने से टैग किया गया commit ठीक वही होता है जिसे remote branch इंगित करती है, चाहे स्थानीय स्थिति कुछ भी हो।
⚠️ **कोई `v` उपसर्ग नहीं।** इस repo के release टैग बिना उपसर्ग के `x.y.z` हैं — `2.2.0`, `2.1.0`, `2.0.0` — इसलिए `v2.3.0` नहीं, बल्कि `2.3.0` टैग करें। ध्यान दें कि npm का अपना `tag-version-prefix` डिफ़ॉल्ट रूप से `v` होता है और repo कोई `.npmrc` सेट नहीं करता, इसलिए एक सादा `npm version` `v`-उपसर्ग वाला टैग बना देता जो इस convention से मेल नहीं खाता। हाथ से टैग करना (चरण 3) ही इसे सही रखता है। workflow का assert चरण तुलना से पहले एक अग्रणी `v` हटा देता है, इसलिए `v`-उपसर्ग वाला टैग फिर भी प्रकाशित होगा — बस यह पिछले हर release के साथ असंगत होगा।
Release का लक्ष्य commit चुनता है कि कौन सा workflow चलेगा, इसलिए यह केवल तब प्रकाशित होता है जब release किसी ऐसे commit से काटा जाता है जिसमें यह (v2) workflow मौजूद है।
**Bump पहले `v2/main` पर क्यों किया जाता है ([#2010](https://github.com/modelcontextprotocol/inspector/issues/2010))।** पहले यह milestone-merge branch पर होता था, जो `main` से काटी जाती है — इसलिए bump केवल `v2/main` के *downstream* मौजूद था और कोई इसे वापस नहीं ले जाता था। `v2/main` 2.1.0 और 2.2.0 दोनों releases के दौरान `2.0.0` पर ही रहा। यह केवल cosmetic नहीं है: milestone-merge branch से काटी गई एक branch चुपचाप bump को एक असंबंधित PR में ले जाती है (यह [#2009](https://github.com/modelcontextprotocol/inspector/issues/2009) पर हुआ, जहाँ एक container bugfix `2.0.0 → 2.2.0` diff के साथ आया), और विकास के दौरान version पढ़ने वाली कोई भी चीज़ — `readInspectorVersion()`, `--version`, `GET /api/config` — दो releases पुराना version बताती थी।
भविष्य के drift को "ठीक" करने के लिए `main` को वापस `v2/main` में merge करने की कोशिश **न करें**। `main` संपूर्ण pre-v2 v1 इतिहास रखता है (`ec5d8e13 chore: replace main's tree with v2` के माध्यम से बनाए रखा गया — ~230 commits जो `v2/main` के पास नहीं हैं), इसलिए back-merge उस सबको develop branch के log में स्थायी रूप से जोड़ देता है, केवल दो-फ़ाइल परिवर्तन पहुँचाने के लिए। पहले bump करने का मतलब है कि back-merge करने के लिए कुछ भी नहीं बचता।
### Docker
एक container image release workflow द्वारा GHCR (`ghcr.io/modelcontextprotocol/inspector`, `linux/amd64` + `linux/arm64`) पर प्रकाशित किया जाता है। [`Dockerfile`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/Dockerfile) एक two-stage build है: पहला stage publishable tarball को install करता है और `npm pack` करता है; दूसरा stage उस tarball को `npm install -g` करता है, इसलिए image npm के समान artifact ही ship करती है, एक साफ `mcp-inspector` bin के साथ।```bash
# run the web UI (reads the auth token from the container logs)
docker run --rm -p 127.0.0.1:6274:6274 ghcr.io/modelcontextprotocol/inspector
# or build the image locally
docker build -t mcp-inspector .
docker run --rm -p 127.0.0.1:6274:6274 mcp-inspector
Apps टैब का उपयोग कर रहे हैं? 6275 भी प्रकाशित करें। MCP Apps सैंडबॉक्स एक दूसरा लिसनर है जिस तक ब्राउज़र सीधे पहुँचता है, MCP_SANDBOX_PORT (डिफ़ॉल्ट 6275) पर। किसी और चीज़ को इसकी आवश्यकता नहीं है, इसलिए ऊपर दिए गए सिंगल-पोर्ट कमांड सामान्य निरीक्षण के लिए ठीक हैं — लेकिन इसके बिना Apps टैब एक खाली विजेट दिखाता है:```bash
docker run --rm -p 127.0.0.1:6274:6274 -p 127.0.0.1:6275:6275
ghcr.io/modelcontextprotocol/inspector
इसे अंदर और बाहर **एक ही पोर्ट नंबर** पर प्रकाशित करें। सैंडबॉक्स URL ब्राउज़र को `/api/config` के माध्यम से `http://localhost:<container port>/sandbox` के रूप में दिया जाता है, इसलिए इसे रीमैप करना (`-p 9000:6275`) एक ऐसे पोर्ट का विज्ञापन करता है जिस तक ब्राउज़र नहीं पहुँच सकता; इसके बजाय `-e MCP_SANDBOX_PORT=9000 -p 127.0.0.1:9000:9000` उपयोग करें।
**प्रकाशित पोर्ट पर `127.0.0.1:` उपसर्ग रखें।** एक सादा `-p 6274:6274` **हर होस्ट इंटरफ़ेस** पर प्रकाशित करता है, जिससे इंस्पेक्टर आपके स्थानीय नेटवर्क पर उपलब्ध हो जाता है। कंटेनर का `HOST=0.0.0.0` एक अलग मुद्दा है — यह _कंटेनर के_ इंटरफ़ेस को नियंत्रित करता है, होस्ट के नहीं — इसलिए `DANGEROUSLY_BIND_ALL_INTERFACES` ऑप्ट-इन, जो कंटेनर के बाहर वाइल्डकार्ड बाइंड के विरुद्ध सुरक्षा देता है, इस पर लागू नहीं होता। यह सामान्य वेब ऐप की तुलना में यहाँ अधिक मायने रखता है: बैकएंड अनुरोध पर प्रोसेस स्पॉन करता है, `GET /` API टोकन को सर्व किए गए HTML में एम्बेड करता है, और एक अनुरोध जिसमें **कोई** `Origin` हेडर नहीं है, ओरिजिन अनुमति-सूची को पूरी तरह से बायपास कर देता है — इसलिए किसी भी गैर-ब्राउज़र क्लाइंट के लिए API टोकन ही एकमात्र सुरक्षा है। अधिक व्यापक रूप से प्रकाशित करने के लिए इंस्पेक्टर के सामने एक वास्तविक एक्सेस-नियंत्रण सीमा की आवश्यकता होती है — एक रिवर्स प्रॉक्सी जो प्रमाणीकरण करता है, एक SSH टनल, एक निजी नेटवर्क। अपना स्वयं का `MCP_INSPECTOR_API_TOKEN` सेट करना इसका विकल्प **नहीं** है: `GET /` उपयोग में मौजूद किसी भी टोकन को उजागर करता है, इसलिए एक कस्टम टोकन भी उतनी ही आसानी से प्राप्त किया जा सकता है जितनी आसानी से एक उत्पन्न टोकन।
**जोड़े गए सर्वरों को बनाए रखना।** इंस्पेक्टर आपकी सर्वर सूची को `$HOME/.mcp-inspector/mcp.json` में सहेजता है, जो इमेज में `/home/node/.mcp-inspector/mcp.json` है — कंटेनर की लिखने योग्य परत के अंदर, इसलिए `--rm` इसे हटा देता है और हर रन एक खाली सूची के साथ शुरू होता है। इसे बनाए रखने के लिए वहाँ एक वॉल्यूम माउंट करें:```bash
docker run --rm -p 127.0.0.1:6274:6274 \
-v mcp-inspector-data:/home/node/.mcp-inspector \
ghcr.io/modelcontextprotocol/inspector
यही वॉल्यूम OAuth टोकन और संग्रहीत स्थिति को भी बनाए रखता है, इसलिए एक अधिकृत सर्वर रनों के बीच अधिकृत बना रहता है। Use -e MCP_CATALOG_PATH=/some/other/path.json to put the catalog somewhere else — mount a volume covering whatever directory you point it at. यदि आप नामित वॉल्यूम के बजाय होस्ट डायरेक्ट्री को बाइंड-माउंट करते हैं (-v "$PWD/inspector-data:/home/node/.mcp-inspector"), तो डायरेक्ट्री अपनी होस्ट स्वामित्व बनाए रखती है, इसलिए Linux पर --user "$(id -u):$(id -g)" जोड़ें या इसे uid 1000 पर chown करें — अन्यथा गैर-root node उपयोगकर्ता लिख नहीं सकता और सर्वर जोड़ने पर EACCES त्रुटि आती है।
इस फिक्स से पहले के इमेज से अपग्रेड कर रहे हैं? पुराने इमेजेस /home/node/.mcp-inspector नहीं बनाते थे, इसलिए Docker ने वॉल्यूम के माउंट पॉइंट को root के रूप में बनाया और गैर-root node उपयोगकर्ता उसमें लिख नहीं सकता था। एक खाली वॉल्यूम वर्तमान इमेज के पहले रन पर स्वयं को ठीक कर लेता है (Docker खाली वॉल्यूम पर इमेज डायरेक्ट्री की स्वामित्व लागू करता है), लेकिन जिसमें पहले से फाइलें हैं, वह अपनी पुरानी root स्वामित्व बनाए रखता है और फिर भी EACCES के साथ विफल होता है। इसे एक बार ठीक करें:```bash
docker run --rm -u 0 --entrypoint chown
-v mcp-inspector-data:/data ghcr.io/modelcontextprotocol/inspector
-R node:node /data
| Config | प्रदर्शित करता है | मुद्दा |
|---|
mcp-app-http.json (legacy युग) | Apps टैब में एक MCP App (UI संसाधन + app टूल) | #1859 |
modern-mrtr-http.json | एक एकल MRTR राउंड-ट्रिप | — |
mrtr-showcase-http.json | एक सर्वर में हर MRTR प्रीसेट | #1860 |
modern-network-http.json | Network टैब: Mcp-* हेडर + त्रुटि वर्गीकरण | #1628 |
xmcpheader-modern-http.json | Tools टैब: x-mcp-header मिररिंग और बहिष्करण | #1632 |
pagination-http.json | पृष्ठ-दर-पृष्ठ सूची लाना | #1721 |
structured-output-http.json | Tools टैब: किसी परिणाम का structuredContent अनुभाग | #1908 |
duplicate-tool-names-http.json | एक tools/list जो किसी टूल का नाम दोहराता है | #1957 |
nullable-fields-http.json | Tools टैब: nullable (anyOf + null) तर्क | #1928 |
rfc6570-templates-http.json | Resources टैब: RFC 6570 संसाधन-टेम्पलेट विस्तार | #1919 |
advertised-extensions-http.json | विज्ञापित एक्सटेंशन पर आधारित टूल पंजीकरण | #1739 |
logging-{legacy,modern}-http.json | लॉगिंग, दोनों युग | #1629 |
subscriptions-{legacy,modern}-http.json | संसाधन सदस्यताएँ, दोनों युग | #1630 |
tasks-{legacy,modern}-http.json | कार्य, दोनों युग | #1631 |
| Preset | व्यवहार |
|---|
mrtr_confirm | एकल राउंड |
mrtr_two_step | requestState के माध्यम से दो इलिसिटेशन राउंड |
mrtr_sample | एम्बेडेड सैंपलिंग → Sampling पैनल |
mrtr_roots | एम्बेडेड roots/list, कॉन्फ़िगर किए गए roots से चुपचाप स्वतः-उत्तरित (कोई मोडल नहीं) |
mrtr_edge | केवल inputRequests वाला राउंड, फिर केवल requestState वाला राउंड |
mrtr_empty | खाली परिणाम के साथ पूर्ण होता है — न content, न structuredContent |
mrtr_loop | कभी पूर्ण नहीं होता → MRTR_MAX_ROUNDS सीमा को ट्रिगर करता है |
| Tool | प्रतिक्रिया |
|---|
trigger_header_mismatch | 400 / -32020 |
trigger_missing_capability | 400 / -32021 |
trigger_unsupported_version | 400 / -32022 (data.supported के साथ) |
trigger_method_not_found | 404 / -32601 |
| Shape | SDK व्यवहार |
|---|
{a,b} | मानों को कच्चा जोड़ता है — कोई एन्कोडिंग नहीं, ऑपरेटर उपसर्ग हटा दिया गया |
{;id} | ; इसकी ऑपरेटर सूची से गायब है, इसलिए वेरिएबल ;id के रूप में पार्स होता है |
{id:3} | उपसर्ग संशोधक नाम में सम्मिलित हो जाता है, जिससे id:3 मिलता है |
{+v} / {#v} | encodeURI आरक्षित [/] को बिगाड़ता है ([::1] → %5B::1%5D) और pct-ट्रिपलेट को दोगुना एन्कोड करता है (%2F → %252F) |
{v} | encodeURIComponent उप-विभाजक !'()* को बिना एन्कोड किए छोड़ देता है, जिसे RFC 6570 एन्कोड करने की आवश्यकता रखता है |