GitShow/modelcontextprotocol/inspector
modelcontextprotocol

inspector

Visual testing tool for MCP servers

by modelcontextprotocol
clidebugmcptooltuiweb
Star on GitHubForkWebsitenpm

TypeScript

10.7k stars1.5k forks140 contributorsActive · 3m agoSince 20242.3.0

Meet the team

See all 140 on GitHub →
cliffhall
cliffhall1.2k contributions
olaservo
olaservo443 contributions
jspahrsummers
jspahrsummers135 contributions
ashwin-ant
ashwin-ant133 contributions
richardkmichael
richardkmichael49 contributions
msabramo
msabramo49 contributions
felixweinberger
felixweinberger47 contributions
jerome3o-anthropic
jerome3o-anthropic42 contributions

Languages

View on GitHub →
TypeScript95.3%
JavaScript4.2%
CSS0.3%
HTML0.1%
Dockerfile0%

Commit activity

Last 12 weeks · 654 commits

Full graph →

Community health

4 of 6 standards met

Community profile →
75
✓README○License✓Contributing✓Code of Conduct○Issue Template✓PR Template

Recent PRs & issues

Active · Last activity 3m ago
See all on GitHub →
cliffhall
fix(web): read MCP App UI resource metadata from _meta.uiOpenPR

Closes #2055 Problem in read the UI resource's sandbox metadata straight off : But describes the value of *, not itself ( ; the tool side of this same file already reads via ). So for every spec-conforming app, and were → returned → emitted , and the app's declared never reached the sandbox. Any App doing network work was unusable in the Apps tab. Fix Read the metadata from , through a small helper that type-guards the value so a malformed bag degrades to "no hints" rather than being cast blindly. Three carriers, consulted most-specific first: 1. the content item's own ; 2. the read result envelope's — , what a callback returns, types at that level; 3. the matching entry's, which documents as the static default a host reviews at connection time, and which a read content item explicitly takes precedence over. Whichever is found first wins outright — the levels are not merged, since a server restating only at a more specific level means that to be the whole grant, not a patch over the broader one. The app-supplied bag is also screened before it reaches the sandbox (, beside the existing ). The proxy's tests each key for truthiness, so would have read as a grant; only the spec's empty-object marker now counts, everything else fails closed. Nothing screened it before because, thanks to this same bug, no permissions ever reached the proxy at all. The listing reaches the factory through a new optional dep, , wired in for both bridge factories from the resources list. Optional because a host with no listing — or one whose listing omits this URI, which is common for resources — must be unaffected rather than broken. Tests 's fixture now builds the real wire shape (), so the existing CSP/permissions assertions exercise the spec structure rather than a shape no server sends. Cases added: each of the three carriers is honored, and the precedence between them (content item > result envelope > listing) none present → still renders under an unnested* bag is ignored — locks the regression direction covers the permissions screen, including adds a case for the App-level wiring itself (a pass-through spy over the factory), since the factory's own tests inject the dep and so cannot see that wiring break. Screenshots None — the change has no visual surface. It alters the `mcp-app-http.json_meta.uiconnectDomains_meta.ui.domainMcpUiSandboxResourceReadyNotification.paramsdomainOrigin: nullsandbox_proxy.htmlallow-same-origindomainnpm run ci` passes.

cliffhall · 2m ago
cliffhall
fix(proxy): route through undici's own fetch and keep undici externalOpenPR

