
افحص خوادم بروتوكول سياق النموذج (MCP) وصحّح أخطاءها واختبرها بصريًا من واجهة ويب أو CLI أو TUI، مع استكشاف الأدوات والموارد، وتسجيل الطلبات، ودعم 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) — أعلام سطر الأوامر، والفصل الجديد بين `--config` و`--catalog`، ورفع إصدار محرك Node، وما لم يعد مشمولًا.
> **حالة المستودع.** هذا هو خط **v2** من Inspector. التطوير النشط يجري على **`v2/main`** (فرع التطوير — تستهدفه جميع طلبات السحب الخاصة بـ v2)، ويُدمج في **`main`** عند الإصدارات الرئيسية؛ `main` هو الفرع الافتراضي ويحتوي على أحدث نسخة v2 مُصدَرة، منشورة على وسم `latest` في npm. أما خط **v1** القديم فيعيش على **`v1/main`** — إصلاحات أمنية فقط، تُنشر مباشرة من ذلك الفرع إلى وسم `v1-latest` في npm (`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` في وقت البناء (بدون `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)، وإعادة تسمية متغيرات البيئة، والحزم الفرعية التي لم تعد تُوفَّر.--catalog مقابل --config، والأهداف المؤقتة، وفاصل --، وتنسيق الملف وحقولها الخاصة بالمفتش لكل خادم. مشترك بين جميع العملاء الثلاثة؛ يحيل ملفا README الخاصان بـ cli و tui قسمي خيارات الخادم إليه.--app-info → تنقل عبر الروابط العميقة → عرض الأداة، بالإضافة إلى تسليم OAuth ودعم الوكيل.يتطلب Node >=22.19.0.```bash
npm install # root install; postinstall cascades into every client
- **استنساخ جديد:** شغّل `npm install` في جذر المستودع.
- **بعد سحب (pull) يغيّر تبعيات أحد العملاء:** أعد تشغيل `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 تتصعد إلى الأعلى، لذا يكون تثبيت الجذر ضمن سلسلة كل عميل، وmanifest الجذر هو بالفعل ما يعتمد عليه الملف المضغوط المنشور في الحلّ. إعلانها في كل عميل على حدة يثبّت نسخة ثانية قد تنحرف عن نسخة الجذر، وبهذا الشكل انتهى الأمر بنسختين من `ext-apps` (ومن `@modelcontextprotocol/sdk` الانتقالية v1) في الشجرة قبل [#1970](https://github.com/modelcontextprotocol/inspector/issues/1970) — والنسخة الثانية من `client`/`core` هي المشكلة التي يحمل `vitest.shared.mts` لها حلًّا بديلًا عبر `dedupe`. وينطبق نفس الإبقاء على الجذر فقط على أي شيء يُتاح حصريًا عبر كود يخصّ الجذر دون manifest خاص به (`test-servers/src`, `core/`)، و`vitest.shared.mts` يعمل alias لتلك العناصر إلى جذر المستودع — `express` و`yaml`، وكلاهما يُتاح عبر `test-servers/src`، هما الاثنان اليوم. **ما إذا كانت هذه الحزمة `dependency` أو `devDependency` يتحدد من يستهلكها وقت التشغيل، وليس من مكان إعلانها:** أي شيء يستورده `core/` وقت التشغيل يجب أن يكون **`dependency`** في الجذر، لأن بناءات العملاء تُخرج حزم npm كاعتماديات خارجية والتثبيت المنشور يحلّها من manifest الجذر، حيث تكون 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`، لذا فإن التثبيت من الحزمة المنشورة يحلّهما من manifest الجذر. نقلهما إلى `devDependencies` سيكسر `--web --dev` لدى المستخدمين (واستدعاء `vite build` عند الحاجة في `ensure-web-build.ts`) بينما يجتاز كل الفحوص المحلية. وهذا يعني أنهما سيظهران تحت `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/` يحتوي على المنطق المشترك بين العملاء الثلاثة جميعًا، بحيث يتصرف الويب وCLI وTUI بشكل متطابق. نقطة الدخول إليه هي فئة **`InspectorClient`** (`core/mcp/`)، وهي المسؤولة عن الاتصال بخادم MCP، ودورة حياة الطلب/الاستجابة، ومجموعة مخازن الحالة؛ بينما يوفر `core/react/` خطافات React فوق تلك المخازن، وتستهلك شجرتا React في الويب وTUI (Ink) هذه الخطافات. أما OAuth (`core/auth/`) فهو مقسّم إلى منطق متماثل الشكل (isomorphic) بالإضافة إلى خلفيات للمتصفح وNode وللوصول البعيد، بحيث تعمل التدفقات نفسها في المتصفح وفي Node وفي مقابل خلفية بعيدة.
`core/` لا يحتوي عمدًا على **أي `package.json`** — ولا يُنشر بمفرده. بل يضمّنه كل عميل عبر اسم مستعار `@inspector/core`:
- **CLI / TUI:** `esbuildOptions.alias` في `tsup.config.ts` الخاص بهما يربط `@inspector/core` → دليل `core/` في المستودع، ويعمل `noExternal: [/^@inspector\/core/]` على تضمينه مباشرة في الحزمة.
- **Web:** نفس الاسم المستعار في `clients/web/vite.config.ts` لتطبيق المتصفح ومشغّل خلفية Node.
نشر `core/` كحزمة مستقلة (مثلًا ليتمكن أطراف ثالثة من البناء عليها) مؤجّل عمدًا — انظر القضية [#1636](https://github.com/modelcontextprotocol/inspector/issues/1636).
## عميل الويب: "مكوّنات بسيطة" + Storybook
عميل الويب الإصدار v2 مبني من **مكوّنات عرضية ("بسيطة")** — تقبل البيانات ومعاودات النداء (callbacks) كخصائص (props) وتحتوي على منطق العرض فقط، دون جلب بيانات مباشر أو حالة عميل. الحالة تأتي من خطافات `@inspector/core` الموصّلة قرب أعلى الشجرة. وهذا يُبقي المكوّنات معزولة وقابلة للاختبار والتوثيق.
هذا النهج هو ما يجعل **Storybook** من الدرجة الأولى هنا: كل مكوّن شاشة أو عنصر له ملف `*.stories.tsx` (أكثر من 96 قصة) يعرضه مقابل خصائص تجريبية. وتعمل **دوال التشغيل (play functions)** في Storybook كاختبارات تفاعل، وتُشغَّل بدون واجهة رسومية في CI (`npm run ci:storybook`، عبر Chromium وPlaywright).
يتبع التنسيق اصطلاحًا صارمًا يضع Mantine أولًا (متغيرات الثيم وخصائص المكوّنات بدلًا من أصناف CSS، وخصائص CSS المخصصة `--inspector-*` بدلًا من قيم الألوان الحرفية). القواعد الكاملة موجودة في [`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md) ضمن **تعليمات React** — اقرأها قبل لمس واجهة الويب. مكوّنات العناصر موجودة في `clients/web/src/components/elements/`؛ ومتغيرات الثيم في `clients/web/src/theme/`.
## خوادم الاختبار
يوفر `test-servers/` **خوادم MCP قابلة للتجميع** تستخدمها مجموعتا اختبارات التكامل والدخان، بحيث تختبر الاختبارات خادمًا حقيقيًا عبر ناقل حقيقي بدلًا من الكائنات المقلدة. يُجمّع الخادم من **قوالب جاهزة (presets)** (مصانع بيانات الاختبار في `test-servers/src/preset-registry.ts` — أدوات، موارد، مطالبات، مهام، elicitation، أخذ عينات، OAuth، …) ويمكن تشغيله بطريقتين:
- **داخل العملية** — استيراد المصانع (`createTestServerHttp`, `createEchoTool`, …) وتشغيل الخادم داخل حلقة أحداث الاختبار (يُستخدم في مسارات تكامل HTTP).
- **كعملية فرعية** — يُطلق `test-servers/build/test-server-stdio.js` كطفل stdio حقيقي (يُستخدم في اختبارات دخان CLI وتكامل stdio).
يمكنك إعداد خادم بطريقة تعريفية باستخدام إعداد JSON (انظر `test-servers/configs/*.json`) لاختيار القوالب، ثم تحميله عبر `--config`. ولأن الخوادم تُطلق كعمليات فرعية حقيقية، يجب أن يكون ناتج البناء موجودًا أولًا:```bash
npm run test-servers:build # (from clients/web) → tsc -p test-servers, emits test-servers/build/
The Vite alias @modelcontextprotocol/inspector-test-server (in clients/web/vite.config.ts) points at test-servers/build/index.js so getTestMcpServerPath() resolves to a real .js path.
يمكن لخادم Streamable-HTTP أيضًا تقديم حقبة البروتوكول الحديثة (2026-07-28) عبر createMcpHandler الخاص بـ SDK:
transport.modern في إعداد JSON — true للخدمة عديمة الحالة ثنائية الحقبة، أو { "legacy": "reject" } للصرامة الحديثة فقط.modern في ServerConfig لاستدعاء createTestServerHttp داخل العملية.هذا ما يسمح لاتصال Inspector الذي يتفاوض على protocolEra: "auto" | "modern" بالوصول إلى المسار الحديث (server/discover معبّأ، بدون جلسات). راجع test-servers/configs/modern-http.json.
كل إعداد أدناه هو خادم جاهز لتجربة ميزة واحدة يدويًا. حمّله باستخدام --config، وما لم يُذكر خلاف ذلك، اتصل بـ Protocol Era = Modern.
mcp-app-http.json يقدّم أداة mcp_app_demo (_meta.ui.resourceUri) إلى جانب مورد واجهة المستخدم mcp_app_demo_widget الخاص بها، بحيث يكون لدى تبويب Apps تطبيق حقيقي لعرضه. إنه خادم Streamable-HTTP عادي — اتصل بحقبة البروتوكول الافتراضية (القديمة)، وليس الحديثة.
افتح تبويب Apps، وحدد mcp_app_demo، وأعطه عنوانًا وانقر على Open App: سيُعرض الودجت داخل iframe بيئة الحماية ويمارس سطح بروتوكول واجهة المستخدم من جانب المضيف — عرض سياق المضيف، وsize-changed، وui/message، وسطر سجل في لوحة App logs. ولأن الودجت يُقدَّم عبر صفحة وكيل بيئة الحماية، فإن هذا الإعداد هو أيضًا ما يعيد إنتاج #1859 (غياب clients/web/static/sandbox_proxy.html يظهر هنا كرسالة "Sandbox not loaded" مكان الودجت) — وهو فشل لا يظهر إلا في حزمة مثبتة، ولا يظهر أبدًا في المستودع.
للنسخة النصية من نفس التدفق (استكشاف --app-info → رابط عميق → ودجت معروض)، راجع مراجعة تطبيق MCP.
modern-mrtr-http.json يقدّم أداة mrtr_confirm (الإعداد المسبق mrtr_confirm، createMrtrTool) عبر المسار الحديث. يعيد معالِجه inputRequired(...) مع تضمين استدعاء نموذج، لذا فإن استدعاءه ينتج رحلة ذهابًا وإيابًا حقيقية: input_required → يلبي العميل الاستدعاء المضمّن ويعيد المحاولة بمعرّف جديد → complete.
يقود Inspector عملية MRTR يدويًا (inputRequired: { autoFulfill: false })، لذا يتوقف الاستدعاء المضمّن عند نافذة الطلب المعلّق (الموسومة "input_required") لتجيب، ثم تكتمل إعادة المحاولة. مفيد لفحص كلٍّ من تجربة الطلب المعلّق وتجميع محادثات MRTR في عرض Protocol.
mrtr-showcase-http.json يجمع كل إعدادات MRTR المسبقة في خادم واحد:
شغّل mrtr_empty وأجب عن استدعائه الوحيد: سيجمع تبويب Protocol التبادل كمحادثة MRTR تنتهي بـ COMPLETE، وتقول لوحة Results "Empty result — The tool call completed successfully and returned no content." في البناء المعطوب، كانت النتيجة نفسها تُعرض كـ "No results yet" — النص الاحتياطي للوحة قبل التشغيل (#1860) — لذا فإن استدعاءً شاهده المستخدم للتو ينجح يُقرأ كاستدعاء لم يُنفَّذ أبدًا. مصفوفة content فارغة بدون structuredContent هي 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:
افتح تبويب Network لترى ترويسات Mcp-* المعكوسة مميزة، والقيم الحارسة مفكوكة الترميز، وكل خطأ معروضًا بشكل مميز.
عكس
Mcp-Param-*يبنيه Inspector، وليس SDK. يعكس SDK الترويسات فقط داخلclient.callTool()، ويتخطاها في المتصفح (detectProbeEnvironment() !== "browser"). يوجّه Inspector استدعاءtools/callعبرclient.request()لقيادة MRTR يدويًا، لذا يبني الترويسات المعكوسة بنفسه (#1846) — على كل عميل، بما في ذلك الويب، لأن طلب عميل الويب العلوي يُصدر من خلفية Node وليس من المتصفح. لذا يمكن استدعاءget_weatherمن الويب وCLI وTUI على حد سواء، في شكلَيها العادي و"Run as task".
x-mcp-header في تبويب Toolsxmcpheader-modern-http.json يقدّم:
echo — أداة عادية.get_weather — تعليق صالح x-mcp-header: "City" على وسيطته 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؛ ويُظهر Inspector السبب.
في SDK الإصدار 2، يظهر رفض tools/call بالخطأ -32602 كلوحة خطأ مميزة بدلًا من نتيجة 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" (إعدادات الخادم — إعداد paginatedLists، أو مفتاح Paginated في شريط جانبي للقائمة) وستُحمَّل القوائم بالصفحة 1 فقط (4 عناصر) مع عنصر تحكم Load next page وحالة N pages loaded. كل نقرة تجلب الأربعة التالية وتلحقها؛ وزر Refresh يعيد التعيين إلى الصفحة 1. عند إيقاف المفتاح (الافتراضي)، تجمع القوائم نفسها الصفحات الثلاث تلقائيًا عند الاتصال.
structured-output-http.json يقدّم list_items (structuredContent متداخل — كائنات داخل مصفوفات داخل كائن، الشكل من #1908)، وget_temp (حمولة مسطحة بثلاثة مفاتيح)، وecho (بدون outputSchema إطلاقًا). إنه خادم Streamable-HTTP عادي — اتصل بحقبة البروتوكول الافتراضية (القديمة).
شغّل list_items من تبويب Tools: تعرض لوحة النتيجة ملخص النص content[] ("Found 2 items.") و قسمًا قابلًا للطي باسم Structured Output يعرض الحمولة المُتحقق منها بالمخطط كـ JSON منسّق وقابل للنسخ. هذا القسم هو ما كان الإصدار 2 يُسقطه — الأداة التي تعلن outputSchema تعيد بياناتها الحقيقية هناك، وعادةً ما يلخصها كتلة النص فقط. شغّل echo لتأكيد غياب القسم عندما لا تحمل النتيجة structuredContent.
duplicate-tool-names-http.json يقدّم get_weather وget_temp وecho وadd، ثم يكرر get_weather وecho في نهاية tools/list بنفس name وعنوان (duplicate) (duplicateToolNames). لا يمكن لأي إعداد مسبق إنتاج هذا الشكل — إذ يرفض registerTool في SDK الاسم المكرر — لكن الخادم الحقيقي يمكنه ذلك ويفعله، وعلى Inspector عرضه بأمانة.
اتصل (حقبة قديمة افتراضية)، وافتح تبويب Tools، واكتب get في Search tools: يجب أن تضيق القائمة إلى صفوف get_* الثلاثة بالضبط. في البناء المعطوب، كانت تبقي صف echo قديمًا، لأن الشريط الجانبي كان يربط الصفوف بـ tool.name وحده، وتسببت المفاتيح المتصادمة في تيتم عنصر أثناء إعادة المطابقة (#1957).
النسخ المكررة تُلحق في النهاية وليس بجانب توأمها عن قصد. يطابق React دفعة الأطفال البادئة بنفس المفتاح أولًا، لذا فإن التكرار المجاور للرأس يصطف مصادفةً ويختفي الخلل؛ فصل الزوج هو ما يجعل الخلل قابلًا للملاحظة — وهو أيضًا الشكل الواقعي، مصدرا أدوات متسلسلان.
nullable-fields-http.json يقدّم record_shipment، الذي صُرّحت وسائطه الأربع كلٌّ منها بدالة .nullish() في Zod — "اختيارية و قابلة للـ null صراحةً". يترجم ذلك إلى anyOf: [<branch>, { "type": "null" }]، لذا يقع النوع الحقيقي (و، بالنسبة للتعداد، قائمة enum الخاصة به) على فرع وليس في المستوى الأعلى. get_temp بجانبه مع تعداد units عادي غير قابل للـ null للمقارنة. Streamable-HTTP عادي — اتصل بحقبة البروتوكول الافتراضية (القديمة).
افتح تبويب Tools وحدد record_shipment: يجب أن يظهر direction كقائمة Select (envio / recebimento) مع زر مسح يعيده إلى null، وreference كحقل إدخال نصي، وquantity كحقل إدخال رقمي، وexpress كمربع اختيار. في البناء المعطوب، سقط كلٌّ منها إلى حقل نص JSON الخام، الذي كان يعيد ترميز محتواه مع كل ضغطة مفتاح حتى صارت القيمة غير قابلة للاستخدام (#1928). تعيد الأداة صدى الوسائط التي تلقتها، لذا تعرض لوحة النتيجة ما أُرسل بالضبط.
كان لدى TUI نفس الفجوة ويستحق الفحص ضد الخادم نفسه (--tui، ثم اختبر record_shipment): direction قائمة تحديد، وquantity حقل عدد صحيح، وexpress قيمة منطقية. يتشارك العميلان الآن خطوة طيّ واحدة — normalizeNullableUnion في core/json/nullableUnion.ts — تحديدًا حتى لا يتباينا في المخططات التي يمكنهما عرضها.
rfc6570-templates-http.json يقدّم قالبَي موارد مباشرةً من #1919 — events_by_topic (foobar://events/{topic}) وevents_by_query (foobar://events{?topic}) — كلٌّ منهما يعيد صدى URI الذي طُوبق عليه، بالإضافة إلى مورد عادي foobar://events (انظر أدناه). Streamable-HTTP عادي؛ اتصل بحقبة البروتوكول الافتراضية (القديمة).
افتح تبويب 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 التعبير بالكامل عندما يكون المتغير غير معرّف، لذا فإن القراءة بحقل فارغ تطلب foobar://events، وملؤه يطلب foobar://events?topic=foo%2Fbar. تعرض معاينة URI بجانب العنوان الشكل الموسع جزئيًا أثناء الكتابة، تاركةً التعبيرات غير المعبأة كما هي.
مورد
foobar://eventsالعادي مسجَّل عن قصد، وليس كحشو. يترجمUriTemplate.match()في SDK تعبير{?topic}إلى\?topic=([^&]+)إلزامي، لذا لا يمكن للقالب وحده خدمة القراءة الفارغة — إذ يعيدmatch("foobar://events")قيمةnull. يكشف الخادم الحقيقي المجموعة غير المصفاة كمورد خاص به؛ يفعل العرض التوضيحي نفسه حتى تتحلّل هذه الخطوة فعلًا.
يوسّع عميل الويب وTUI عبر أداة مساعدة مشتركة واحدة، core/mcp/uriTemplate.ts — نموذج Resources في الويب مباشرةً، وTUI عبر InspectorClient.readResourceFromTemplate — ويستمد كلاهما حقول نماذجه من محللها أيضًا، وهو النصف الذي يجعل المشاركة حقيقية: يرسل النموذج القيم تحت الأسماء التي عرضها، لذا فالمحلل الذي يشوّه اسمًا يُسقط القيمة بصمت في وقت التوسيع. (CLI ليس مستهلكًا: لا يحتوي على نموذج قالب، وresources/read الخاص به يمرر --uri الموسع بالفعل مباشرةً.)
ما زال UriTemplate الخاص بـ SDK مستخدمًا، لكن فقط للتحقق من القالب (إنشاؤه هو ما يرفض التعبير غير المغلق). موسّعه ليس كذلك، لأنه ناقص في خمسة جوانب — كلٌّ منها مُقاس ضد إصدار 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} قيمة caf%C3%A9/value، وليس UTF-8 خامًا في المسار — وهو ما لا يفعله موسّع SDK أيضًا. وأما الأسماء التي قد يستخدمها القالب فهي varchar الخاص بـ RFC 6570 بالإضافة إلى تسامح موسوم لـ - و~: ترفض مجموعة المطابقة {default-graph-uri}، لكن الخوادم الحقيقية تنشر مثل هذه الأسماء ومطابق SDK يعيد توجيهها، لذا يوسّعها Inspector ويعلّم المتغير conforming: false بدلًا من رفض مورد يعمل بشكل مثبت.
المتغير غير المعرّف هو ما يُسقط تعبيره — المتغير المعرّف كسلسلة فارغة يتوسع (x{?q} يعطي x?q=، وx{;q} يعطي x;q، وفق RFC 6570 §3.2.7). يحترم الموسّع هذا التمييز، لذا يمكن لمستدعٍ مثل readResourceFromTemplate طلب أيٍّ من URI. دمج الاثنين مسألة نموذج، لا مسألة قالب: يبذر كلا العميلين كل متغير معلن بقيمة "" ولا يمكن لحقل إدخال نصي التعبير عن "معرّف لكن فارغ"، لذا يُسقط كل نموذج فراغاته (definedValues) عند الدخول.
الإلزامية خاصية من خصائص التعبير، لا المتغير: يُسقط RFC 6570 الأسماء غير المعرّفة من تعبير متعدد الأسماء، لذا فإن {a,b} مع تعبئة a فقط قابل للتوسيع ويجب ألا يمنعه النموذج. يعيد 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 عند مستوى محدد. القديم منها هو خادم HTTP قابل للبث عادي؛ أما الحديث فيضبط transport.modern: true.
send_notification إلى بث السجل إلى اللوحة._meta["io.modelcontextprotocol/logLevel"] على كل طلب لاحق (تحقّق من جسم الطلب في تبويب Network). يؤدي استدعاء send_notification إلى بث السجل عبر استجابة SSE الخاصة بالطلب. أعد الضبط إلى Off وسيتم حجب الاستدعاء نفسه بصمت — إذ يحذف الطلب مفتاح logLevel، فلا يصل السجل أبدًا.هذا الحجب مطابق للمواصفة ("a server MUST NOT emit notifications/message for a request that didn't opt in") لأن send_notification يُصدر عبر extra.log الخاص بـ SDK والمحصور بطلب معيّن والمدرك لحدود العتبة (ctx.mcpReq.log). في المسار الحديث، يقرأ اشتراك logLevel الخاص بكل طلب من مغلّف الطلب ويسقط الرسالة عندما لا يشترك العميل أو عندما يكون المستوى أدنى من الخطورة المطلوبة؛ وفي المسار القديم يلتزم بمستوى الجلسة من logging/setLevel. وبما أنه يُصدر عبر notify الخاص بالطلب، ترتقي الاستجابة الحديثة إلى SSE ويتنقل السجل عبر تيار الطلب الأصلي.
يقدّم كل من subscriptions-legacy-http.json وsubscriptions-modern-http.json ثلاثة numbered_resources مع subscriptions: true. كما يقدّم القديم أداة update_resource؛ بينما يضبط الحديث transport.modern: true.
resources/subscribe ويقسم Subscriptions بسرد URI دون أي زخارف بث. استدعِ update_resource مع ذلك URI، فيحدّث الخادم المحتوى ويصدر notifications/resources/updated، مانحًا البطاقة المشتركة طابع وقت آخر تحديث.subscriptions/listen (يحمل عامل التصفية resourceSubscriptions بالإضافة إلى الاشتراك resourcesListChanged) وتكتمل عند notifications/subscriptions/acknowledged. يعرض قسم Subscriptions بعدها شارة حالة البث (Connecting… → Listening) في ترويسته، ويعيد الاتصال بإعادة القائمة إذا انقطع التيار الطويل الأمد.يحذف الإعداد الحديث عمدًا update_resource. المسار الحديث في SDK عديم الحالة ويعمل لكل طلب (createMcpHandler(() => createMcpServer(config)))، لذا ستعمل الأداة مقابل مثيل خادم مؤقت — لن يستمر تغيير المحتوى إلى resources/read التالي، ولن يصل resources/updated إلى تيار الاستماع المنفصل. إنه يسبب الارتباك أكثر من كونه مفيدًا.
لذا يتم عرض دورة إشعار التحديث المباشرة ذهابًا وإيابًا على الخادم القديم (جلسة ذات حالة)، أما الخادم الحديث فهو لسلوك الاشتراك/الاستماع/الشارة. مسار receive في Inspector شفاف بين الحقبتين، لذا فإن أي خادم حديث حقيقي ذي حالة يوجّه 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 الإصدار 2 كل دعم المهام و يستبعد أساليب مواصفة tasks/* من الحقبة الحديثة على كلا الجانبين. لذا يقود Inspector الامتداد بنفسه — يُعاد كتابة إطار resultType: "task" عند النقل إلى CallToolResult يحمل المقبض، وتنتقل tasks/get / update / cancel عبر قناة طلبات خام مع المغلف الحديث الكامل. يقدّم خادم الاختبار tasks/* من معترض Express قبل معالج SDK، لأن مسار SDK الحديث كان سيجيب عليها بـ -32601.
يعيد Refresh في تبويب Tasks استطلاع المقابض المعروفة بالفعل لدى العميل — فالحقبة الحديثة لا تحتوي على قائمة مهام من جهة الخادم.
npm run build # builds all clients: web → cli → tui → launcher
العملاء الفرديون: `build:web`, `build:cli`, `build:tui`, `build:launcher`. ينتج بناء الويب كلاً من SPA للمتصفح (`clients/web/dist`, Vite) ومشغّل خادم الإنتاج Node (`clients/web/build`, tsup).
## الاختبار وبوابة الجودة
كل عميل يتحقق من نفسه من مجلده الخاص؛ وتربطها سكربتات الجذر في سلسلة. لا يوجد **أي** سكربت `test` جذري مجمّع — استخدم `validate` (سريع) أو `coverage` (البوابة).
| السكربت | ماذا يفعل |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `npm run validate` | يشغّل أولاً الحرّاس الثلاثة الدائمين — `verify:format-coverage` (كل ملف مصدري متتبَّع خاضع لبوابة التنسيق)، `verify:typecheck-coverage` (كل ملف يقع ضمن مشروع tsconfig)، `verify:dep-lockstep` (لا يصل أي اعتماد إلى برنامج `tsc` واحد من تثبيتين بشكل منحرف عبرهما) — ثم `test:scripts` (اختبارات الوحدة الخاصة بمحللات الحرّاس)، ثم `validate:core` (بوابة `format:check` + `lint` المشتركة في `core/`)، ثم لكل عميل: `format:check` + `lint` + **`typecheck`** (cli/tui/launcher؛ بينما web يتحقق من الأنواع عبر `tsc -b` داخل `build`) + `build` + اختبارات الوحدة السريعة. إنه فحص الحلقة الداخلية السريع. |
| `npm run coverage` | بوابة **≥90% لكل ملف** (الأسطر/العبارات/الدوال/الفروع) تحت أدوات v8، لكل عميل. مفروضة في CI. بالنسبة إلى web، يشغّل هذا أيضًا مشروع التكامل ويغطي وقت تشغيل `core/` المشترك (بما في ذلك `core/json` و`core/client`). |
| `npm run smoke` | اختبارات دخان شاملة عبر المشغِّل المبني (`--help` dispatch + prod cli/tui/web)، إضافة إلى اختباري دخان عبر Chromium بدون واجهة: اختبار دخان للإقلاع يشغّل حزمة الويب الإنتاجية ويتحقق من أول عرض نظيف (لا خطأ غير ملتقط — استثناء متزامن أو رفض غير معالج، وهو كيفية ظهور وحدة Node مدمجة تصل إلى حزمة المتصفح)، واختبار دخان **تطبيقات MCP** (`smoke:web:app`) يقود الاتصال ← فتح التطبيق ← `data-app-status="ready"` مقابل خادم تطبيقات قابل للتركيب، ويغطي وسيط الصندوق الرملي وجسر بروتوكول الواجهة. |
| `npm run verify:build-gate` | يشغّل `vite build` حقيقيًا مع وحدة Node مدمجة مُجبرة على الدخول إلى رسم المتصفح، ويتحقق من أن البناء **يفشل** عبر بوابة #1769 (التي تحوّل تحذير Vite الخاص بإخراج وحدات المتصفح إلى خطأ صارم). يحرس من انجراف صيغة التحذير في ترقية Vite وتعطيل البوابة بصمت. جزء من `npm run ci`. |
| `npm run verify:format-coverage` | يحلل أنماط glob الخاصة بـ `format:check` من كل `package.json` (فقط تلك القابلة للوصول من `validate`)، ويعدّ جميع الملفات المصدرية المتتبَّعة، و**يفشل** مع سرد أي ملف غير مغطى بواسطة glob — الحارس الدائم لثبات «كل ملف مصدري من الطرف الأول خاضع لبوابة التنسيق» (#1792). يعمل أولاً في `validate`. |
| `npm run test:scripts` | اختبارات وحدة مدفوعة بالجداول (`node --test`) لمحللات الحرّاس النقية نفسها (`scripts/lib/npm-scripts.mjs`, `scripts/lib/tsc-program.mjs` + الأدوات المساعدة المُصدَّرة من `verify-typecheck-coverage.mjs` و`verify-dep-lockstep.mjs`)، حالة واحدة لكل قاعدة ترمّزها، بالإضافة إلى `scripts/lib/resolve-node-bin.test.mjs` — محلل الثنائيات عبر المنصات (#1939)، المثبَّت مقابل أشكال `bin`/`exports` الحقيقية للحزم التي تشغّلها السكربتات فعليًا. يعمل في `validate` — و`verify:typecheck-coverage` يحرس *هذه* البوابة بدوره (قابل للوصول من `validate`، مجموعة اختبارات غير فارغة، كل ملف اختبار مطابق لأنماط glob الخاصة بـ `test:scripts`)، لأن `node --test` يتخطى بصمت أي ملف يفوته glob الخاص به ويظل يخرج برمز 0. |
| `npm run verify:typecheck-coverage` | النظير المكافئ لتغطية فحص الأنواع لما سبق (#1791): لكل عميل Node (يُكتشف تلقائيًا من القرص — يُسجَّل عبر مشاريع سكربت `typecheck` الخاص به، أو لعميل `tsc -b` مثل `clients/web` عبر `references` في `tsconfig.json`) يشغّل تلك المشاريع باستخدام `tsc --listFilesOnly`، ويوحّدها، و**يفشل** مع سرد أي ملف `.ts`/`.tsx`/`.mts`/`.cts` متتبَّع داخل العميل لا يقع في أي مشروع (حتى لا يمر إعداد/مساعدة جديدة على مستوى الجذر دون فحص أنواع بصمت). كما يتطلب، افتراضيًا على الرفض، أن توجد ملفات TypeScript من الطرف الأول التي لا يملكها أي عميل (`test-servers/src`، و`vitest.shared.mts` الجذرية، وكل `core/`، وأي موقع جديد على مستوى الجذر) في تمريرة tsc التابعة لمشروع أحد العملاء — لذا يُلتقط أيضًا `*.tsx` في `core` لا تصل إليه مشاريع الويب. ويتحقق أيضًا من أن البوابة موصولة (تمريرة فحص الأنواع لكل عميل — سكربت `typecheck` الخاص به، أو `tsc -b` الخاص بـ web — قابلة للوصول من `validate` الخاص به، والسلسلة الجذرية تشغّل `validate` لكل عميل). يعمل في `validate`. |
| `npm run verify:dep-lockstep` | يحرس ثبات «إصدار واحد لكل اعتماد يعبر التثبيتات» (#1896). v2 ليس مساحة عمل، لذا فإن مشروع اختبار العميل يترجم TypeScript المشترك من الطرف الأول — `core/` و`test-servers/src` و`vitest.shared.mts` المملوكة للجذر، وكلها تحل اعتماداتها من التثبيت **الجذري** — جنبًا إلى جنب مصادر العميل نفسه، مما يضع الحزمة نفسها في برنامج `tsc` واحد مرتين. عند نفس الإصدار يكون ذلك غير ضار؛ أما عند التباين، يجب على TypeScript الربط بين نسختين مختلفتين بنيويًا لكل نوع، وهو ما يكون أسيًا بالنسبة لسطح عام-تكراري (استنفد zod `4.3.6` مقابل `4.4.3` كومة tsc بسعة 4GB في `clients/web`). يستمد مجموعة مرشحاته من **ما يدخل فعليًا كل برنامج** (#1965) — كل مشروع tsconfig للعملاء مدرج عبر `tsc --listFilesOnly` باستخدام `scripts/lib/tsc-program.mjs` المشترك، ويُربط كل ملف `node_modules` تم حله بتثبيته المالك، محتفظًا بالحزم التي تصل إلى برنامج واحد من تثبيتين (الحزمة التي تصل تصريحاتها فقط عبر `.d.ts` لحزمة أخرى، كما تفعل تصريحات `@modelcontextprotocol/sdk`، تكون غير مرئية لفحص استيرادات الطرف الأول). يحدد إصدار كل نسخة من إدخال ملف القفل لمسار التثبيت الدقيق الذي حله البرنامج، ويقارن فقط التثبيتات التي التقت في برنامج واحد، و**يفشل افتراضيًا على الرفض** عند أي اختلاف غير موجود في قائمة `TOLERATED_SKEW` المسموحة — فارغة اليوم — مع التسامح مع الحزمة في القائمة فقط *داخل إصدار رئيسي واحد*. يعمل في `validate`.
| `npm run ci` | **أمر إلزامي قبل الدفع.** `validate` → `coverage` → `verify:build-gate` → `smoke` → Storybook. مجموعة شاملة فعلية لـ GitHub CI. |
| `npm run pack:verify` | اختبار دخان للنشر — انظر [النشر](#publishing). |
توجد أيضًا سكربتات لكل عميل (`validate:web`, `coverage:cli`, `smoke:tui`, …)، بالإضافة إلى `validate:core` / `format:core` الجذرية لحزمة `core/` المشتركة، و`format:scripts` لأدوات `scripts/` الجذرية، و`format:shared` / `lint:shared` لسطح «المشترك» الجذري (`test-servers/src/**`، `vitest.shared.mts`، و`eslint.config.js` الجذرية). شغّل `npm run format` قبل الالتزام — `format` الجذري يصلح `core/` و`scripts/` الجذرية والسطح المشترك وكل عميل؛ بينما `validate` يشغّل `format:check` غير المُصلِح ويفشل في CI عند أي ملف غير منسّق.
**الفحص اللغوي (Linting) مدرك للأنواع.** جميع نطاقات ESLint الخمسة (`clients/{web,cli,tui,launcher}` بالإضافة إلى `core/` الجذرية وبوابة المشترك) تفعّل `@typescript-eslint/no-floating-promises` عند مستوى `error`، لذا فإن أي promise لا يُنتظَر ولا يُعاد ولا يُنهى بـ `.catch(…)` ولا يُتخلص منه صراحةً بـ `void` يفشل في `lint` — وبالتالي في `validate` ([#1959](https://github.com/modelcontextprotocol/inspector/issues/1959)). تحتاج القاعدة إلى معلومات الأنواع، لذلك يسمي إعداد كل نطاق مشروع محلل؛ نطاق الجذر هو **`tsconfig.lint.json`**، وهو مشروع مخصص للفحص اللغوي فقط يغطي `core/**` و`test-servers/src/**` و`vitest.shared.mts`، التي لا تملك tsconfig خاصًا بها. لا يُنتج شيئًا ولا يغير أي فحص أنواع — لكن أي موقع TypeScript جديد من الطرف الأول يُضاف إلى نطاق lint الجذري يجب إضافته إلى `include` الخاص به. راجع **تعليمات TypeScript** في [`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md) لمعرفة متى يكون `void` مقبولًا.
للقواعد الكاملة للاختبار — بوابة ≥90% لكل ملف، وأماكن وجود ملفات الاختبار، ومشاريع الوحدة مقابل التكامل مقابل Storybook، وسياسة `v8 ignore` — راجع [`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md).
## النشر
تُوزَّع حزمة الجذر `@modelcontextprotocol/inspector` **كملف tarball واحد برقم إصدار واحد** — لا توجد حزم منفصلة `-web` / `-cli` / `-tui` / `-core`. يبني `npm run build` كل عميل، ثم يعمل `prepack` قبل `npm publish`. تُعلن اعتماديات وقت التشغيل في `package.json` الجذرية؛ وتربط بنيات العملاء `@inspector/core` وتُخرج حزم npm المُحلَّة من التثبيت الجذري كحزم خارجية.
### ما يُوزَّع، وثوابت التعبئة
قائمة `"files"` المسموحة في `package.json` الجذرية هي مصدر الحقيقة لملف tarball. توجد بعض الإدخالات غير الواضحة لأنها تُقرأ **وقت التشغيل** أو أُسقطت بصمت بواسطة packlist الخاصة بـ npm — لا تزلها دون إعادة تشغيل `npm run pack:verify`:
- **لا توجد خرائط مصدر.** تضبط أدوات البناء الخاصة بالعملاء `sourcemap: false` (`clients/{cli,tui}/tsup.config.ts`, `clients/web/tsup.runner.config.ts`)؛ بينما لا يُصدر Vite و`tsc` الخاص بالمشغِّل أي خرائط أصلًا. تشكّل الخرائط حوالي نصف الحجم بعد فك الضغط وليست مطلوبة وقت التشغيل — صحّح الأخطاء عبر `npm run dev` على المصدر.
- **`clients/web/build` يُوزَّع عبر `clients/web/.npmignore`.** يسرد `clients/web/.gitignore` `build/`، وتلتزم packlist الخاصة بـ npm بذلك `.gitignore` المتداخل على حساب قائمة `"files"` الجذرية — لذلك كان مشغّل خادم الويب الإنتاجي مفقودًا بصمت من tarball بينما تسلل `clients/web/dist` (ملف `.gitignore` الخاص به يسرد `dist-ssr` فقط). يتجاوز `clients/web/.npmignore` ملف `.gitignore` للنشر بحيث يُوزَّع كل من `build/` (المشغّل) و`dist/` (SPA). لا يحتاج العملاء الآخرون إلى هذا — لا يوزع أي منهم `.gitignore` متداخلًا.
- **`clients/web/static` يوزع وسيط الصندوق الرملي لتطبيقات MCP.** `clients/web/static/sandbox_proxy.html` ملف مصدري مُلتزم (ليس ناتج بناء)، يُقرأ من القرص وقت التشغيل بواسطة `clients/web/server/sandbox-controller.ts` بصفته `<runner dir>/../static/sandbox_proxy.html`. كان مفقودًا تمامًا من قائمة `"files"` الجذرية، لذلك فشلت كل بنية منشورة في تبويب Apps برسالة **"Sandbox not loaded"** ([#1859](https://github.com/modelcontextprotocol/inspector/issues/1859)) بينما كان يعمل جيدًا في المستودع. ولأن المسار يُحل _نسبيًا إلى_ `clients/web/build`، يجب أن يُوزَّع الدليل في ذلك الموقع بالضبط — يتحقق `pack:verify` من كل من إدخال tarball والمسار المثبت على القرص.
- **الاعتماد الذي يعرض React يُدمج، لا يُخرَج كخارجي.** الحزمة المُخرَجة تحل `react` الخاص بها من أي مكان وضعه npm **إياها** في شجرة المستهلك، وهو ليس بالضرورة المكان الذي يحل فيه الرابط نسختنا — يضع npm الحزمة بجانب React يلبي نطاق peer *الخاص بها*، وتلك النطاقات أوسع من نطاقاتنا. يصرح `ink-form` و`ink-scroll-view` بـ `">=18"`، لذا فإن مشروعًا يحمل React 18 يلبيها ويجعلها تُرفع (hoisted) بينما تتعشش React 19 الخاصة بالمفتش تحتها: نسختان من React، ويموت TUI مع `TypeError: Cannot read properties of null (reading 'useState')` لحظة تركيب نموذج اختبار أداة أو عرض تمرير ([#1952](https://github.com/modelcontextprotocol/inspector/issues/1952)). لذلك يُدمج كلاهما داخل `clients/tui/tsup.config.ts` وليسا **اعتمادين جذريين**: يوزع tarball الكود الخاص بهما داخل `clients/tui/build/index.js` بدلًا من جعل المستهلكين يثبّتونها. كما يثبّت الدمج اعتمادياتهما العابرة على ما حلّه تثبيت هذا المستودع (بشكل ملحوظ `ink-select-input@6` عبر `overrides`، التي يتجاهلها npm للحزمة المثبتة باعتبارها اعتمادًا). **`ink` هو الاستثناء الوحيد، بسبب التكلفة:** دمجه يعمل لكنه يضيف حوالي 1.4 ميغابايت (`react-reconciler` و`yoga-layout` يأتيان معه، بالإضافة إلى لافتة `createRequire` لـ CJS المدمج)، لذا يبقى خارجيًا — *ليس* لأن peer الخاص به `">=19"` يجعله آمنًا، فهو ليس كذلك. ما يجعل ذلك محتملًا هو نطاق `react` الجذري: `"^19.0.0"` مفتوح عمدًا للنسخة الرئيسية بأكملها حتى يتمكن npm من إزالة ازدواجية React الخاصة بنا مع أي React 19 يثبّته المستهلك، تاركًا `ink` خارجيًا على نفس النسخة التي يستخدمها الرابط. **تضييق هذا النطاق يعيد فتح الخطأ للمُصيّر نفسه** — `clients/tui/__tests__/tsupConfig.test.ts` يثبّته على الحد الأدنى لـ peer الخاص بـ `ink`، ويحرس بقية التقسيم؛ انظر [README الخاص بـ TUI](https://github.com/modelcontextprotocol/inspector/blob/HEAD/clients/tui/README.md#bundling-react-rendering-dependencies-must-be-inlined-1952).
- **رقم إصدار واحد، يُقرأ من `package.json` الجذرية.** يُوزَّع المفتش كحزمة واحدة بإصدار واحد، لذا تحمل **الجذر** `package.json` فقط `version` — بينما لا تحمل الأربعة `clients/*/package.json` أي إصدار عمدًا. يحل كل عميل Node (CLI وTUI وواجهة web الخلفية) الإصدار عبر قارئ `readInspectorVersion()` المشترك في `core/node/version.ts`، الذي يصعد إلى ملف الجذر (الموجود دائمًا في tarball). لا يُقرأ أي `package.json` للعملاء وقت التشغيل، لذا لا يحتاج أي منها إلى التوزيع. لا يمكن لمتصفح **web** قراءة نظام الملفات؛ يحصل على إصداره من الواجهة الخلفية عبر `GET /api/config` (انظر [#1639](https://github.com/modelcontextprotocol/inspector/issues/1639)).
### `npm run pack:verify` — اختبار دخان للنشر ضد ملف tarball الحقيقيتعمل نصوص `smoke:*` على شجرة البناء داخل المستودع، وهي **ليست** الحزمة المنشورة. يسدّ `npm run pack:verify` (`scripts/pack-and-verify.mjs`) هذه الفجوة: إذ يبني المشروع، وينفّذ `npm pack` للحزمة القابلة للنشر (مؤكدًا عدم تضمين خرائط المصدر ووجود الملفات المطلوبة وقت التشغيل)، ويثبّت الحزمة المضغوطة في **مستهلك نظيف مؤقت** — دليل مؤقت جديد حيث ينفّذ عملية `npm install <tgz>` حقيقية (يسحب التبعيات وقت التشغيل ويشغّل `postinstall`)، تمامًا كما يفعل `npx @modelcontextprotocol/inspector` — ثم يقود الملف التنفيذي `mcp-inspector` المثبّت من البداية إلى النهاية: تنفيذ `--help`، واستدعاء حقيقي لـ `--cli tools/list` عبر stdio، وإقلاع إنتاجي `--web` يجب أن يخدم `/` من `dist` المُشحون. يلتقط هذا فشل المسار/التغليف من نوع "يعمل في `--dev`، لكنه يتعطل تحت `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، لذلك لا توجد `publish-all`/`--workspaces` بأسلوب v1)، مع توثيق منشأ موقّع عبر 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 بمفرده ينشئ وسمًا أيضًا، وسيقع الوسم على commit في v2/main — لكن يجب أن يُنشأ الإصدار من main، لذا يجب أن يشير الوسم إلى commit الدمج هناك (الخطوة 3). إن إنشاء وسم هنا يُنشئ وسمًا على commit لا يُطرح أبدًا.
2. ادمج v2/main → main عبر فرع دمج المراحل المعتاد. وهو الآن يحمل زيادة رقم الإصدار، لذا يستقر الإصدار على main والنسخة صحيحة بالفعل.
بين الخطوتين 1 و2، يختلف الفرعان فعلًا، وهذا متوقع وليس انحرافًا: يقرأ v2/main النسخة التي يتم بناؤها بينما يقرأ main النسخة المُصدَرة حاليًا. ما يزيله هذا الترتيب هو الانحراف بعد الإصدار — بمجرد اكتمال دمج المراحل يعودان متطابقين، ولا يُترك v2/main أبدًا خلف main. إذا رأيت v2/main متقدمًا على main، فهناك إصدار قيد التنفيذ؛ وإذا رأيته خلفه، فهناك خطأ ما.
3. ضع وسمًا على commit في main وأعدّ مسودة الإصدار:```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) التي قمت بضبطها، لذا فإن وجود `main` محلي مختلف يمكن أن يُنتج أو يُعيد تشغيل التزامات محلية بهدوء. تعليم `HEAD` هنا يعني تعليم التزام غير موجود على `origin/main`، و`git push origin <tag>` يدفع الوسم فقط — تاركًا إصدارًا لم يُنشر التزامه أبدًا. إن تسمية `origin/main` صراحةً تجعل الالتزام المعلَّم مطابقًا تمامًا لما يشير إليه الفرع البعيد، بغض النظر عن الحالة المحلية.
⚠️ **لا بادئة `v`.** وسوم الإصدارات في هذا المستودع هي `x.y.z` مجردة — `2.2.0`، `2.1.0`، `2.0.0` — لذا علّم `2.3.0` وليس `v2.3.0`. لاحظ أن `tag-version-prefix` الخاص بـ npm يستخدم `v` افتراضيًا ولا يضع المستودع أي `.npmrc`، لذا فإن `npm version` البسيط كان سينتج وسمًا بادئته `v` لا يتطابق مع القاعدة. التعليم اليدوي (الخطوة 3) هو ما يُبقي الأمر صحيحًا. خطوة التأكيد في سير العمل تزيل بادئة `v` قبل المقارنة، لذا فإن وسمًا بادئته `v` سيُنشر مع ذلك — لكنه سيكون غير متسق مع كل إصدار سابق.
الالتزام المستهدف للإصدار هو الذي يحدد أي سير عمل سيُشغَّل، لذلك لا يحدث النشر إلا عند اقتطاع إصدار من التزام يحمل سير العمل هذا (v2).
**لماذا تُنفَّذ زيادة الإصدار على `v2/main` أولاً ([#2010](https://github.com/modelcontextprotocol/inspector/issues/2010)).** كانت الزيادة تحدث سابقًا على فرع دمج المراحل (milestone-merge branch)، وهو فرع يُقتطع من `main` — لذا كانت الزيادة موجودة فقط *في اتجاه المصب (downstream)* من `v2/main` ولم يُعدها أي شيء إلى الوراء. بقي `v2/main` عند `2.0.0` طوال إصدارَي 2.1.0 و2.2.0. وهذه ليست مسألة شكلية: الفرع المقتطع من فرع دمج المراحل يحمل الزيادة بصمت إلى طلب سحب غير ذي صلة (وهذا ما حدث في [#2009](https://github.com/modelcontextprotocol/inspector/issues/2009)، حيث وصل إصلاح خطأ في الحاوية مع فرق `2.0.0 → 2.2.0`)، وأي شيء يقرأ الإصدار أثناء التطوير — `readInspectorVersion()`، `--version`، `GET /api/config` — كان يعرض إصدارًا قديمًا بإصدارين.
**لا** «تُصلح» انحرافًا مستقبليًا بدمج `main` مرة أخرى داخل `v2/main`. يحمل `main` كامل تاريخ v1 قبل v2 (المُحتفَظ به عبر `ec5d8e13 chore: replace main's tree with v2` — ~230 التزامًا لا يمتلكها `v2/main`)، لذا فإن الدمج الرجعي (back-merge) يضمّن كل ذلك في سجل فرع التطوير بشكل دائم لتسليم تغيير من ملفين. القيام بالزيادة أولاً يعني عدم وجود ما يلزم دمجه رجعيًا.
### Docker
يتم نشر صورة حاوية على GHCR (`ghcr.io/modelcontextprotocol/inspector`، `linux/amd64` + `linux/arm64`) بواسطة سير عمل الإصدار. ملف [`Dockerfile`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/Dockerfile) هو بناء من مرحلتين: المرحلة الأولى تثبّت وتنفّذ `npm pack` على الأرشيف (tarball) القابل للنشر؛ والمرحلة الثانية تُشغّل `npm install -g` على ذلك الأرشيف، لذلك تحتوي الصورة على نفس القطعة الأثرية (artifact) تمامًا التي ينشرها npm، مع أمر `mcp-inspector` نظيف.```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
هل تستخدم تبويب التطبيقات؟ انشر 6275 أيضًا. بيئة MCP Apps المعزولة هي مستمع ثانٍ يصل إليه المتصفح مباشرةً على MCP_SANDBOX_PORT (الافتراضي 6275). لا يحتاجها أي شيء آخر، لذا فإن أوامر المنفذ الواحد أعلاه كافية للفحص العادي — لكن تبويب التطبيقات يعرض أداة فارغة بدونها:```bash
docker run --rm -p 127.0.0.1:6274:6274 -p 127.0.0.1:6275:6275
ghcr.io/modelcontextprotocol/inspector
انشره على **نفس رقم المنفذ** من الداخل والخارج. يتم تسليم عنوان URL للـ sandbox إلى المتصفح عبر `/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` المجرد ينشر على **كل واجهات المضيف**، مما يضع الـ Inspector على شبكتك المحلية. إن `HOST=0.0.0.0` في الحاوية هو أمر منفصل — فهو يتحكم في واجهات _الحاوية_ وليس واجهات المضيف — لذا فإن خيار الاشتراك `DANGEROUSLY_BIND_ALL_INTERFACES` الذي يحرس الربط الجامح خارج الحاوية لا يغطي هذا الأمر. إنه أكثر أهمية هنا من تطبيق ويب عادي: حيث تولّد الواجهة الخلفية عمليات عند الطلب، و`GET /` يدمج رمز الـ API في HTML المُرسَل، والطلب الذي يصل **بدون** ترويسة `Origin` يتجاوز قائمة السماح للأصول تمامًا — لذا بالنسبة لأي عميل غير متصفح، يكون رمز الـ API هو الحارس الوحيد. النشر على نطاق أوسع يتطلب حدًا حقيقيًا للتحكم في الوصول أمام الـ Inspector — وكيل عكسي يقوم بالمصادقة، أو نفق SSH، أو شبكة خاصة. تعيين رمز `MCP_INSPECTOR_API_TOKEN` الخاص بك **لا** يُغني عن ذلك: `GET /` يكشف أي رمز قيد الاستخدام، لذا فإن الرمز المخصص يُلتقط بنفس سهولة التقاط الرمز المُولَّد.
**الاحتفاظ بالخوادم التي تضيفها.** يحفظ الـ Inspector قائمة الخوادم الخاصة بك في `$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 والحالة المخزنة، بحيث يبقى الخادم المصرح به مصرحًا عبر عمليات التشغيل. استخدم -e MCP_CATALOG_PATH=/some/other/path.json لوضع الكتالوج في مكان آخر — وقم بتركيب مجلد يغطي أي دليل تشير إليه. إذا قمت بتركيب دليل مضيف (bind-mount) بدلاً من مجلد مسمى (-v "$PWD/inspector-data:/home/node/.mcp-inspector")، يحتفظ الدليل بملكية المضيف، لذا على لينكس أضف --user "$(id -u):$(id -g)" أو نفّذ chown عليه إلى uid 1000 — وإلا فلن يتمكن المستخدم node غير الجذر من الكتابة وسيفشل إضافة خادم مع EACCES.
هل تقوم بالترقية من صورة قبل هذا الإصلاح؟ الصور الأقدم لم تنشئ /home/node/.mcp-inspector، لذا أنشأ Docker نقطة تركيب المجلد كـ 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
تعتمد الصورة افتراضيًا على `--web` المرتبط بـ `0.0.0.0:6274` مع تعطيل الفتح التلقائي للمتصفح؛ تجاوز الوسائط لتشغيل وضع آخر (`docker run --rm ghcr.io/modelcontextprotocol/inspector --cli …`). مرّر `-e MCP_INSPECTOR_API_TOKEN=…` لتعيين رمز معروف (وإلا فسيُولَّد رمز ويُطبع في السجلات)، أو `-e DANGEROUSLY_OMIT_AUTH=true` لتعطيل المصادقة. يُرفض ربط `0.0.0.0` (جميع واجهات الشبكة) افتراضيًا خارج الحاوية — لأنه يعرّض الواجهة الخلفية التي تولّد العمليات للشبكة المحلية — لذا تختار الصورة ذلك صراحةً عبر `DANGEROUSLY_BIND_ALL_INTERFACES=true` (المُعيّنة مسبقًا في `Dockerfile`)؛ و`HOST=0.0.0.0` وحده دون تلك العلامة يخرج بخطأ. إذا **أعدت تعيين المنفذ المنشور** (`-p 127.0.0.1:8080:6274`)، فلن يعد أصل المتصفح (`http://localhost:8080`) مطابقًا لمنفذ الحاوية، لذا عيّن `-e ALLOWED_ORIGINS=http://localhost:8080,http://127.0.0.1:8080` (أو شغّل `-e CLIENT_PORT=8080 -p 127.0.0.1:8080:8080`) وإلا فستُرفض الاتصالات برمز 403. يعمل `ALLOWED_ORIGINS` على **استبدال** القائمة الافتراضية بدلًا من دمجها، لذا اذكر كل صيغة loopback ستتصفح منها (انظر [web README](https://github.com/modelcontextprotocol/inspector/blob/HEAD/clients/web/README.md#host-binding--the-origin-allow-list)). تعمل الصورة كمستخدم `node` غير الجذر ولديها `HEALTHCHECK` يفحص واجهة الويب — ويفترض وضع `--web` الافتراضي، لذا أضف `--no-healthcheck` عند تشغيل `--cli`/`--tui` (اللذين لا يملكان خادم ويب).
## المساهمة — `AGENTS.md` و`CLAUDE.md`
**[`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md) هو العقد الخاص بتغيير قاعدة الكود هذه، وينطبق على البشر ووكلاء الذكاء الاصطناعي على حدٍّ سواء.** إنه ليس مجرد قالب جاهز خاص بالوكلاء — بل يحمل الاتفاقيات الفعلية للمشروع: سير عمل المشكلات واللوحة، وقواعد الفروع/الوسوم، ومعايير TypeScript وMantine/React، ومتطلبات الاختبار والتغطية، وبوابة ما قبل الدفع الإلزامية (pre-push). اقرأه قبل إجراء أي تغييرات، وحافظ على تحديثه عندما تغيّر البنية أو الأدوات أو القواعد.
`CLAUDE.md` هو نقطة الدخول التي يحمّلها وكيل [Claude Code](https://claude.com/claude-code) تلقائيًا؛ وهو ببساطة يتضمن `AGENTS.md` وREADME هذا، بحيث يعمل الوكلاء والبشر من مصدر الحقيقة نفسه. إذا كنت تستخدم وكيلًا مختلفًا يقرأ `AGENTS.md`، فستحصل على القواعد نفسها.
قاعدة رئيسية تستحق الإبراز هنا: **كل العمل مدفوع بالمشكلات (issue-driven).** قبل البدء، ابحث عن مشكلة تتبّع على لوحة مشروع v2 أو أنشئها؛ وافتح طلبات السحب (PRs) على `v2/main` مع `Closes #<issue>`. الوصفات الدقيقة (الوسوم، معرّفات اللوحة، الحالات) موجودة في `AGENTS.md`.
## الترخيص
MIT.
| الإعداد | يوضح | المشكلة |
|---|
mcp-app-http.json (حقبة قديمة) | تطبيق MCP (مورد واجهة مستخدم + أداة تطبيق) في تبويب Apps | #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: وسائط قابلة للـ null (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 |
| الإعداد المسبق | السلوك |
|---|
mrtr_confirm | جولة واحدة |
mrtr_two_step | جولتا استدعاء عبر requestState |
mrtr_sample | أخذ عينات مضمّن → لوحة Sampling |
mrtr_roots | roots/list مضمّن، يُجاب عنه تلقائيًا بصمت من الجذور المكوّنة (بدون نافذة) |
mrtr_edge | جولة inputRequests فقط، ثم جولة requestState فقط |
mrtr_empty | يكتمل بنتيجة فارغة — بدون content وبدون structuredContent |
mrtr_loop | لا يكتمل أبدًا → يصل إلى حد MRTR_MAX_ROUNDS |
| الأداة | الاستجابة |
|---|
trigger_header_mismatch | 400 / -32020 |
trigger_missing_capability | 400 / -32021 |
trigger_unsupported_version | 400 / -32022 (مع data.supported) |
trigger_method_not_found | 404 / -32601 |
| الشكل | سلوك SDK |
|---|
{a,b} | يدمج القيم خام — بدون ترميز، وتُسقط بادئة العامل |
{;id} | ; مفقودة من قائمة العوامل لديه، لذا يُحلل المتغير كـ ;id |
{id:3} | يُدمج معدِّل البادئة في الاسم، منتجًا id:3 |
{+v} / {#v} | يشوّه encodeURI الرموز المحجوزة [/] ([::1] → %5B::1%5D) ويعيد ترميز الثلاثيات المئوية (%2F → %252F) |
{v} | يترك encodeURIComponent الفواصل الفرعية !'()* عارية، بينما يتطلب RFC 6570 ترميزها |