--method, prints the result, and exits. That makes it a good fit for CI pipelines, shell one-liners, and coding agents that need to verify a server change immediately.
mcp-inspector binary. Without a global install, prefix each command with npx @modelcontextprotocol/inspector instead, as above.
Choosing a server
The CLI accepts a positional command (stdio), a--server-url (HTTP/SSE), or a named server out of a catalog or config file:
--header flag overrides the file’s headers for that run while leaving its timeouts and OAuth in place.
Later examples abbreviate whichever of these forms you use, along with its --transport or --config/--server flags, as <server>.
The config file is the only durable way to give a run its
roots: there is no roots flag, and the
CLI has no
roots/set method. Roots configured for a server are advertised at
connect, so a server that calls roots/list (as
@modelcontextprotocol/server-filesystem does, to learn its allowed
directories) gets them.--catalog vs. --config, the -- separator, and the shared server-selection flags.
Methods
Stream- or session-only methods (
logging/tail, for example) are rejected, since a process that exits can’t hold a stream open.
Passing arguments
--tool-arg takes key=value and coerces each value by JSON-parsing it when it parses, so count=1 and zip=10001 both send numbers. A value that is not valid JSON (zip=012, a bare word) is sent as a string, and a string is then converted to the type the tool’s input schema declares for that property, so zip=012 against a numeric zip still sends 12:
--tool-args-json takes the whole argument object at once and skips the key=value parsing, so {"zip":"10001"} sends the string "10001" rather than a number. The schema conversion still applies to string values, so this only keeps a string a string when the schema types that property as a string (or doesn’t declare it). The two flags are mutually exclusive:
Output
--format text (the default) pretty-prints for humans. --format json emits a single JSON object on stdout with no banners, so the whole output pipes cleanly:
Probing MCP Apps
--app-info reports whether a tool ships an MCP App UI (its ui:// resource, CSP, and permissions) without calling the tool, so a pipeline can decide whether it needs a browser before invoking anything:
0, one with no app exits 2, and a missing tool exits 5, so a typo isn’t mistaken for “no app”. A probe failure (unreadable UI resource, malformed resourceUri) is reported in a resourceError field rather than aborting, so one bad tool never kills a whole listing.
The CLI can’t render an App, so by default it doesn’t advertise the MCP Apps extension at initialize. A server that exposes its App tools only to clients claiming App support will then look app-less (exit 2). Add --advertise-apps to claim the extension for that run.
tools/list --app-info always emits NDJSON (one line per tool) regardless of
--format; --format json reshapes only the single-tool output of
tools/call --app-info.Exit codes and error envelopes
Every non-zero exit maps to a stable failure class, so a caller can branch on why without scraping prose:
On any non-zero exit the CLI also writes a single JSON line to stderr:
code and message always, plus cause (the underlying error, such as a DNS failure), status (the HTTP status) and url when known. Query-string secrets in a URL are redacted. code is a stable name for the failure: auth_required, unreachable, tool_not_found, tool_is_error, no_app, schema_unportable, store_unavailable, oauth_state_unrecognized, or the catch-all error, among others.
Because it’s one line, a caller can parse it with 2>&1 | tail -1 | jq .error.
A tools/call that returns isError: true still prints its payload, but exits 5, so an && chain doesn’t proceed on a failed call.
Authorization in scripts
By default the CLI runs the same loopback OAuth flow as the TUI: it opens a browser and waits on a localhost callback that a CI job can’t complete. Two flags make non-interactive runs predictable:--stored-auth-only: never start interactive OAuth or step-up, and never auto-open a browser. Use tokens from the shared store if present, otherwise fail immediately withauth_required. This is the flag CI wants.--use-stored-auth: reuse a token that the web Inspector already obtained on this machine, refreshing it first when a refresh token is stored. It looks the token up by URL, so it requires--server-url.
auth_required rather than hanging for fifteen minutes on a callback nobody will complete.
See Authorization for the full flow, the web-to-CLI handoff, and --print-handoff.
Recipes
Verify a server in CI
CI gates
Three flags turn a check into a non-zero exit, each with its own code so a job can tell the failures apart:--strict(withtools/list): reports tool-schema portability problems in full on stderr (path, issue, suggested fix) and exits6if any is error-severity. Without it,tools/listprints only a one-line count. Under--format jsonthe findings are folded into the output asschemaFindings.--verify(withskills/listorskills/get): runs the SEP-2640 conformance and digest checks, writes one JSON report per skill on stdout, and exits7on a violation or8when the read bounds stopped it before every skill was checked.--skill-catalog-max-skillsand--skill-catalog-max-bytesset those bounds.--require-digests(with--verify): exits9for a skill that advertises no digests, instead of reporting it as unverifiable and exiting0.
Branch on the failure class
Smoke-test every tool that has a UI
Inspect a catalog without connecting
Editing the catalog
servers/add, servers/edit, and servers/remove write the catalog without connecting to anything. They go through the same code as the web client’s server list, so env values and client secrets land in the secret store exactly as they would from the UI, and a running web client picks up the change.
--catalog, MCP_CATALOG_PATH, or the default) can be written; --config is refused. With an in-memory secret store, a write that supplies -e values (and a --rename) is refused, since the secrets would be lost when the run exits.
Proxies
Connections to remote HTTP/SSE servers honor the conventional proxy variables:HTTPS_PROXY / HTTP_PROXY (and their lowercase forms) select the proxy and NO_PROXY exempts hosts. No Inspector-specific flag is needed, and the proxy agent is loaded lazily, so runs without a proxy pay nothing. The same applies to the web client’s backend.