Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
inspector — Untersuchen, debuggen und visuell testen Sie Model Context Protocol (MCP)-Server über eine Web-UI, CLI oder TUI, mit Tool-/Ressourcen-Exploration, Anfrageprotokollierung und OAuth-Unterstützung. | Kitploit
Tools/GitHubGitHub/modelcontextprotocol/inspector
Scripting & AutomatisierungDebuggerDienstprogramme & FrameworksAuthentifizierung
GitHubmodelcontextprotocol/inspector

inspector

Untersuchen, debuggen und visuell testen Sie Model Context Protocol (MCP)-Server über eine Web-UI, CLI oder TUI, mit Tool-/Ressourcen-Exploration, Anfrageprotokollierung und OAuth-Unterstützung.

Repository anzeigen

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen
Webseite
10.7k1.5kvor 20h 29mVon Kitploit geprüft

MCP Inspector

Ein Entwicklerwerkzeug zum Untersuchen von Model Context Protocol (MCP)-Servern. Es wird als ein einziges Paket, @modelcontextprotocol/inspector, ausgeliefert, das drei Möglichkeiten bietet, einen Server zu untersuchen:

  • Web — eine Vite + React + Mantine Single-Page-App mit einem Node-Backend.
  • CLI — ein skriptbarer Kommandozeilen-Client für Automatisierung, CI und schnelle Agenten-Feedbackschleifen.
  • TUI — eine interaktive Terminal-UI, erstellt mit Ink.

Alle drei laufen über eine einzige globale mcp-inspector-Binärdatei:```bash npx @modelcontextprotocol/inspector # web UI (default) npx @modelcontextprotocol/inspector --cli # CLI npx @modelcontextprotocol/inspector --tui # TUI

root@kitploit:~
> **Upgrade von v1?** Lies den [v1 → v2-Migrationsleitfaden](https://github.com/modelcontextprotocol/inspector/blob/HEAD/docs/v1-to-v2-migration.md) — CLI-Flags, die neue Aufteilung zwischen `--config` und `--catalog`, das Node-Engine-Upgrade und was nicht mehr mitgeliefert wird.

> **Repo-Status.** Dies ist die **v2**-Linie des Inspectors. Die aktive Entwicklung findet auf **`v2/main`** statt (dem Develop-Branch — alle v2-PRs zielen darauf), der bei Meilenstein-Releases in **`main`** zusammengeführt wird; `main` ist der Standard-Branch und enthält die neueste veröffentlichte v2-Version, die auf den npm-Tag `latest` veröffentlicht wird. Die Legacy-**v1**-Linie lebt auf **`v1/main`** weiter — nur Sicherheitskorrekturen, die direkt von diesem Branch auf den npm-Tag `v1-latest` veröffentlicht werden (`npx @modelcontextprotocol/inspector@v1-latest`). Siehe [`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md) für Branch-/Board-Konventionen.

## Projektstruktur