Closes #2067 Corporate-proxy users cannot connect to any remote MCP server from v2 — a regression from v1 that has been present since 2.0.0. The reporter's investigation was thorough and correct; I reproduced both defects end to end before fixing them. Two independent bugs 1. The web and TUI bundles shipped an unloadable copy of .* It was declared in the root and manifests but named in no client's tsup list, and tsup auto-externalizes only what the nearest manifest declares. So esbuild inlined 1.05MB of it: is CommonJS, so the emitted chunk's hits esbuild's ESM shim: The rejected surfaced as "HTTPS_PROXY / HTTP_PROXY is set but the package is not available" — telling users to install a package that was already present, and which could not have helped: the specifier had been rewritten to a relative chunk at build time, so no user-side install could ever satisfy it. 2. A userland dispatcher is incompatible with Node's built-in . injected a userland into Node's built-in . That couples two different copies of undici at the dispatcher handler interface, which is not stable across majors: So the CLI — which did externalize undici and loaded the real package — failed differently, and fixing the bundling alone would only have converted the web/TUI failure into the CLI's. The fix Both halves of the pair now come from userland undici — its and its . That keeps the dispatcher handler inside one copy, which is why it works unchanged from the Node 22.19 floor through Node 26. Deleting the wrapper in favor of Node's native was considered and rejected on measurement — it is not supported at our floor: Responses are re-wrapped as genuine global objects (streaming preserved). undici's is a different class, so is for it — and the SDK branches on exactly that in , which would degrade every OAuth error to . The proxy moved to the bottom of the fetch stack — for the Node clients, and the default for the web backend (its one caller that supplies no ). Proxying means substituting the fetch, not decorating one, so wrapping a supplied would discard it. As the reporter notes, sets unconditionally (), so a fix keyed off "was a supplied?" would have fixed web and silently left the CLI and TUI broken. A side effect worth having: OAuth discovery and token requests are now proxied too — they run through and never were before. Verification Driven through a real forwarding proxy against a real MCP server (), with the built clients: All requests confirmed to traverse the proxy (18 forwarded requests logged). Before this change, the web backend failed on every Node version with the misleading toast, and the CLI failed on 22/24 with . Guarding it is now root-declared only and named in all three tsup lists, per the placement rule in . The two installs had already drifted (root vs cli ). New fails if any package a client declares was inlined into that client's bundle anyway. It reads the built output, not the config — the two disagreed for four releases — and derives what to check from each client's own array, so a newly externalized package is covered without editing the guard. Wired into and the GitHub workflow, with table-driven unit tests via . Verified against the real pre-fix artifact, which it flags: The proxy tests now drive a real forwarding proxy rather than a mock. That is load-bearing: bug 2 was a runtime* interface mismatch, and no mock can catch it because no mock ever dispatches. The suite also pins the global- contract, , null-body statuses, and that no longer wraps a supplied . 's must-not-bundle rule (added by #2082) is extended with why slipped past it, and with the trap the reporter flagged — probing the bundled chunk with falsely succeeds, because it exposes a global that satisfies esbuild's guard. Mirrored into . Notes No UI change — no component, theme, or story is touched. The user-visible difference is that the connection succeeds instead of raising an error toast. drops ~1.05MB and the same, since neither vendors undici any more. 🤖 Generated with Claude Code https://claude.ai/code/session_01JTHVxSu8AUgHRLvo1ntZ8H

cliffhall · 5m ago
cliffhall
Run the MCP Apps sandbox smokes in Firefox and WebKit, not just ChromiumOpenIssue

Problem Every browser-based check in this repo runs in Chromium only: Firefox and WebKit appear nowhere — not in a script, not in the vitest browser config, not in the workflow. That is fine for most of the web client, whose behavior is React and Mantine. It is not fine for the MCP Apps sandbox, which is the one part of the Inspector built out of the browser primitives that actually diverge between engines: a CSP `srcdocsrcdocabout:blanksandboxPermissions-PolicyallowpermissionspostMessagesandbox-csp.test.tsapproveCspSourcesbuildSandboxCspPolicyAppRendererAppsScreenAppElicitationHostPLACEHOLDER_SANDBOX = "data:text/html,…"srcdocui/initializesandbox-csp.tscreateAppBridgeFactory.tssmoke:web:appsmoke:web:elicitscripts/smoke-web-app.mjsscripts/smoke-web-elicitation.mjsloadChromium()smoke:web:browser.github/workflows/main.ymlnpx playwright install --with-deps chromium firefox webkitfirefoxwebkitsmoke:web:appsmoke:web:elicitplaywright install --with-deps smoke:web:elicit#1565` sandbox hardening, so fixing it narrowly for elicitation would leave the larger surface uncovered.

cliffhall · 1h ago

Recent fixes

View closed PRs →
brandonja991
Bug: a failed server-list refresh after adding a server closes the modal with no errorClosedIssue

NOTE: Will fix but rescoped See: https://github.com/modelcontextprotocol/inspector/issues/1914#issuecomment-5305668184 Summary When manually adding an MCP server and the connection attempt fails, MCP Inspector still creates a server entry that cannot be edited or removed through normal means. The failure is also silent (no visible error/toast). Later, if a user tries to import a server with the same name from a client config file, the import fails with an "already exists" error, even though the original entry never successfully connected and isn't visibly manageable. Environment Tool: MCP Inspector v2 Affected transports: , (manual configuration flow) Steps to Reproduce See reproduction steps in #1918 mcp inspector must be ran from within docker container. 1. MCP Inspector > Add Server > Add manually. 2. Set a server name. 3. Select transport or . 4. Enter a URL that will fail to connect (in my case, the server returned HTTP 500 because required headers/cookies could not be set — see companion header/cookie ticket). 5. Submit. Observe: the failure is silent — no error message or indication that the connection failed. 6. Now go to MCP Server > Add Server > Import from client config > From file... 7. Select a valid client config file that contains a server entry with the same name used in step 2. 8. Observe: an error stating the MCP server already exists. Expected Behavior A failed manual connection attempt should surface a clear, visible error to the user (not fail silently). A server entry that failed to connect should either not persist, or should be visible/editable/removable in the UI so the name can be reused or the entry fixed. Importing a server from client config should not be blocked by a failed/ghost entry that the user cannot see or manage. Actual Behavior The failed manual connection attempt produces no visible error (silent failure). A "ghost" entry is created under the hood for that server name that does not appear to be editable/removable through the normal UI. Attempting to import a client-config server with the same name later fails with a duplicate-name/"already exists" error, with no clear path to resolve it (e.g., rename, delete the ghost entry, or overwrite). Suggested Fix 1. Surface connection failures to the user immediately (toast/inline error) instead of failing silently. 2. Do not persist a server entry if the connection attempt fails, OR make any persisted-but-failed entries visible and manageable (edit/delete) in the server list. 3. When importing from client config and a name collision occurs, provide the user an option to overwrite/replace the existing (especially failed/unconnected) entry rather than only blocking with an error.