v2 ist **kein** npm-Workspace. Jeder Client unter `clients/*` besitzt sein eigenes `package.json` und `node_modules`; gemeinsam genutzter Code liegt in `core/` und wird über einen `@inspector/core`-Build-Zeit-Alias konsumiert (ohne eigenes `package.json`). Ein einziges `npm install` im Root löst kaskadenartig Installationen in jedem Client aus (siehe [Setup](#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

Jeder Client hat ein eigenes README mit clientspezifischen Details: web · cli · tui · launcher.

Aufgabenorientierte Leitfäden findest du unter docs/:

  • Migration von v1 zu v2 — die Zuordnung von v1 → v2: CLI-Flag-Zuordnung, --config-vs.---catalog-Semantik mit Vorher/Nachher-Beispielen, die Anhebung der Node-Engine (>=22.7.5 → >=22.19.0), Umbenennungen von Umgebungsvariablen sowie die Teilpakete, die nicht mehr ausgeliefert werden.
  • MCP-Server-Konfiguration — mit welchen Servern sich der Inspector verbindet: --catalog vs. --config, Ad-hoc-Ziele, das ---Trennzeichen, das Dateiformat und seine Inspector-spezifischen Felder pro Server. Wird von allen drei Clients gemeinsam genutzt; die READMEs von cli und tui delegieren ihre Abschnitte zu Serveroptionen an dieses Dokument.
  • Prüfen einer MCP-App — das Rezept von CLI-first zu one-shot-web für die automatisierte Prüfung von App-Tools: --app-info-Sonde → Deep-Link-Navigation → gerendertes Widget, dazu OAuth-Übergabe und Proxy-Unterstützung.
  • Konsolidierung von Launcher und Konfiguration — warum der Launcher einen Client im selben Prozess ausführt, statt ihn als eigenen Prozess zu starten, und wie der gemeinsame Konfigurationsprozessor sich einfügt.

Einrichtung

Erfordert Node >=22.19.0.```bash npm install # root install; postinstall cascades into every client

root@kitploit:~
- **Frischer Klon:** `npm install` im Repository-Root ausführen.
- **Nach einem Pull, der die Abhängigkeiten eines Clients ändert:** `npm install` erneut im Root ausführen, um jeden Client neu zu synchronisieren.

Die Kaskade (`scripts/install-clients.mjs`) ist nur für die Entwicklung gedacht – sie wird früh beendet, wenn das Paket als Abhängigkeit installiert ist, und das veröffentlichte Tarball enthält nur das `build/` jedes Clients, sodass Endbenutzer nicht betroffen sind. Setzen Sie `INSPECTOR_SKIP_CLIENT_INSTALL=1`, um sie zu überspringen.

**Wo eine Abhängigkeit deklariert wird.** Die MCP-SDK-Pakete (`@modelcontextprotocol/client`, `core`, `server`, `server-legacy`, `ext-apps`) leben ausschließlich in der **Root**-`package.json` – niemals in der eines Clients. Die Node-Auflösung wandert nach oben, sodass die Root-Installation in der Kette jedes Clients liegt und das Root-Manifest bereits das ist, gegen das das veröffentlichte Tarball aufgelöst wird. Sie pro Client zu deklarieren installiert eine zweite Kopie, die von der des Roots abweichen kann – so landeten vor [#1970](https://github.com/modelcontextprotocol/inspector/issues/1970) zwei Versionen von `ext-apps` (und des transitiven v1 `@modelcontextprotocol/sdk`) im Baum – und eine zweite Kopie von `client`/`core` ist der Fehler, für den `vitest.shared.mts` einen `dedupe`-Workaround enthält. Die gleiche Root-only-Platzierung gilt für alles, was ausschließlich über Root-eigenen Code ohne eigenes Manifest erreicht wird (`test-servers/src`, `core/`), und `vitest.shared.mts` aliasiert diese auf das Repository-Root – `express` und `yaml`, beide über `test-servers/src` erreicht, sind heute die beiden. **Ob ein solches Paket eine `dependency` oder eine `devDependency` ist, ergibt sich daraus, wer es zur Laufzeit konsumiert, nicht daraus, wo es deklariert ist:** Alles, was `core/` zur Laufzeit importiert, muss eine Root-**`dependency`** sein, weil die Client-Builds npm-Pakete externalisieren und eine veröffentlichte Installation sie aus dem Root-Manifest auflöst, wo devDependencies fehlen. `express` ist nur für Tests da und eine devDependency; `yaml` steht derzeit in `dependencies`. **`vite` und `@vitejs/plugin-react` sind aus demselben Grund Root-`dependencies`, nicht aus Versehen** – sie sehen nach Build-Werkzeugen aus, aber `clients/web/server/start-vite-dev-server.ts` importiert sie zur Laufzeit für `mcp-inspector --web --dev`, und `clients/web/tsup.runner.config.ts` listet beide als `external`, sodass eine veröffentlichte Installation sie aus dem Root-Manifest auflöst. Sie in `devDependencies` zu verschieben würde `--web --dev` für Konsumenten brechen (und das bedarfsgesteuerte `vite build` in `ensure-web-build.ts`), während alle lokalen Prüfungen bestanden würden. Es bedeutet, dass sie unter `npm audit --omit=dev` auftauchen, was ein Feature ist: Sie sind wirklich im Produktionsbaum.

## Entwicklung während der Laufzeit

Für die tägliche Web-Entwicklung führen Sie Vite direkt aus dem Web-Client aus (schnelles HMR, kein Launcher-Build erforderlich):```bash
cd clients/web && npm run dev

Die unten stehenden launchergesteuerten Skripte führen den gebauten Launcher aus, baue also zuerst (npm run build):```bash npm run web # prod web launcher against clients/web/dist npm run web:dev # web launcher in --dev mode (Vite)

root@kitploit:~
## Das gemeinsame Paket `@inspector/core`

![Gemeinsame Code-Architektur: die vier Clients über dem gemeinsamen Paket @inspector/core](https://assets.kitploit.com/production/public/readmes/50997/12951c8cb492b9d753ea13b98a8ea2475ef2e250a9e849227b05c3cfc7c5d31c/962dd293aaae81c94af868f086686d56189155e63449bfc6b537818e8725c812-display-v1.webp)

`core/` enthält die Logik, die allen drei Clients gemeinsam ist, sodass sich Web, CLI und TUI identisch verhalten. Der Einstiegspunkt ist die **`InspectorClient`**-Klasse (`core/mcp/`), die die Verbindung zu einem MCP-Server, den Request/Response-Lebenszyklus und eine Reihe von State-Stores besitzt; `core/react/` stellt React-Hooks für diese Stores bereit, die sowohl der Web- als auch der TUI-(Ink-)React-Baum konsumieren. OAuth (`core/auth/`) ist in isomorphe Logik plus Browser-/Node-/Remote-Backends aufgeteilt, sodass dieselben Abläufe im Browser, in Node und gegen ein Remote-Backend funktionieren.

`core/` hat absichtlich **kein `package.json`** — es wird nicht eigenständig veröffentlicht. Jeder Client bindet es über einen `@inspector/core`-Alias ein:

- **CLI / TUI:** `esbuildOptions.alias` in ihrer jeweiligen `tsup.config.ts` mappt `@inspector/core` → das `core/`-Verzeichnis des Repos, und `noExternal: [/^@inspector\/core/]` bindet es in das Bundle ein.
- **Web:** derselbe Alias in `clients/web/vite.config.ts` für die Browser-App und den Node-Backend-Runner.

Die Veröffentlichung von `core/` als eigenes Paket (z. B. damit Dritte darauf aufbauen können) ist bewusst aufgeschoben — siehe Issue [#1636](https://github.com/modelcontextprotocol/inspector/issues/1636).

## Web-Client: „dumme Komponenten“ + Storybook

Der Web-Client v2 ist aus **präsentationalen („dummen“) Komponenten** aufgebaut — sie akzeptieren Daten und Callbacks als Props und enthalten nur Anzeigelogik, ohne direkt Daten abzurufen oder Client-State zu halten. Der State stammt aus den `@inspector/core`-Hooks, die nahe der Spitze des Baums eingebunden sind. Dadurch bleiben Komponenten isoliert, testbar und dokumentierbar.

Dieser Ansatz macht **Storybook** hier erstklassig: Jede Screen- und Element-Komponente besitzt eine `*.stories.tsx`-Datei (96+ Stories), die sie mit Fixture-Props rendert. Storybooks **Play-Funktionen** dienen zugleich als Interaktionstests und laufen headless in CI (`npm run ci:storybook`, Chromium über Playwright).

Das Styling folgt einer strikten Mantine-first-Konvention (Theme-Varianten und Komponenten-Props statt CSS-Klassen, CSS-Custom-Properties der Form `--inspector-*` statt roher Farb-Literale). Die vollständigen Regeln stehen in [`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md) unter **React-Anweisungen** — lies sie, bevor du die Web-UI anfasst. Element-Komponenten liegen in `clients/web/src/components/elements/`; Theme-Varianten in `clients/web/src/theme/`.

## Test-Server

`test-servers/` stellt **komponierbare MCP-Server** für die Integrations- und Smoke-Testsuiten bereit, sodass die Tests einen echten Server über einen echten Transport verwenden, statt auf Mocks zurückzugreifen. Ein Server wird aus **Presets** zusammengesetzt (Fixture-Fabriken in `test-servers/src/preset-registry.ts` — Tools, Resources, Prompts, Tasks, Elicitation, Sampling, OAuth, …) und kann auf zwei Arten betrieben werden:

- **Prozessintern** — importiere die Fabriken (`createTestServerHttp`, `createEchoTool`, …) und führe den Server innerhalb der Event-Loop des Tests aus (genutzt von den HTTP-Integrationspfaden).
- **Als Subprozess** — `test-servers/build/test-server-stdio.js` wird als echter stdio-Subprozess gestartet (genutzt von den CLI-Smoke-Tests und den Stdio-Integrationstests).

Konfiguriere einen Server deklarativ mit einer JSON-Konfiguration (siehe `test-servers/configs/*.json`), die Presets auswählt, und lade ihn dann über `--config`. Da die Server als echte Subprozesse gestartet werden, muss die Build-Ausgabe zuerst existieren:```bash
npm run test-servers:build   # (from clients/web) → tsc -p test-servers, emits test-servers/build/

Der Vite-Alias @modelcontextprotocol/inspector-test-server (in clients/web/vite.config.ts) zeigt auf test-servers/build/index.js, sodass getTestMcpServerPath() zu einem echten .js-Pfad aufgelöst wird.

Die moderne Protokoll-Ära bedienen

Ein Streamable-HTTP-Server kann über den createMcpHandler des SDKs auch die moderne Protokoll-Ära (2026-07-28) bedienen:

  • Setze transport.modern in der JSON-Konfiguration — true für zustandsloses Bedienen beider Ären, oder { "legacy": "reject" } für strikt moderne-only.
  • Oder übergib modern an der ServerConfig für ein prozessinternes createTestServerHttp.

Dadurch kann eine Inspector-Verbindung, die protocolEra: "auto" | "modern" aushandelt, den modernen Zweig erreichen (befülltes server/discover, sessionlos). Siehe test-servers/configs/modern-http.json.

Showcase-Konfigurationen

Jede der folgenden Konfigurationen ist ein fertiger Server, um eine Funktion von Hand zu testen. Lade eine mit --config, und verbinde dich – sofern nicht anders angegeben – mit Protokoll-Ära = Modern.

MCP-Apps

mcp-app-http.json stellt das Tool mcp_app_demo (_meta.ui.resourceUri) zusammen mit seiner UI-Ressource mcp_app_demo_widget bereit, sodass der Tab Apps eine echte App zum Rendern hat. Es ist ein einfacher Streamable-HTTP-Server – verbinde dich mit der Standard- (Legacy-) Protokoll-Ära, nicht Modern.

Öffne den Apps-Tab, wähle mcp_app_demo, gib ihm einen Titel und klicke auf App öffnen: Das Widget rendert im Sandbox-iframe und durchläuft die UI-Protokollfläche der Host-Seite – Host-Kontext-Rendering, size-changed, ui/message und eine Logzeile im Panel App-Logs. Da das Widget über die Sandbox-Proxy-Seite ausgeliefert wird, reproduziert genau diese Konfiguration auch #1859 (eine fehlende clients/web/static/sandbox_proxy.html erscheint hier als Meldung „Sandbox nicht geladen“ anstelle des Widgets) – ein Fehler, der nur in einem installierten Paket auftrat, nie im Repo.

Für die geskriptete Version desselben Ablaufs (--app-info-Probe → Deep-Link → gerendertes Widget) siehe Überprüfen einer MCP-App.

MRTR

modern-mrtr-http.json stellt das Tool mrtr_confirm (Preset mrtr_confirm, createMrtrTool) über den modernen Zweig bereit. Sein Handler gibt inputRequired(...) zurück, das eine Formular-Elicitation einbettet; der Aufruf erzeugt also einen echten Roundtrip: input_required → der Client erfüllt die eingebettete Elicitation und wiederholt mit einer neuen id → complete.

Der Inspector steuert MRTR manuell (inputRequired: { autoFulfill: false }), sodass die eingebettete Elicitation am Modal für ausstehende Anfragen (gekennzeichnet als „input_required“) wartet, bis du antwortest; danach schließt der Wiederholungsversuch ab. Nützlich, um sowohl die Pending-Request-UX als auch die MRTR-Gesprächsgruppierung in der Protokollansicht zu prüfen.

mrtr-showcase-http.json bündelt jedes MRTR-Preset in einem Server:

Führe mrtr_empty aus und beantworte seine einzelne Elicitation: Der Protokoll-Tab gruppiert den Austausch als MRTR-Konversation, die mit COMPLETE endet, und das Ergebnispanel zeigt „Leeres Ergebnis — Der Toolaufruf wurde erfolgreich abgeschlossen und hat keinen Inhalt zurückgegeben.“ In der fehlerhaften Version wurde dasselbe Ergebnis als „Noch keine Ergebnisse“ gerendert, dem Platzhalter des Panels vor der Ausführung (#1860) – ein Aufruf, dessen Erfolg der Benutzer gerade gesehen hatte, wirkte also wie ein Aufruf, der nie lief. Ein leeres content-Array ohne structuredContent ist ein zulässiges CallToolResult, und das Panel wird erst gemountet, sobald ein Ergebnis existiert; die Platzhalterformulierung konnte dort also nicht zutreffen. (Die benachbarte Hälfte derselben Lücke – ein Ergebnis, dessen Nutzdaten nur in structuredContent liegen – wurde durch #1908 geschlossen.)

Das Legacy-Preset collect_elicitation ruft server.elicitInput auf, das auf dem 2026-07-28-Zweig einen Fehler auslöst – Server→Client-Anfragen sind dort nicht erlaubt. MRTR ist der moderne Ersatz.

Netzwerk-Tab — standardisierte Header und Fehler-Taxonomie

modern-network-http.json deckt SEP-2243 / SEP-2575 ab. Es stellt ein Tool get_weather bereit, dessen Argument city eine Annotation x-mcp-header: "City" trägt, sodass ein moderner Client sie zu Mcp-Param-City spiegelt.

Es stellt außerdem vier trigger_*-Tools bereit, die der Spec-Error-Injektor des modernen Zweigs (transport.modern.injectSpecErrors: true) mit einem echten HTTP-Status plus JSON-RPC-Fehlerbody beantwortet:

Öffne den Netzwerk-Tab, um die gespiegelten Mcp-*-Header hervorgehoben, Sentinel-Werte dekodiert und jeden Fehler auf eigene Weise dargestellt zu sehen.

Die Mcp-Param-*-Spiegelung baut der Inspector, nicht das SDK. Das SDK spiegelt nur innerhalb von client.callTool() und überspringt sie im Browser (detectProbeEnvironment() !== "browser"). Der Inspector leitet tools/call über client.request(), um MRTR manuell zu steuern, und baut die gespiegelten Header daher selbst (#1846) – auf jedem Client, einschließlich Web, da die vorgelagerte Anfrage des Web-Clients vom Node-Backend und nicht vom Browser ausgeht. get_weather ist also aus Web, CLI und TUI gleichermaßen aufrufbar, sowohl in der einfachen als auch in der Form „Als Aufgabe ausführen“.

x-mcp-header im Tools-Tab

xmcpheader-modern-http.json stellt bereit:

  • echo — einfaches Tool.
  • get_weather — eine gültige x-mcp-header: "City"-Annotation an seinem Argument city.
  • invalid_header_tool — eine Annotation mit dem Headernamen "Bad Header". Das Leerzeichen macht daraus ein ungültiges RFC-9110-Token, sodass die gesamte Tooldefinition ungültig ist.
  • trigger_invalid_params — wird mit einem echten -32602 Invalid params-Fehler beantwortet, dessen Meldung nicht von einem fehlenden Tool handelt.

Öffne den Tools-Tab: Das Detailpanel von get_weather zeigt einen Abschnitt „Gespiegelte Anforderungsheader (SEP-2243)“ (city → Mcp-Param-City), und invalid_header_tool erscheint durchgestrichen unter einem Trenner „Ausgeschlossen (SEP-2243)“ mit dem Grund beim Hovern. Ein konformer Streamable-HTTP-Client MUSS es aus tools/list entfernen; der Inspector zeigt warum.

Unter SDK v2 wird ein tools/call, das mit -32602 abgelehnt wird, als eigenes Fehlerpanel statt als isError-Ergebnis dargestellt – mit der Überschrift „Unbekanntes Tool“, wenn die Meldung ein fehlendes Tool nennt, oder andernfalls „Ungültige Parameter“ (führe trigger_invalid_params aus).

Seitenweises Laden

pagination-http.json stellt 12 Tools, 12 Ressourcen und 12 Prompts bereit (Presets numbered_tools / numbered_resources / numbered_prompts, count: 12), jeweils mit einem maxPageSize von 4, sodass jede Liste in drei Seiten paginiert.

Aktiviere „Listen seitenweise laden“ (Server-Einstellungen – die Einstellung paginatedLists oder der Schalter Paginiert in einer Listenseitenleiste), und die Listen laden nur Seite 1 (4 Einträge) mit einem Steuerelement Nächste Seite laden und einem Status N Seiten geladen. Jeder Klick holt die nächsten 4 und hängt sie an; Aktualisieren setzt auf Seite 1 zurück. Ist der Schalter aus (Standard), aggregieren dieselben Listen beim Verbinden automatisch alle drei Seiten.

Strukturierte Ausgabe

structured-output-http.json stellt list_items (verschachteltes structuredContent – Objekte in Arrays in einem Objekt, die Form aus #1908), get_temp (ein flaches Drei-Key-Payload) und echo (ganz ohne outputSchema) bereit. Es ist ein einfacher Streamable-HTTP-Server – verbinde dich mit der Standard- (Legacy-) Protokoll-Ära.

Führe list_items vom Tools-Tab aus: Das Ergebnispanel zeigt die Textzusammenfassung aus content[] („Found 2 items.“) und einen einklappbaren Abschnitt Strukturierte Ausgabe, der das schema-validierte Payload als hübsch formatiertes, kopierbares JSON rendert. Genau diesen Abschnitt ließ v2 weg – ein Tool, das ein outputSchema deklariert, liefert seine eigentlichen Daten dort, und der Textblock fasst sie normalerweise nur zusammen. Führe echo aus, um zu bestätigen, dass der Abschnitt fehlt, wenn ein Ergebnis kein structuredContent enthält.

Doppelte Toolnamen

duplicate-tool-names-http.json stellt get_weather, get_temp, echo und add bereit und wiederholt dann get_weather und echo am Ende von tools/list mit demselben name und einem Titel (duplicate) (duplicateToolNames). Kein Preset kann diese Form erzeugen – registerTool des SDKs lehnt einen doppelten Namen ab – aber ein echter Server kann und tut das, und der Inspector muss es originalgetreu darstellen.

Verbinde dich (Standard-Legacy-Ära), öffne den Tools-Tab und tippe get in Tools suchen: Die Liste muss sich auf genau die drei get_*-Zeilen verengen. In der fehlerhaften Version blieb eine veraltete echo-Zeile erhalten, weil die Seitenleiste Zeilen nur nach tool.name schlüsselte und die kollidierenden Schlüssel während der Reconciliation ein Kind verwaist zurückließen (#1957).

Die duplizierten Kopien werden absichtlich angehängt und nicht neben ihr Gegenstück gesetzt. React gleicht zuerst eine führende Folge von Kindern mit demselben Schlüssel ab; ein direkt an der Spitze platziertes Duplikat würde also zufällig zusammenpassen und den Fehler verbergen. Das Trennen des Paars macht ihn sichtbar – und es ist auch die realistische Form: zwei konkatenierte Tool-Quellen.

Nullable-Argumente

nullable-fields-http.json stellt record_shipment bereit, dessen vier Argumente jeweils mit Zod’s .nullish() deklariert sind – „optional und explizit nullable“. Das kompiliert zu anyOf: [<branch>, { "type": "null" }], sodass der eigentliche Typ (und beim Enum dessen enum-Liste) auf einem Zweig sitzt statt auf der obersten Ebene. Daneben liegt get_temp mit einem einfachen, nicht-nullbaren units-Enum zum Vergleich. Einfacher Streamable-HTTP – verbinde dich mit der Standard- (Legacy-) Protokoll-Ära.

Öffne den Tools-Tab und wähle record_shipment: direction muss als Select (envio / recebimento) mit einem Löschen-Button erscheinen, der es auf null zurücksetzt, reference als Texteingabe, quantity als Zahleneingabe und express als Checkbox. In der fehlerhaften Version fielen alle in das Raw-JSON-Textarea, das bei jedem Tastendruck seinen eigenen Inhalt erneut escapete, bis der Wert unbrauchbar war (#1928). Das Tool gibt die empfangenen Argumente zurück, sodass das Ergebnispanel genau zeigt, was gesendet wurde.

Die TUI hatte dieselbe Lücke und lohnt einen Check gegen denselben Server (--tui, dann record_shipment testen): direction ist ein Select, quantity ein Integer-Feld, express ein Boolean. Beide Clients teilen sich jetzt einen gemeinsamen Reduktionsschritt – normalizeNullableUnion in core/json/nullableUnion.ts – genau damit sie bei der Frage, welche Schemas sie rendern können, nicht auseinanderdriften.

RFC-6570-Ressourcentemplates

rfc6570-templates-http.json stellt zwei Ressourcentemplates direkt aus #1919 bereit – events_by_topic (foobar://events/{topic}) und events_by_query (foobar://events{?topic}) –, die jeweils die URI zurückspiegeln, gegen die sie gematcht wurden, plus eine einfache Ressource foobar://events (siehe unten). Einfacher Streamable-HTTP; verbinde dich mit der Standard- (Legacy-) Protokoll-Ära.

Öffne den Ressourcen-Tab und wähle events_by_topic, gib dann foo/bar ein. Die Anfrage muss als foobar://events/foo%2Fbar rausgehen, und das Ergebnis spiegelt die URI zurück, die der Server gematcht hat. In der fehlerhaften Version wurde der Wert roh eingefügt, sodass der Schrägstrich ein zweites Pfadsegment erzeugte und der Matcher des SDKs mit -32602 Resource not found: foobar://events/foo/bar antwortete – genau der Fehler aus dem Issue. Dasselbe gilt für ?, #, %, Leerzeichen und Nicht-ASCII-Text.

events_by_query ist die Hälfte, die unsichtbar war: Der alte Scan /\{(\w+)\}/g konnte keinen Ausdruck mit Operator erkennen, daher wurde gar kein topic-Eingabefeld gerendert. Jetzt erscheint es, markiert als Optional – RFC 6570 verwirft den gesamten Ausdruck, wenn die Variable undefiniert ist; ein Lesen mit leerem Feld fragt also foobar://events an, und ein Ausfüllen fragt foobar://events?topic=foo%2Fbar an. Die URI-Vorschau neben dem Titel zeigt beim Tippen die teilexpandierte Form und lässt unausgefüllte Ausdrücke unverändert stehen.

Die einfache Ressource foobar://events ist bewusst registriert, nicht als Füllmaterial. UriTemplate.match() des SDKs kompiliert {?topic} zu einem erforderlichen \?topic=([^&]+), daher kann ein Template allein das leere Lesen nicht bedienen – match("foobar://events") gibt null zurück. Ein echter Server stellt die ungefilterte Sammlung als eigene Ressource bereit; die Showcase-Konfiguration tut dasselbe, damit dieser Schritt tatsächlich aufgelöst wird.

Der Web-Client und die TUI expandieren über einen gemeinsamen Helfer, core/mcp/uriTemplate.ts – das Web-Formular für Ressourcen direkt, die TUI über InspectorClient.readResourceFromTemplate – und beide leiten auch ihre Formularfelder aus dessen Parser ab; genau das macht die gemeinsame Nutzung real: Ein Formular übermittelt Werte unter den Namen, die es gerendert hat. Ein Parser, der einen Namen verstümmelt, verwirft den Wert beim Expandieren also stillschweigend. (Der CLI ist kein Konsument: Er hat kein Template-Formular, und sein resources/read reicht das bereits expandierte --uri direkt durch.)

Das UriTemplate des SDKs wird weiterhin verwendet, aber nur, um ein Template zu validieren (das Konstruieren ist es, was einen ungeschlossenen Ausdruck ablehnt). Sein Expander wird nicht verwendet, weil er in fünf Punkten unvollständig ist – jeweils gemessen am festgepinnten SDK, nicht vermutet:

Die Zeilen ; und :3 sind die, die ein Benutzer direkt sieht: Beim Parsen durch das SDK rendert das Formular Felder, die wörtlich ;id und id:3 heißen. Die Zeile +/# ist stille Korruption statt Über-Escaping – ein IPv6-Literal oder ein bereits kodierter Pfad kommt verändert am Server an.

Ein Template, das überhaupt nicht expandiert werden kann – ein grammatikfremder Modifikator ({id:abc}) oder ein Ausdruck ohne Variable ({}, {a,}, {?}) – verweigert das Lesen, statt irgendetwas zu senden. Wähle events_malformed (foobar://events/{topic:abc}), um es zu sehen: Ressource lesen ist deaktiviert, der Grund wird unter dem Formular ausgegeben, und die Vorschau zeigt das Template so, wie der Server es veröffentlicht hat. Die Alternative ist schlimmer, als sie aussieht: x://{} würde sonst zu x:// expandieren, ohne dass Eingaben gerendert werden; die Prüfung „alles Erforderliche ist ausgefüllt“ des Formulars würde also ins Leere laufen, und es würde eine URI lesen, die nicht das vom Server veröffentlichte Template ist.

Literale werden beim Expandieren ebenfalls pct-kodiert (RFC 6570 §3.1): café/{var} sendet caf%C3%A9/value, kein rohes UTF-8 im Pfad – etwas, das der Expander des SDKs ebenfalls nicht tut. Und die Namen, die ein Template verwenden darf, sind RFC 6570s varchar plus eine gekennzeichnete Toleranz für - und ~: Die Konformitätssuite lehnt {default-graph-uri} ab, aber echte Server veröffentlichen solche Namen, und der Matcher des SDKs verarbeitet sie verlustfrei. Der Inspector expandiert sie daher und markiert die Variable mit conforming: false, statt eine Ressource zu verweigern, die nachweislich funktioniert.

Eine undefinierte Variable ist es, die ihren Ausdruck weglässt – eine als leere Zeichenkette definierte Variable expandiert (x{?q} ergibt x?q=, x{;q} ergibt x;q, gemäß RFC 6570 §3.2.7). Der Expander respektiert diese Unterscheidung, sodass ein Aufrufer wie readResourceFromTemplate beide URIs anfordern kann. Beides gleichzusetzen ist eine Formular-Angelegenheit, keine Template-Frage: Beide Clients initialisieren jede deklarierte Variable mit "", und eine Texteingabe kann „definiert, aber leer“ nicht ausdrücken; daher entfernt jedes Formular seine Leerwerte (definedValues) beim Erzeugen der Anfrage.

Erforderlichkeit ist eine Eigenschaft des Ausdrucks, nicht der Variable: RFC 6570 lässt undefinierte Namen aus einem Ausdruck mit mehreren Namen weg, daher ist {a,b} mit nur ausgefülltem a expandierbar, und ein Formular darf es nicht blockieren. requiredGroups liefert einen Eintrag pro nicht weglassbarem Ausdruck, und hasRequiredValues verlangt, dass jeder durch einen beliebigen seiner Namen erfüllt wird – was kein Pro-Variable-Flag ausdrücken kann, sobald ein Name in mehreren Ausdrücken vorkommt ({a,b}{a,c} ist erfüllt, indem b und c ausgefüllt werden).#### Angekündigte Erweiterungen

advertised-extensions-http.json bedient echo (immer) und ein get_weather-Tool, das über die io.modelcontextprotocol/tasks-Erweiterung gesteuert wird (extensionGatedTools): das Tool wird registriert, startet aber deaktiviert, und der Server aktiviert es bei notifications/initialized nur dann, wenn der Client diese Erweiterung in seinen capabilities.extensions angekündigt hat.

  1. Verbinden — der Inspector kündigt die Tasks-Erweiterung standardmäßig an, daher zeigt die Tool-Liste sowohl echo als auch get_weather.
  2. Öffnen Sie Server-Einstellungen → Angekündigte Erweiterungen, deaktivieren Sie Tasks (io.modelcontextprotocol/tasks) und verbinden Sie sich erneut.
  3. Der Client kündigt nun keine Erweiterungen mehr an, der Server aktiviert get_weather nie, und die Tool-Liste zeigt nur echo.

Das ist die Debugging-Stellschraube für einen Server, der die Tool-Registrierung legitimerweise basierend auf den Ankündigungen des Clients ändert. Nur der zustandsbehaftete Legacy-Zweig — der moderne Pro-Request-Zweig hat kein persistentes oninitialized.

Logging, beide Ären

logging-legacy-http.json und logging-modern-http.json bedienen beide logging: true sowie ein send_notification-Tool, das ein notifications/message auf einer gewählten Ebene ausgibt. Die Legacy-Variante ist ein einfacher Streamable-HTTP-Server; die moderne Variante setzt transport.modern: true.

  • Legacy — der Logs-Tab bietet einen sitzungsbezogenen Selektor Aktive Ebene festlegen plus Schaltfläche Festlegen. Ein Aufruf von send_notification streamt das Log in das Panel.
  • Modern — derselbe Tab zeigt stattdessen Log-Ebene pro Request. Wählen Sie eine Ebene, um das Opt-in zu setzen, und der Client versieht jeden nachfolgenden Request mit _meta["io.modelcontextprotocol/logLevel"] (im Request-Body des Netzwerk-Tabs prüfbar). Ein Aufruf von send_notification streamt das Log über die SSE-Antwort des Requests. Setzen Sie es zurück auf Aus, und derselbe Aufruf wird still unterdrückt — der Request lässt den logLevel-Schlüssel aus, sodass das Log nie ankommt.

Diese Unterdrückung entspricht der Spezifikation („ein Server DARF KEIN notifications/message für einen Request ausgeben, der kein Opt-in gesetzt hat"), weil send_notification über das pro Request geltende, schwellenwertbewusste extra.log des SDKs ausgibt (ctx.mcpReq.log). Im modernen Zweig liest es das Pro-Request-Opt-in logLevel aus dem Request-Envelope und verwirft die Nachricht, wenn der Client kein Opt-in gesetzt hat oder die Ebene unter dem angeforderten Schweregrad liegt; im Legacy-Zweig berücksichtigt es die Sitzungsebene aus logging/setLevel. Da es über das notify des Requests ausgibt, wechselt die moderne Antwort auf SSE, und das Log fährt auf dem Stream des ursprünglichen Requests mit.

Ressourcen-Abonnements, beide Ären

subscriptions-legacy-http.json und subscriptions-modern-http.json bedienen beide drei numbered_resources mit subscriptions: true. Die Legacy-Variante bedient außerdem ein update_resource-Tool; die moderne Variante setzt transport.modern: true.

  • Legacy — öffnen Sie eine Ressource im Ressourcen-Tab und klicken Sie auf Abonnieren. Der Client sendet resources/subscribe, und der Abonnements-Bereich listet den URI ohne Stream-Statusanzeige. Rufen Sie update_resource mit diesem URI auf, aktualisiert der Server den Inhalt und gibt notifications/resources/updated aus, wobei der Zeitstempel „Zuletzt aktualisiert“ der abonnierten Kachel neu gestempelt wird.
  • Modern — dasselbe Abonnieren sendet stattdessen subscriptions/listen (sein Filter trägt resourceSubscriptions plus das resourcesListChanged-Opt-in) und wird bei notifications/subscriptions/acknowledged aufgelöst. Der Abonnements-Bereich zeigt dann in seinem Kopfbereich ein Stream-Status-Badge (Verbinden… → Lauschen) und stellt die Verbindung durch erneutes Auflisten wieder her, falls der langlebige Stream abbricht.

Die moderne Konfiguration lässt update_resource bewusst aus. Der moderne Zweig des SDKs ist zustandslos/pro-Request (createMcpHandler(() => createMcpServer(config))), sodass das Tool gegen eine Wegwerf-Serverinstanz laufen würde — die Inhaltsänderung würde für das nächste resources/read nicht erhalten bleiben, und ihr resources/updated würde den getrennten Listen-Stream nicht erreichen. Eher verwirrend als nützlich.

Der Live-Round-Trip der Aktualisierungsbenachrichtigung wird also auf dem Legacy-Server (zustandsbehaftete Sitzung) demonstriert, und der moderne Server dient dem Abonnieren/Lauschen/Badge-Verhalten. Der Empfangs-Pfad des Inspectors ist ära-transparent, sodass ein echter zustandsbehafteter moderner Server, der resources/updated auf den Listen-Stream leitet, die abonnierte Kachel auf dieselbe Weise steuert.

Tasks, beide Ären

Legacy (tasks-legacy-http.json) kündigt capabilities.tasks an (tasks: { list, cancel }) mit den Presets simple_task / progress_task / elicitation_task. Führen Sie eines dieser Tools mit aktivierter Option Als Task ausführen aus, und der Tasks-Tab listet es auf (befüllt über tasks/list), pollt tasks/get, holt die Nutzlast mit dem blockierenden tasks/result und bricht mit tasks/cancel ab.

Modern (tasks-modern-http.json) setzt transport.modern: true und tasksExtension: true, kündigt die io.modelcontextprotocol/tasks-Erweiterung (SEP-2663) an und bedient modern_task / modern_input_task. Der Tasks-Tab hängt von der ausgehandelten Erweiterung ab, nicht von capabilities.tasks.

  • Führen Sie modern_task als Task aus — der tools/call gibt ein CreateTaskResult zurück (resultType: "task", sichtbar in den Protokoll-/Netzwerk-Tabs), der Client pollt tasks/get (kein tasks/list), und der abgeschlossene Task bettet sein Ergebnis direkt ein (kein blockierendes tasks/result).
  • Führen Sie modern_input_task aus — der Task wechselt zu input_required und zeigt eine eingebettete Elicitation über das Modal für ausstehende Requests an. Die Beantwortung sendet tasks/update mit den inputResponses, und die nächste Abfrage schließt den Task ab.

SDK v2 hat die gesamte Tasks-Unterstützung entfernt und sperrt die tasks/*-Spezifikationsmethoden auf beiden Seiten aus der modernen Ära aus. Der Inspector treibt die Erweiterung also selbst an — der resultType: "task"-Frame wird am Transport in ein CallToolResult umgeschrieben, das den Handle trägt, und tasks/get / update / cancel laufen über einen Raw-Wire-Requestkanal mit dem vollständigen modernen Envelope. Der Testserver bedient tasks/* über einen Express-Interceptor vor dem SDK-Handler, da der moderne Zweig des SDKs sie mit -32601 beantworten würde.

Die Schaltfläche Aktualisieren des Tasks-Tabs pollt die dem Client bereits bekannten Handles erneut — die moderne Ära hat keine serverseitige Task-Liste.

Erstellen```bash

npm run build # builds all clients: web → cli → tui → launcher

root@kitploit:~
Einzelne Clients: `build:web`, `build:cli`, `build:tui`, `build:launcher`. Der Web-Build erzeugt sowohl die Browser-SPA (`clients/web/dist`, Vite) als auch den Node-Prod-Server-Runner (`clients/web/build`, tsup).

## Testing & das Qualitäts-Gate

Jeder Client validiert sich selbst aus seinem eigenen Ordner; die Root-Skripte führen sie in einer Kette aus. Es gibt **kein** aggregiertes Root-`test`-Skript — verwenden Sie `validate` (schnell) oder `coverage` (das Gate).

| Script                              | Was es tut                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `npm run validate`                  | Führt zuerst die drei dauerhaften Guards aus — `verify:format-coverage` (jede nachverfolgte Quelldatei ist format-gated), `verify:typecheck-coverage` (jede landet in einem tsconfig-Projekt), `verify:dep-lockstep` (keine Abhängigkeit, die aus zwei Installationen in ein `tsc`-Programm gelangt, weicht zwischen ihnen ab) — dann `test:scripts` (die eigenen Parser-Unit-Tests der Guards), dann `validate:core` (das gemeinsame `core/`-Gate aus `format:check` + `lint`), dann pro Client: `format:check` + `lint` + **`typecheck`** (cli/tui/launcher; web führt den Typecheck über `tsc -b` innerhalb seines `build` aus) + `build` + schnelle Unit-Tests. Der schnelle Inner-Loop-Check. |
| `npm run coverage`                  | Das **Pro-Datei-≥90%-Gate** (Zeilen/Anweisungen/Funktionen/Zweige) unter v8-Instrumentierung, pro Client. Von der CI erzwungen. Für Web läuft dabei auch das Integrationsprojekt und es wird die gemeinsame Laufzeit von `core/` abgedeckt (einschließlich `core/json` und `core/client`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `npm run smoke`                     | End-to-End-Smoke-Tests durch den gebauten Launcher (`--help`-Dispatch + Prod-CLI/TUI/Web), plus zwei Headless-Chromium-Smoke-Tests: ein Boot-Smoke, der das Prod-Web-Bundle ausführt und einen sauberen ersten Render bestätigt (kein unbehandelter Fehler — synchrone Exception oder unbehandelte Promise-Ablehnung, so wie sich ein in das Browser-Bundle gelangendes Node-Builtin äußert), und ein **MCP-Apps**-Smoke (`smoke:web:app`), der Verbinden → App öffnen → `data-app-status="ready"` gegen einen komponierbaren App-Server durchläuft und dabei den Sandbox-Proxy und die UI-Protokoll-Brücke abdeckt.                                                                                                                                                                                                                                                                                                                                   |
| `npm run verify:build-gate`         | Führt einen echten `vite build` mit einem Node-Builtin aus, das gewaltsam in den Browser-Graph eingefügt wird, und stellt fest, dass der Build über das #1769-Gate **fehlschlägt** (das Vites Warnung zur Browser-Externalisierung in einen harten Fehler verwandelt). Schützt davor, dass die Formulierung der Warnung bei einem Vite-Versionssprung abweicht und das Gate stillschweigend deaktiviert. Teil von `npm run ci`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `npm run verify:format-coverage`    | Extrahiert die `format:check`-Globs aus jeder `package.json` (nur die von `validate` aus erreichbaren), zählt alle nachverfolgten Quelldateien auf und **schlägt fehl**, wobei alle aufgelistet werden, die von keinem Glob abgedeckt sind — der dauerhafte Guard für die Invariante „jede First-Party-Quelldatei ist format-gated“ (#1792). Läuft zuerst in `validate`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `npm run test:scripts`              | Tabellenbasierte Unit-Tests (`node --test`) für die eigenen reinen Parser der Guards (`scripts/lib/npm-scripts.mjs`, `scripts/lib/tsc-program.mjs` + die exportierten Helfer von `verify-typecheck-coverage.mjs` und `verify-dep-lockstep.mjs`), jeweils ein Fall pro Regel, die sie kodieren, plus `scripts/lib/resolve-node-bin.test.mjs` — der plattformübergreifende Bin-Auflöser (#1939), fixiert an den tatsächlichen `bin`/`exports`-Formen der Pakete, die die Skripte tatsächlich starten. Läuft in `validate` — und `verify:typecheck-coverage` bewacht wiederum *dieses* Gate (von `validate` erreichbar, nicht-leere Testmenge, jede Testdatei vom `test:scripts`-Glob erfasst), da `node --test` eine Datei, die sein Glob verfehlt, stillschweigend überspringt und trotzdem mit 0 beendet.                                                                                                                                                                                                                                                                                                                                                       |
| `npm run verify:typecheck-coverage` | Das Typecheck-Coverage-Pendant zum obigen (#1791): Für jeden Node-Client (automatisch von der Platte erkannt — eingebunden über die Projekte seines `typecheck`-Skripts, oder bei einem `tsc -b`-Client wie `clients/web` über die `references` seiner `tsconfig.json`) führt es diese Projekte mit `tsc --listFilesOnly` aus, vereinigt sie und **schlägt fehl**, wobei jede nachverfolgte `.ts`/`.tsx`/`.mts`/`.cts`-Datei unter dem Client aufgelistet wird, die in keinem Projekt landet (damit eine neue Top-Level-Config/Helper nicht stillschweigend untypechecked bleibt). Es verlangt außerdem, deny-by-default, dass das First-Party-TS, das kein Client besitzt (`test-servers/src`, die root `vitest.shared.mts`, das gesamte `core/` und jeder neue Top-Level-Ort), in einem tsc-Durchlauf irgendeines Client-Projekts landet — so wird auch ein `core`-`*.tsx` erfasst, das die Web-Projekte nicht erreichen. Zudem stellt es sicher, dass das Gate verdrahtet ist (jeder Typecheck-Durchlauf eines Clients — sein `typecheck`-Skript oder das `tsc -b` des Webs — von seinem `validate` aus erreichbar ist und die Root-Kette das `validate` jedes Clients ausführt). Läuft in `validate`. |
| `npm run verify:dep-lockstep`       | Bewacht die Invariante „eine Version pro installationsübergreifende Abhängigkeit“ (#1896). v2 ist kein Workspace, daher kompiliert ein Testprojekt eines Clients das gemeinsame First-Party-TypeScript — `core/`, `test-servers/src` und die root-eigene `vitest.shared.mts`, die alle ihre Abhängigkeiten aus der **Root**-Installation auflösen — zusammen mit den eigenen Quellen des Clients, wodurch dasselbe Paket zweimal in ein `tsc`-Programm gelangt. Bei derselben Version ist das harmlos; bei Abweichung muss TypeScript zwei strukturell verschiedene Kopien jedes Typs zueinander in Beziehung setzen, was bei einer rekursiv-generischen Oberfläche exponentiell ist (zod `4.3.6` vs. `4.4.3` erschöpfte den 4GB-tsc-Heap in `clients/web`). Leitet seine Kandidatenmenge daraus ab, **was tatsächlich in jedes Programm gelangt** (#1965) — jedes Client-tsconfig-Projekt, das mit `tsc --listFilesOnly` über das gemeinsame `scripts/lib/tsc-program.mjs` aufgelistet wird, wobei jede aufgelöste `node_modules`-Datei ihrer besitzenden Installation zugeordnet wird, und behält die Pakete, die aus zwei Installationen in ein Programm gelangen (ein Paket, dessen Deklarationen nur über die `.d.ts` eines anderen Pakets ankommen, wie bei `@modelcontextprotocol/sdk`, ist für einen Scan von First-Party-Importen unsichtbar). Bewertet jede Kopie anhand des Lockfile-Eintrags für den genauen Installationspfad, den das Programm aufgelöst hat, vergleicht nur die Installationen, die sich in einem Programm getroffen haben, und **schlägt deny-by-default fehl** bei jeder Abweichung, die nicht in der annotierten `TOLERATED_SKEW`-Allowlist steht — heute leer —, wobei ein auf der Allowlist stehendes Paket nur *innerhalb einer Major-Version* toleriert wird. Läuft in `validate`. |
| `npm run ci`                        | **Verpflichtender Befehl vor dem Push.** `validate` → `coverage` → `verify:build-gate` → `smoke` → Storybook. Eine echte Obermenge der GitHub-CI.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `npm run pack:verify`               | Publish-Smoke-Test — siehe [Publishing](#publishing).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |

Es gibt auch Pro-Client-Skripte (`validate:web`, `coverage:cli`, `smoke:tui`, …), dazu root `validate:core` / `format:core` für das gemeinsame `core/`-Paket, `format:scripts` für die Root-`scripts/`-Werkzeuge und `format:shared` / `lint:shared` für die Root-„shared“-Oberfläche (`test-servers/src/**`, `vitest.shared.mts`, die Root-`eslint.config.js`). Führen Sie vor dem Committen `npm run format` aus — das Root-`format` korrigiert `core/`, die Root-`scripts/`, die Shared-Oberfläche und jeden Client; `validate` führt das nicht korrigierende `format:check` aus und lässt CI bei jeder unformatierten Datei fehlschlagen.

**Das Linting ist typbewusst.** Alle fünf ESLint-Bereiche (`clients/{web,cli,tui,launcher}` plus das Root-`core/` + gemeinsame Gate) aktivieren `@typescript-eslint/no-floating-promises` auf `error`, sodass ein Promise, das weder awaited noch zurückgegeben, mit `.catch(…)` abgeschlossen noch explizit mit `void` verworfen wird, bei `lint` fehlschlägt — und damit bei `validate` ([#1959](https://github.com/modelcontextprotocol/inspector/issues/1959)). Die Regel benötigt Typinformationen, daher benennt die Konfiguration jedes Bereichs ein Parser-Projekt; das des Root-Bereichs ist **`tsconfig.lint.json`**, ein reines Lint-Projekt, das `core/**`, `test-servers/src/**` und `vitest.shared.mts` abdeckt, die kein eigenes tsconfig haben. Es erzeugt nichts und ändert keinen Typecheck — aber ein neuer First-Party-TS-Ort, der zum Root-Lint-Bereich hinzugefügt wird, muss zu dessen `include` hinzugefügt werden. Siehe **TypeScript-Anweisungen** in [`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md) dazu, wann `void` akzeptabel ist.

Die vollständigen Testregeln — das ≥90%-Pro-Datei-Gate, wo Testdateien liegen, die Unit- vs. Integrations- vs. Storybook-Projekte und die `v8 ignore`-Richtlinie — finden Sie in [`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md).

## Publishing

Das Root-Paket `@modelcontextprotocol/inspector` wird als **ein Tarball mit einer einzigen Versionsnummer** ausgeliefert — keine separaten Pakete `-web` / `-cli` / `-tui` / `-core`. `npm run build` baut jeden Client, dann läuft `prepack` vor `npm publish`. Laufzeitabhängigkeiten sind in der Root-`package.json` deklariert; Client-Builds bündeln `@inspector/core` und externalisieren npm-Pakete, die aus der Root-Installation aufgelöst werden.

### Was ausgeliefert wird und die Packaging-Invarianten

Die `"files"`-Allowlist der Root-`package.json` ist die maßgebliche Quelle für den Tarball. Einige nicht offensichtliche Einträge existieren, weil sie **zur Laufzeit** gelesen werden oder von npm's packlist stillschweigend entfernt wurden — entfernen Sie sie nicht, ohne `npm run pack:verify` erneut auszuführen:

- **Keine Source Maps.** Die Client-Bundler setzen `sourcemap: false` (`clients/{cli,tui}/tsup.config.ts`, `clients/web/tsup.runner.config.ts`); Vite und das `tsc` des Launchers erzeugen ohnehin keine. Maps machen etwa die Hälfte der entpackten Größe aus und werden zur Laufzeit nicht benötigt — debuggen Sie über `npm run dev` direkt im Quellcode.
- **`clients/web/build` wird über `clients/web/.npmignore` ausgeliefert.** `clients/web/.gitignore` listet `build/` auf, und npm's Packlist beachtet diese verschachtelte `.gitignore` gegenüber der Root-`"files"`-Allowlist — dadurch fehlte der Prod-Web-Server-Runner stillschweigend im Tarball, während `clients/web/dist` durchrutschte (seine `.gitignore` listet nur `dist-ssr`). `clients/web/.npmignore` überschreibt die `.gitignore` für die Veröffentlichung, sodass sowohl `build/` (Runner) als auch `dist/` (SPA) ausgeliefert werden. Die anderen Clients brauchen das nicht — keiner liefert eine verschachtelte `.gitignore` aus.
- **`clients/web/static` liefert den MCP-Apps-Sandbox-Proxy aus.** `clients/web/static/sandbox_proxy.html` ist eine kommittierte Quelldatei (kein Build-Artefakt), die zur Laufzeit von `clients/web/server/sandbox-controller.ts` von der Platte gelesen wird als `<runner dir>/../static/sandbox_proxy.html`. Sie fehlte vollständig in der Root-`"files"`-Allowlist, sodass jeder veröffentlichte Build den Apps-Tab mit **"Sandbox not loaded"** ([#1859](https://github.com/modelcontextprotocol/inspector/issues/1859)) scheitern ließ, während es im Repo einwandfrei funktionierte. Da der Pfad _relativ zu_ `clients/web/build` aufgelöst wird, muss das Verzeichnis an genau dieser Stelle ausgeliefert werden — `pack:verify` prüft sowohl den Tarball-Eintrag als auch den auf der Platte installierten Pfad.
- **Eine Abhängigkeit, die React rendert, wird gebündelt, nicht externalisiert.** Ein externalisiertes Paket löst sein eigenes `react` von dem Ort auf, an dem npm **es** im Baum des Konsumenten platziert hat, was nicht zwangsläufig der Ort ist, an dem das Bundle unseres auflöst — npm platziert ein Paket neben einem React, das seinen *Peer*-Bereich erfüllt, und diese Bereiche sind lockerer als unsere. `ink-form` und `ink-scroll-view` deklarieren `">=18"`, daher erfüllt ein Projekt mit React 18 sie und sorgt dafür, dass sie gehoistet werden, während das React 19 des Inspectors darunter verschachtelt wird: zwei React-Kopien, und die TUI stirbt mit `TypeError: Cannot read properties of null (reading 'useState')`, sobald ein Tool-Test-Formular oder eine Scroll-View gemountet wird ([#1952](https://github.com/modelcontextprotocol/inspector/issues/1952)). Beide werden daher von `clients/tui/tsup.config.ts` inline eingebunden und sind **keine** Root-Abhängigkeiten: Der Tarball liefert ihren Code innerhalb von `clients/tui/build/index.js`, statt sie vom Konsumenten installieren zu lassen. Das Bündeln fixiert außerdem ihre transitiven Abhängigkeiten auf das, was die Installation dieses Repos aufgelöst hat (insbesondere `ink-select-input@6` über `overrides`, die npm bei einem als Abhängigkeit installierten Paket ignoriert). **`ink` ist die eine Ausnahme, aus Kostengründen:** Das Bündeln funktioniert, fügt aber ~1.4 MB hinzu (`react-reconciler` und `yoga-layout` kommen mit, plus ein `createRequire`-Banner für das inline eingebundene CJS), daher bleibt es externalisiert — *nicht*, weil sein `">=19"`-Peer es sicher macht, was es nicht tut. Was das erträglich hält, ist der Root-`react`-Bereich: `"^19.0.0"` ist bewusst für die gesamte Major-Version offen, damit npm unser React mit jedem React 19 deduplizieren kann, das ein Konsument pinnt, sodass ein externalisiertes `ink` auf derselben Kopie bleibt, die das Bundle verwendet. **Eine Verengung dieses Bereichs öffnet den Bug für den Renderer selbst erneut** — `clients/tui/__tests__/tsupConfig.test.ts` pinnt ihn auf die Peer-Untergrenze von `ink` und sichert den Rest der Aufteilung ab; siehe [TUI README](https://github.com/modelcontextprotocol/inspector/blob/HEAD/clients/tui/README.md#bundling-react-rendering-dependencies-must-be-inlined-1952).
- **Eine einzige Versionsnummer, gelesen aus der Root-`package.json`.** Der Inspector wird als ein Paket mit einer Version ausgeliefert, daher trägt nur die **Root**-`package.json` eine `version` — die vier `clients/*/package.json`s haben bewusst keine. Jeder Node-Client (CLI, TUI und das Web-Backend) löst die Version über den gemeinsamen Reader `readInspectorVersion()` in `core/node/version.ts` auf, der bis zum Root-Manifest (im Tarball immer vorhanden) hochgeht. Keine Client-`package.json` wird zur Laufzeit gelesen, daher muss keine ausgeliefert werden. Der Web-**Browser** kann das Dateisystem nicht lesen; er erhält seine Version vom Backend über `GET /api/config` (siehe [#1639](https://github.com/modelcontextprotocol/inspector/issues/1639)).

### `npm run pack:verify` — Publish-Smoke-Test gegen den echten TarballDie `smoke:*`-Skripte laufen gegen den Build-Baum im Repository, der **nicht** das veröffentlichte Paket ist. `npm run pack:verify` (`scripts/pack-and-verify.mjs`) schließt diese Lücke: Es baut, packt den veröffentlichbaren Tarball mit `npm pack` (wobei sichergestellt wird, dass keine Source Maps ausgeliefert werden und die zur Laufzeit benötigten Dateien vorhanden sind), installiert den Tarball in einen **sauberen Wegwerf-Consumer** – ein frisches temporäres Verzeichnis, in dem es ein echtes `npm install <tgz>` ausführt (zieht Laufzeitabhängigkeiten, führt `postinstall` aus), genau wie es `npx @modelcontextprotocol/inspector` tun würde – und testet das installierte `mcp-inspector`-Binärprogramm Ende zu Ende: `--help`-Aufruf, ein echter `--cli tools/list`-Aufruf über stdio und ein Produktionsstart mit `--web`, der `/` aus dem ausgelieferten `dist`-Verzeichnis bedienen muss. Damit fängt es Pfad-/Paketierungsfehler ab, bei denen etwas „unter `--dev` funktioniert, unter `npx …` bricht“. Es erfordert Netzwerkzugriff (die Installation zieht Abhängigkeiten), ist also ein lokaler / Release-Check und **kein** Teil der schnellen `validate`/`ci`-Schleife.

### Ein Release erstellen

Die Veröffentlichung wird von zwei release-gesteuerten Jobs in [`.github/workflows/main.yml`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/.github/workflows/main.yml) automatisiert (`github.event_name == 'release'`, beide `needs: build`):

- **`publish`** — das npm-Paket. Führt `npm run pack:verify` als Pre-Publish-Gate aus, stellt sicher, dass das Release-Tag mit der Version der `package.json` im Wurzelverzeichnis übereinstimmt, und führt dann `npm publish --access public --provenance` aus — ein einzelner `npm publish`-Aufruf (v2 ist kein npm-Workspace, daher gibt es kein `publish-all`/`--workspaces` im v1-Stil), mit einer signierten Provenance-Bestätigung über GitHub OIDC (`id-token: write`, `environment: release`, `NPM_TOKEN`).
- **`publish-github-container-registry`** — das Container-Image (siehe [Docker](#docker)).

Ein v2-Release wird von **`main`** erstellt, nachdem die Arbeit des Meilensteins von `v2/main` dorthin gemerged wurde — nicht von `v2/main` selbst. (Die v1-Linie veröffentlicht unabhängig von `v1/main` auf das `v1-latest`-Tag und berührt `main` nie; siehe [Repo-Status](#mcp-inspector).)

Da es **eine einzige Versionsnummer** gibt (nur die `package.json` im Wurzelverzeichnis hat eine — die Clients tragen keine, es gibt also nichts zu synchronisieren und keinen `check-version`-Schritt), besteht der Release-Ablauf aus drei Schritten.

**1. Versionsanhebung auf `v2/main`, vor dem Merge des Meilensteins.** Die Versionsanhebung ist Teil der Arbeit des Meilensteins, gehört also in den Develop-Branch und fließt mit allem anderen in `main` ein:

Ersetzen Sie unten die echte Issue-Nummer und das Release — die Befehle sind so geschrieben, dass sie unverändert kopiert und eingefügt werden können (ein Minor-Bump von `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 ist von entscheidender Bedeutung. Ein bloßes npm version erzeugt ebenfalls einen Tag, und der Tag würde auf einem v2/main-Commit landen — aber das Release muss von main aus erstellt werden, also muss der Tag auf den dortigen Merge-Commit zeigen (Schritt 3). Hier zu taggen erzeugt einen Tag auf einem Commit, der nie veröffentlicht wird.

2. Führe den Merge v2/main → main über den üblichen Milestone-Merge-Branch durch. Er enthält nun die Versionserhöhung, sodass das Release mit der bereits korrekten Version auf main landet.

Zwischen Schritt 1 und 2 unterscheiden sich die beiden Branches tatsächlich — und das ist erwartet, kein Drift: v2/main liest die Version, die gerade gebaut wird, während main noch die aktuell veröffentlichte Version liest. Was diese Reihenfolge beseitigt, ist der Post-Release-Drift — sobald der Milestone-Merge landet, stimmen sie wieder überein, und v2/main bleibt nie hinter main zurück. Wenn v2/main vor main liegt, ist ein Release in Arbeit; wenn es dahinter liegt, ist etwas schiefgelaufen.

3. Tagge den main-Commit und erstelle den Release-Entwurf:```bash git fetch origin main git tag 2.3.0 origin/main && git push origin 2.3.0

then draft & publish a GitHub Release for that tag → triggers publish

root@kitploit:~
⚠️ **Tagge `origin/main`, nicht dein lokales `HEAD`.** `git checkout main && git pull` läuft über die von dir konfigurierte Merge- oder Rebase-Strategie, sodass ein divergierendes lokales `main` stillschweigend lokale Commits erzeugen oder erneut anwenden kann. `HEAD` dort zu taggen, taggt einen Commit, der nicht auf `origin/main` liegt, und `git push origin <tag>` pusht nur den Tag – zurück bleibt ein Release, dessen Commit nie veröffentlicht wurde. `origin/main` explizit zu benennen, sorgt dafür, dass der getaggte Commit exakt das ist, worauf der Remote-Branch zeigt, unabhängig vom lokalen Zustand.

⚠️ **Kein `v`-Präfix.** Die Release-Tags dieses Repos sind schlicht `x.y.z` – `2.2.0`, `2.1.0`, `2.0.0` – also tagge `2.3.0`, nicht `v2.3.0`. Beachte, dass das npm-eigene `tag-version-prefix` standardmäßig auf `v` gesetzt ist und das Repo keine `.npmrc` festlegt, sodass ein bloßes `npm version` einen Tag mit `v`-Präfix erzeugt hätte, der nicht zur Konvention passt. Manuelles Taggen (Schritt 3) hält es korrekt. Der Assert-Schritt des Workflows entfernt ein führendes `v` vor dem Vergleich, sodass ein Tag mit `v`-Präfix trotzdem veröffentlicht würde – er wäre nur inkonsistent mit jedem vorherigen Release.

Der Ziel-Commit des Releases bestimmt, welcher Workflow läuft, daher wird nur dann veröffentlicht, wenn ein Release aus einem Commit erstellt wird, der diesen (v2) Workflow trägt.

**Warum der Versions-Bump zuerst auf `v2/main` geht ([#2010](https://github.com/modelcontextprotocol/inspector/issues/2010)).** Früher passierte das auf dem Milestone-Merge-Branch, der von `main` abgezweigt wird – daher existierte der Bump nur *downstream* von `v2/main`, und nichts trug ihn zurück. `v2/main` blieb während der Releases 2.1.0 und 2.2.0 bei `2.0.0`. Das ist nicht kosmetisch: Ein von einem Milestone-Merge-Branch abgezweigter Branch trägt den Bump stillschweigend in einen unzusammenhängenden PR ein (das passierte in [#2009](https://github.com/modelcontextprotocol/inspector/issues/2009), wo ein Container-Bugfix mit einem `2.0.0 → 2.2.0`-Diff eintraf), und alles, was die Version während der Entwicklung liest — `readInspectorVersion()`, `--version`, `GET /api/config` — meldete eine Version, die zwei Releases alt war.

Versuch **nicht**, eine zukünftige Drift zu "reparieren", indem du `main` zurück in `v2/main` mergst. `main` trägt die gesamte Pre-v2-v1-Historie (erhalten durch `ec5d8e13 chore: replace main's tree with v2` — ~230 Commits, die `v2/main` nicht hat), ein Back-Merge würde also all das dauerhaft in das Log des Develop-Branchs einpflegen, nur um eine Zwei-Dateien-Änderung zu liefern. Wenn man zuerst bumpt, muss nichts zurückgemergt werden.

### Docker

Ein Container-Image wird vom Release-Workflow auf GHCR (`ghcr.io/modelcontextprotocol/inspector`, `linux/amd64` + `linux/arm64`) veröffentlicht. Das [`Dockerfile`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/Dockerfile) ist ein zweistufiger Build: Die erste Stufe installiert das veröffentlichbare Tarball und führt `npm pack` aus; die zweite Stufe installiert dieses Tarball per `npm install -g`, sodass das Image exakt dasselbe Artefakt wie npm ausliefert, mit einem sauberen `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-Tab in Verwendung? Veröffentliche auch 6275. Die MCP-Apps-Sandbox ist ein zweiter Listener, den der Browser direkt erreicht, auf MCP_SANDBOX_PORT (Standard 6275). Sonst braucht sie nichts, daher sind die Einzelport-Befehle oben für die normale Inspektion in Ordnung – aber der Apps-Tab rendert ohne sie ein leeres Widget:```bash docker run --rm -p 127.0.0.1:6274:6274 -p 127.0.0.1:6275:6275
ghcr.io/modelcontextprotocol/inspector

root@kitploit:~
Veröffentliche ihn innen und außen auf **derselben Portnummer**. Die Sandbox-URL wird dem Browser über `/api/config` als `http://localhost:<container port>/sandbox` übergeben; ein Umapping (`-p 9000:6275`) bewirbt also einen Port, den der Browser nicht erreichen kann. Verwende stattdessen `-e MCP_SANDBOX_PORT=9000 -p 127.0.0.1:9000:9000`.

**Behalte das Präfix `127.0.0.1:` am veröffentlichten Port.** Ein bloßes `-p 6274:6274` veröffentlicht auf **jedem Host-Interface** und bringt den Inspector in dein lokales Netzwerk. Das `HOST=0.0.0.0` des Containers ist eine separate Angelegenheit – es betrifft die _Interfaces des Containers_, nicht die des Hosts – daher deckt das `DANGEROUSLY_BIND_ALL_INTERFACES`-Opt-in, das einen Wildcard-Bind außerhalb eines Containers absichert, dies nicht ab. Das ist hier wichtiger als bei einer gewöhnlichen Web-App: Das Backend startet auf Anfrage Prozesse, `GET /` bettet das API-Token in das ausgelieferte HTML ein, und eine Anfrage ohne **`Origin`-Header** überspringt die Origin-Allowlist vollständig – für jeden Nicht-Browser-Client ist das API-Token also die einzige Absicherung. Eine breitere Veröffentlichung erfordert eine echte Zugriffskontrollgrenze vor dem Inspector – einen Reverse-Proxy, der authentifiziert, einen SSH-Tunnel, ein privates Netzwerk. Das Setzen eines eigenen `MCP_INSPECTOR_API_TOKEN` ist **kein** Ersatz: `GET /` legt das jeweils verwendete Token offen, sodass ein benutzerdefiniertes genauso leicht abgegriffen werden kann wie ein generiertes.

**Die hinzugefügten Server behalten.** Der Inspector speichert deine Serverliste unter `$HOME/.mcp-inspector/mcp.json`, was im Image `/home/node/.mcp-inspector/mcp.json` ist – innerhalb der beschreibbaren Ebene des Containers, sodass `--rm` sie verwirft und jeder Lauf mit einer leeren Liste beginnt. Binde dort ein Volume ein, um sie zu behalten:```bash
docker run --rm -p 127.0.0.1:6274:6274 \
  -v mcp-inspector-data:/home/node/.mcp-inspector \
  ghcr.io/modelcontextprotocol/inspector

Dasselbe Volume speichert auch OAuth-Tokens und den gespeicherten Zustand, sodass ein autorisierter Server über mehrere Läufe hinweg autorisiert bleibt. Verwenden Sie -e MCP_CATALOG_PATH=/some/other/path.json, um den Katalog woanders abzulegen – mounten Sie ein Volume, das das Verzeichnis abdeckt, auf das Sie ihn zeigen. Wenn Sie ein Host-Verzeichnis per Bind-Mount einbinden statt eines benannten Volumes (-v "$PWD/inspector-data:/home/node/.mcp-inspector"), behält das Verzeichnis die Eigentümerschaft des Hosts, daher fügen Sie unter Linux --user "$(id -u):$(id -g)" hinzu oder ändern Sie den Besitzer per chown auf UID 1000 – andernfalls kann der Nicht-Root-Benutzer node nicht schreiben und das Hinzufügen eines Servers schlägt mit EACCES fehl.

Upgrade von einem Image vor diesem Fix? Frühere Images erstellten /home/node/.mcp-inspector nicht, daher legte Docker den Mount-Punkt des Volumes als root an, und der Nicht-Root-Benutzer node konnte nicht darauf schreiben. Ein leeres Volume repariert sich selbst beim ersten Start eines aktuellen Images (Docker überträgt die Eigentümerschaft des Image-Verzeichnisses auf ein leeres Volume), aber ein Volume, das bereits Dateien enthält, behält seine alte root-Eigentümerschaft und schlägt weiterhin mit EACCES fehl. Beheben Sie es einmal:```bash docker run --rm -u 0 --entrypoint chown
-v mcp-inspector-data:/data ghcr.io/modelcontextprotocol/inspector
-R node:node /data

root@kitploit:~
Das Image verwendet standardmäßig `--web`, gebunden an `0.0.0.0:6274`, wobei das automatische Öffnen des Browsers deaktiviert ist; überschreiben Sie die Argumente, um einen anderen Modus auszuführen (`docker run --rm ghcr.io/modelcontextprotocol/inspector --cli …`). Übergeben Sie `-e MCP_INSPECTOR_API_TOKEN=…`, um ein bekanntes Token zu setzen (andernfalls wird eines generiert und in den Logs ausgegeben), oder `-e DANGEROUSLY_OMIT_AUTH=true`, um die Authentifizierung zu deaktivieren. Das Binden an `0.0.0.0` (alle Netzwerkschnittstellen) wird außerhalb eines Containers standardmäßig verweigert — es setzt das Backend zum Starten von Prozessen dem lokalen Netzwerk aus — daher aktiviert das Image dies explizit mit `DANGEROUSLY_BIND_ALL_INTERFACES=true` (bereits im `Dockerfile` gesetzt); ein bloßes `HOST=0.0.0.0` ohne dieses Flag endet mit einem Fehler. Wenn Sie **den veröffentlichten Port neu zuordnen** (`-p 127.0.0.1:8080:6274`), stimmt die Origin des Browsers (`http://localhost:8080`) nicht mehr mit dem Port im Container überein; setzen Sie daher `-e ALLOWED_ORIGINS=http://localhost:8080,http://127.0.0.1:8080` (oder führen Sie `-e CLIENT_PORT=8080 -p 127.0.0.1:8080:8080` aus), sonst werden Verbindungen mit 403 beantwortet. `ALLOWED_ORIGINS` **ersetzt** die Standardliste, anstatt sie zu erweitern; listen Sie daher jede Loopback-Variante auf, von der aus Sie browsen (siehe [web README](https://github.com/modelcontextprotocol/inspector/blob/HEAD/clients/web/README.md#host-binding--the-origin-allow-list)). Das Image läuft als Nicht-Root-Benutzer `node` und besitzt einen `HEALTHCHECK`, der die Weboberfläche abfragt — er setzt den Standardmodus `--web` voraus; fügen Sie daher `--no-healthcheck` hinzu, wenn Sie `--cli`/`--tui` ausführen (die keinen Webserver besitzen).

## Mitwirken — `AGENTS.md` und `CLAUDE.md`

**[`AGENTS.md`](https://github.com/modelcontextprotocol/inspector/blob/HEAD/AGENTS.md) ist der Vertrag für Änderungen an dieser Codebasis und gilt gleichermaßen für Menschen und KI-Agenten.** Es ist kein bloßes Boilerplate für Agenten — es enthält die tatsächlichen Konventionen des Projekts: den Issue- und Board-Workflow, die Branch-/Label-Regeln, die TypeScript- und Mantine/React-Standards, die Test- und Abdeckungsanforderungen sowie das verpflichtende Pre-Push-Gate. Lesen Sie es, bevor Sie Änderungen vornehmen, und halten Sie es aktuell, wenn Sie Struktur, Tooling oder Regeln ändern.

`CLAUDE.md` ist der Einstiegspunkt, den der [Claude Code](https://claude.com/claude-code)-Agent automatisch lädt; er bindet lediglich `AGENTS.md` und dieses README ein, sodass Agenten und Menschen auf dieselbe Quelle der Wahrheit zugreifen. Wenn Sie einen anderen Agenten verwenden, der `AGENTS.md` liest, gelten dieselben Regeln.

Eine wichtige Regel sei hier hervorgehoben: **Alle Arbeit ist issuebasiert.** Finden oder erstellen Sie vor Beginn ein Tracking-Issue auf dem v2-Projektboard; eröffnen Sie PRs gegen `v2/main` mit `Closes #<issue>`. Die genauen Anleitungen (Labels, Board-IDs, Statuswerte) finden Sie in `AGENTS.md`.

## Lizenz

MIT.
Tool herunterladen
KonfigurationDemonstriertIssue
mcp-app-http.json (Legacy-Ära)Eine MCP-App (UI-Ressource + App-Tool) im Apps-Tab#1859
modern-mrtr-http.jsonEin einzelner MRTR-Roundtrip—
mrtr-showcase-http.jsonJedes MRTR-Preset in einem Server#1860
modern-network-http.jsonNetzwerk-Tab: Mcp-*-Header + Fehler-Taxonomie#1628
xmcpheader-modern-http.jsonTools-Tab: x-mcp-header-Spiegelung und Ausschlüsse#1632
pagination-http.jsonSeitenweises Abrufen von Listen#1721
structured-output-http.jsonTools-Tab: der structuredContent-Abschnitt eines Ergebnisses#1908
duplicate-tool-names-http.jsonEin tools/list, das einen Toolnamen wiederholt#1957
nullable-fields-http.jsonTools-Tab: nullable (anyOf + null)-Argumente#1928
rfc6570-templates-http.jsonRessourcen-Tab: Expansion von RFC-6570-Ressourcentemplates#1919
advertised-extensions-http.jsonTool-Registrierung abhängig von angekündigten Erweiterungen#1739
logging-{legacy,modern}-http.jsonLogging, beide Ären#1629
subscriptions-{legacy,modern}-http.jsonRessourcen-Abonnements, beide Ären#1630
tasks-{legacy,modern}-http.jsonAufgaben, beide Ären#1631
PresetVerhalten
mrtr_confirmEinzelner Roundtrip
mrtr_two_stepZwei Elicitationsrunden über requestState
mrtr_sampleEingebettetes Sampling → das Sampling-Panel
mrtr_rootsEingebettetes roots/list, automatisch still aus konfigurierten Roots beantwortet (kein Modal)
mrtr_edgeEine Runde nur mit inputRequests, dann eine Runde nur mit requestState
mrtr_emptySchließt mit einem leeren Ergebnis ab — kein content, kein structuredContent
mrtr_loopWird nie abgeschlossen → löst die MRTR_MAX_ROUNDS-Grenze aus
ToolAntwort
trigger_header_mismatch400 / -32020
trigger_missing_capability400 / -32021
trigger_unsupported_version400 / -32022 (mit data.supported)
trigger_method_not_found404 / -32601
FormSDK-Verhalten
{a,b}fügt die Werte roh zusammen — keine Kodierung, Operatorpräfix entfällt
{;id}; fehlt in seiner Operatorliste, daher parst die Variable als ;id
{id:3}der Präfix-Modifikator wird in den Namen gefaltet, was id:3 ergibt
{+v} / {#v}encodeURI verstümmelt reservierte [/] ([::1] → %5B::1%5D) und kodiert Pct-Tripletts doppelt (%2F → %252F)
{v}encodeURIComponent lässt die Sub-Delims !'()* nackt, die RFC 6570 kodiert verlangt