brandonja991 · 1h ago
cliffhall
fix(web): surface a failed post-write server-list reloadMergedPR

Closes #1914 Takes the re-scoped remainder of #1914: surface a post-write server-list reload failure instead of closing the modal silently. The ghost entry and the import collision were #1848, fixed in #1945; the swallow that made the failure invisible survived it. The bug ' mutators await , and records a failed list read in the hook's state rather than throwing. renders only for something threw — so a successful followed by a failed closes the Add Server modal as though everything worked. No inline error, no toast, and no new row either, because the list read is what would have produced it. #1848 (a keyring-less container 500'ing every ) removed the trigger, not the swallow, so any other cause of a failing list read reproduces it. Verified against current before touching anything — see the before shot. The fix Split the list read into , and give the mutators their own . It records the failure in exactly as before and then rethrows. is unchanged for the two callers that have nowhere to put a rejection — the mount effect and the SSE loop. The thrown message says the write landed, because it did; only reading the list back failed: The server was added, but the server list could not be reloaded: keyring unavailable A bare "could not add server" would send the user straight into a retry that trips the duplicate-id check — the dead end the original report described. The rejection is its own exported type rather than a bare , because a rejection after a successful write means the opposite of what every existing consumer assumed. Review found five places that had to branch on it, and that count is the argument for the type — scoping the throw to would have hidden four of them rather than fixed them: One more state was reachable only because the modal now stays open: an edit modal whose target had been renamed kept pointing at the old id, so when the row landed under the new one went undefined and 's own reset blanked the open form and cleared the error it was showing. An edit/clone modal whose target has left the list now closes — adjusted during render via , so no frame paints the blanked form. That also covers the case that was always latent there: an external edit removing the server being edited. Proof Both shots: Add Server → manual → / , with forced to 500 from the moment Add is clicked (the keyring-less-container shape). Before — the modal closed, is nowhere in the list, and nothing says anything went wrong: !before After — the modal stays open with the reason, and says the server was saved: !after Tests drives the real Hono app through a that lets the seed load and the write through, then fails every subsequent list read: / / / each reject with the "was `v2/mainaddServererrorServerListReloadErrorErrorrefresh()App.test.tsxuseImportClientConfig.test.tsxserversnpm run ci` green.

cliffhall · 1h ago
Izelude
Make complex _meta possibleClosedIssue

Is your feature request related to a problem? Please describe. For my use cases i need to be able to attach complex (json) _meta to mcp requests. With the current implementation only pairs are possible. Describe the solution you'd like Replace (maybe optionally) the string, string pair list of _meta in the server settings with a json editor. Describe alternatives you've considered I had already implemented this behaviour for v1 and opened a PR (https://github.com/modelcontextprotocol/inspector/pull/1159) Additional context Since you are no longer accepting direct contributions, i will not open a PR for it, but please consider adding this functionality. There is nothing in the MCP spec which restricts custom _meta data to just string,string pairs so i don't think the main mcp testing tool (which the inspector is for many people) should do that.

Izelude · 1h ago
Structured data for AI agents

Repository: modelcontextprotocol/inspector. Description: Visual testing tool for MCP servers Stars: 10733, Forks: 1496. Primary language: TypeScript. Languages: TypeScript (95.3%), JavaScript (4.2%), CSS (0.3%), HTML (0.1%), Dockerfile (0%). Homepage: https://modelcontextprotocol.io/docs/2026-07-28/tools/inspector Topics: cli, debug, mcp, tool, tui, web. Latest release: 2.3.0 (4d ago). Open PRs: 6, open issues: 46. Last activity: 3m ago. Community health: 75%. Top contributors: cliffhall, olaservo, jspahrsummers, ashwin-ant, richardkmichael, msabramo, felixweinberger, jerome3o-anthropic, BobDickinson, pulkitsharma07 and others.

·@ofershap

Replace github.com with gitshow.dev