docs: add an environment-variable reference (#2365)
Every variable that changes runtime behavior now lives on one page, docs/environment-variables.md: which client reads it, its default, and its effect. It covers three variables that were undocumented (MCP_LOG_FILE, MCP_INSPECTOR_LOG_DIR, LOG_LEVEL), the secret-store variables previously only in the Docker guide, and the Node TLS variables that answer #1936 (a self-signed https://localhost server). The README docs table, the migration guide, docker.md and the four client READMEs link to it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TRJ64MnDTg1TpzCTEV9Xva Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
3e728a3b8c
commit
2c1d3fdba8
8 changed files with 116 additions and 4 deletions
|
|
@ -76,6 +76,7 @@ Each client has its own README with client-specific detail:
|
|||
| [Publishing](./docs/publishing.md) | What ships in the tarball, the packaging invariants, and `pack:verify` |
|
||||
| [Docker](./docs/docker.md) | Running the container image — ports, volumes, and where secrets go |
|
||||
| [Migrating from v1 to v2](./docs/v1-to-v2-migration.md) | CLI flag mapping, `--config` vs. `--catalog`, the Node engine bump, env-var renames |
|
||||
| [Environment variables](./docs/environment-variables.md) | Every variable that changes runtime behavior — auth, ports, storage, the secret store, logging, proxies — plus the Node TLS variables for a self-signed server |
|
||||
| [MCP server configuration](./docs/mcp-server-configuration.md) | Which server(s) the Inspector connects to, and the config file format |
|
||||
| [Reviewing an MCP App](./docs/mcp-app-review.md) | The CLI-first → one-shot-web recipe for automated App-tool review |
|
||||
| [Smoke-testing an MCP server](./docs/cli-smoke-testing.md) | The connect → list → call → assert workflow for a shell or CI job: `--format json` + `jq`, the exit-code map, and keeping OAuth non-interactive |
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ When a server is loaded from a `--catalog`/`--config` file, its per-server setti
|
|||
|
||||
The file is the only durable way to give a run its roots: there is no roots flag, and `--method roots/set` applies only to that one short-lived connection. Roots configured for a server (the same field the web UI's Server Settings writes) are advertised at connect, so a server that asks for `roots/list` — `@modelcontextprotocol/server-filesystem` does, to learn its allowed directories — gets them.
|
||||
|
||||
**Environment-variable semantics.** `MCP_CATALOG_PATH` is honored only when no ad-hoc target is given (positional command, `--server-url`, or `--transport`) — so a shell that exports it can still run one-off ad-hoc invocations without hitting the catalog/ad-hoc conflict. `MCP_STORAGE_DIR` sets the storage directory used by the OAuth persist backend (`<MCP_STORAGE_DIR>/oauth.json`); the per-file `MCP_INSPECTOR_OAUTH_STATE_PATH` override still takes precedence over it.
|
||||
**Environment-variable semantics.** `MCP_CATALOG_PATH` is honored only when no ad-hoc target is given (positional command, `--server-url`, or `--transport`) — so a shell that exports it can still run one-off ad-hoc invocations without hitting the catalog/ad-hoc conflict. `MCP_STORAGE_DIR` sets the storage directory used by the OAuth persist backend (`<MCP_STORAGE_DIR>/oauth.json`); the per-file `MCP_INSPECTOR_OAUTH_STATE_PATH` override still takes precedence over it. Every variable the CLI reads is listed in [Environment variables](../../docs/environment-variables.md).
|
||||
|
||||
### HTTP proxy support
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ The launcher is the package that provides the global `mcp-inspector` binary (e.g
|
|||
|
||||
All configuration parsing, config-file loading, and server setup are handled by the app runners and by **core**; the launcher does not interpret config or env vars.
|
||||
|
||||
**Error reporting.** A `--cli` failure is routed through the CLI's own error sink, so `mcp-inspector --cli` preserves the CLI exit-code map (`1` usage, `2` no-app, `3` auth-required, `4` unreachable, `5` tool-error) and its machine-readable `{"error":{…}}` stderr envelope — the same as invoking the CLI bin directly. `--web` / `--tui` failures print a human-readable `Error: <message>` and exit `1` (append `MCP_DEBUG=1` for the stack).
|
||||
**Error reporting.** A `--cli` failure is routed through the CLI's own error sink, so `mcp-inspector --cli` preserves the CLI exit-code map (`1` usage, `2` no-app, `3` auth-required, `4` unreachable, `5` tool-error) and its machine-readable `{"error":{…}}` stderr envelope — the same as invoking the CLI bin directly. `--web` / `--tui` failures print a human-readable `Error: <message>` and exit `1` (append `MCP_DEBUG=1` for the stack). `MCP_DEBUG` (or `DEBUG`) is the only variable the launcher reads itself; the rest are listed in [Environment variables](../../docs/environment-variables.md).
|
||||
|
||||
## Web server-list flags (`--web`)
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ OAuth redirect URIs must match **exactly** what you register on the authorizatio
|
|||
|
||||
#### Flags
|
||||
|
||||
Variables with no flag of their own — the secret store, `MCP_INSPECTOR_LOG_DIR`, `LOG_LEVEL`, proxies — are listed in [Environment variables](../../docs/environment-variables.md).
|
||||
|
||||
| Option | Env | Description |
|
||||
| ----------------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--client-config <path>` | `MCP_CLIENT_CONFIG_PATH` | Install-level client config (default: `~/.mcp-inspector/storage/client.json`). |
|
||||
|
|
|
|||
|
|
@ -384,7 +384,7 @@ Storybook is first-class here because the components are presentational — each
|
|||
|
||||
## Auth token
|
||||
|
||||
The dev/prod backend guards every `/api/*` route with `x-mcp-remote-auth: Bearer <MCP_INSPECTOR_API_TOKEN>`. The browser recovers the token, in priority order (see `App.tsx` `getAuthToken()`): the `window.__INSPECTOR_API_TOKEN__` global injected into `index.html` on every page load (`server/inject-auth-token.ts`), then a `?MCP_INSPECTOR_API_TOKEN=…` query param, then `sessionStorage`. Injection is a no-op when auth is disabled (`DANGEROUSLY_OMIT_AUTH`). See the root [AGENTS.md](../../AGENTS.md) for the full rationale.
|
||||
The dev/prod backend guards every `/api/*` route with `x-mcp-remote-auth: Bearer <MCP_INSPECTOR_API_TOKEN>`. The browser recovers the token, in priority order (see `App.tsx` `getAuthToken()`): the `window.__INSPECTOR_API_TOKEN__` global injected into `index.html` on every page load (`server/inject-auth-token.ts`), then a `?MCP_INSPECTOR_API_TOKEN=…` query param, then `sessionStorage`. Injection is a no-op when auth is disabled (`DANGEROUSLY_OMIT_AUTH`). See the root [AGENTS.md](../../AGENTS.md) for the full rationale, and [Environment variables](../../docs/environment-variables.md) for every variable the backend reads.
|
||||
|
||||
## Host binding & the origin allow-list
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ If another process holds the lock and will not let go, the save **fails** rather
|
|||
|
||||
It is also what covers the lock being unavailable. This store exists for boxes where the usual mechanism isn't there, so a directory that can't hold a lock file — a read-only `$HOME`, a mount owned by another uid — makes the save proceed unlocked with a warning, rather than turning every `set` into a failure on exactly the deployments the store was written for.
|
||||
|
||||
Three env vars affect where the file lands. `MCP_INSPECTOR_SECRET_STORE=keyring|file|memory` picks the store outright, bypassing the probe. `MCP_INSPECTOR_SECRET_FILE` names the file. Failing both, the file follows `MCP_STORAGE_DIR` — the same variable that relocates OAuth tokens and `client.json` — so mounting a volume at your configured storage directory is enough to make secrets durable there.
|
||||
Three env vars affect where the file lands. `MCP_INSPECTOR_SECRET_STORE=keyring|file|memory` picks the store outright, bypassing the probe. `MCP_INSPECTOR_SECRET_FILE` names the file. Failing both, the file follows `MCP_STORAGE_DIR` — the same variable that relocates OAuth tokens and `client.json` — so mounting a volume at your configured storage directory is enough to make secrets durable there. These variables apply outside a container too; every runtime variable is listed in [Environment variables](./environment-variables.md).
|
||||
|
||||
**Upgrading from an image before this fix?** Earlier images did not create `/home/node/.mcp-inspector`, so Docker created the volume's mount point as `root` and the non-root `node` user couldn't write to it. An **empty** volume repairs itself on the first run of a current image (Docker applies the image directory's ownership to an empty volume), but one that already has files in it keeps its old `root` ownership and still fails with `EACCES`. Fix it once:
|
||||
|
||||
|
|
|
|||
107
docs/environment-variables.md
Normal file
107
docs/environment-variables.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# Environment variables
|
||||
|
||||
Every environment variable that changes how the Inspector behaves at runtime, in one place. Set them in the shell that launches `mcp-inspector` (or with `-e` for the [Docker image](./docker.md)).
|
||||
|
||||
The **Read by** column names the client whose process reads the variable: **web** is the Node backend that `--web` starts (the browser itself reads no environment), **CLI** and **TUI** are those clients, and **launcher** is the `mcp-inspector` bin that picks one of them. A variable read in shared `core/` code is marked with every client that reaches it.
|
||||
|
||||
Unless a row says otherwise, an unset variable and an empty one behave the same.
|
||||
|
||||
## Authentication and network exposure
|
||||
|
||||
These guard the web backend, which spawns processes on request. Read [Host binding and the origin allow-list](../clients/web/README.md#host-binding--the-origin-allow-list) before widening any of them.
|
||||
|
||||
| Variable | Read by | Default | Effect |
|
||||
| --------------------------------- | -------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `MCP_INSPECTOR_API_TOKEN` | web, CLI | a random token per launch | Bearer token guarding every `/api/*` route (`x-mcp-remote-auth: Bearer <token>`). Set it to use a known token instead of the generated one printed in the launch banner. The CLI reads it only to fill the `autoConnect` parameter of the deep link it emits. |
|
||||
| `MCP_PROXY_AUTH_TOKEN` | web | — | **Deprecated** v1 name for `MCP_INSPECTOR_API_TOKEN`, used only when the new name is unset. |
|
||||
| `DANGEROUSLY_OMIT_AUTH` | web | unset | Disables the API token entirely. ⚠️ **Any non-empty value turns auth off, including `false` and `0`** — unset the variable to keep auth on. |
|
||||
| `HOST` | web, CLI | `127.0.0.1` | Address the web server binds. An all-interfaces host (`0.0.0.0`, `::`, and equivalent spellings) is **refused** unless `DANGEROUSLY_BIND_ALL_INTERFACES` is enabled. The CLI reads it only to build its deep link. |
|
||||
| `DANGEROUSLY_BIND_ALL_INTERFACES` | web | off | Opts in to an all-interfaces `HOST`. Only `true` or `1` (case-insensitive) enable it, so `false` reads as off. The Docker image sets it. |
|
||||
| `ALLOWED_ORIGINS` | web | the loopback origins | Comma-separated origins allowed to call the API. **Replaces** the default list rather than adding to it, so list every form you browse from. Each entry must include the scheme (`http://localhost:6274`). |
|
||||
|
||||
## Ports
|
||||
|
||||
The web client needs **fixed** ports: the origin allow-list and the MCP Apps sandbox CSP are both derived from them.
|
||||
|
||||
| Variable | Read by | Default | Effect |
|
||||
| --------------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `CLIENT_PORT` | web, CLI | `6274` | Web UI port. Must be an integer in 1–65535; `0` (a dynamic port) is rejected at startup. |
|
||||
| `MCP_SANDBOX_PORT` | web, CLI | `6275` | Port of the MCP Apps sandbox server. An invalid value is ignored with a warning. |
|
||||
| `SERVER_PORT` | web | — | v1's proxy port, now only a fallback for `MCP_SANDBOX_PORT` when that is unset. |
|
||||
| `MCP_APP_ORIGIN_PORT` | web, CLI | `6278` | Port of the dedicated app-origin server, used only by an MCP App whose UI resource declares `_meta.ui.domain`. An invalid value is ignored with a warning. |
|
||||
|
||||
The CLI reads the three port variables and `HOST` only to build the deep link and port list it hands to a web session; it binds none of them.
|
||||
|
||||
## Behavior
|
||||
|
||||
| Variable | Read by | Default | Effect |
|
||||
| ------------------------ | ------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `MCP_AUTO_OPEN_ENABLED` | web, CLI, TUI | open | `false` never opens a browser; `true` always does. In the CLI and TUI it also governs the interactive OAuth flow, and `true` opens the browser even when stderr is not a TTY. Any other value behaves as unset. |
|
||||
| `MCP_CATALOG_PATH` | web, CLI, TUI | `~/.mcp-inspector/mcp.json` | Default writable catalog, used when no `--catalog` is given. The CLI honors it only when no ad-hoc target (positional command, `--server-url`, or `--transport`) is given. See [MCP server configuration](./mcp-server-configuration.md). |
|
||||
| `MCP_OAUTH_CALLBACK_URL` | CLI, TUI | `http://127.0.0.1:6276/oauth/callback` | Loopback redirect URL for the CLI/TUI OAuth flow. `--callback-url` takes precedence. |
|
||||
| `NO_COLOR` | CLI | unset | Any value disables ANSI styling in the CLI's human-readable output. |
|
||||
|
||||
## Storage and state
|
||||
|
||||
| Variable | Read by | Default | Effect |
|
||||
| -------------------------------- | ------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `MCP_STORAGE_DIR` | web, CLI, TUI | `~/.mcp-inspector/storage` | Storage directory. Relocates the OAuth state file (`oauth.json`) and the secrets file (`secrets.json`) for every client. For the **web** backend it also relocates `client.json`; the CLI and TUI find `client.json` through `MCP_CLIENT_CONFIG_PATH` instead. |
|
||||
| `MCP_INSPECTOR_OAUTH_STATE_PATH` | web, CLI, TUI | `<MCP_STORAGE_DIR>/oauth.json` | Names the OAuth state file outright. Lookup order: this variable, then `<MCP_STORAGE_DIR>/oauth.json`, then `~/.mcp-inspector/storage/oauth.json`. ⚠️ Setting `MCP_STORAGE_DIR` alone does not isolate a run if this variable is also exported. |
|
||||
| `MCP_CLIENT_CONFIG_PATH` | CLI, TUI | `~/.mcp-inspector/storage/client.json` | Install-level client config (CIMD, enterprise IdP). `--client-config` takes precedence. |
|
||||
|
||||
## Secret store
|
||||
|
||||
Where server secrets (headers, client secrets) are kept. The details — the keychain probe, the file format, encryption and locking — are in the [Docker guide](./docker.md); these variables apply to every install, not only containers.
|
||||
|
||||
| Variable | Read by | Default | Effect |
|
||||
| ---------------------------- | ------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `MCP_INSPECTOR_SECRET_STORE` | web, CLI, TUI | probe the OS keychain | `keyring`, `file`, or `memory` (case-insensitive) picks the store outright and skips the probe. Any other value is ignored with a warning. |
|
||||
| `MCP_INSPECTOR_SECRET_FILE` | web, CLI, TUI | `<MCP_STORAGE_DIR>/secrets.json` | Path of the file store. Falls back to `secrets.json` in `MCP_STORAGE_DIR`, then to `~/.mcp-inspector/secrets.json`. |
|
||||
| `MCP_INSPECTOR_SECRET_KEY` | web, CLI, TUI | unset (file is plaintext, `0600`) | Passphrase that encrypts the file store. Use a generated, high-entropy value. ⚠️ Changing or losing it makes the existing file unreadable; see the Docker guide before rotating it. |
|
||||
|
||||
When no store is configured, the choice also depends on whether the Inspector is running in a container, which it detects from `KUBERNETES_SERVICE_HOST` (or Docker's and Podman's marker files). That variable is set by the orchestrator, not by you.
|
||||
|
||||
## Logging and debugging
|
||||
|
||||
| Variable | Read by | Default | Effect |
|
||||
| ----------------------- | -------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `MCP_DEBUG` / `DEBUG` | launcher | off | Prints the full stack trace when the launcher exits on an error. Any value other than `0` or `false` (case-insensitive) turns it on. |
|
||||
| `MCP_LOG_FILE` | web | unset (no log) | Appends the web backend's structured (pino, JSON lines) log to this file, creating its directory if needed. |
|
||||
| `MCP_INSPECTOR_LOG_DIR` | TUI | `~/.mcp-inspector` | Directory of the TUI's `auth.log`. The TUI logs to a file so its output does not corrupt the terminal UI. |
|
||||
| `LOG_LEVEL` | TUI | `info` | Level of the TUI's `auth.log` (`trace`, `debug`, `info`, `warn`, `error`, `fatal`, `silent`). |
|
||||
|
||||
## Outbound proxy
|
||||
|
||||
| Variable | Read by | Default | Effect |
|
||||
| ------------------------------- | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `HTTPS_PROXY` / `HTTP_PROXY` | web, CLI, TUI | unset | Route connections to remote HTTP/SSE servers, including OAuth discovery and token requests, through a proxy. Lowercase forms are honored too. See [HTTP proxy support](../clients/cli/README.md#http-proxy-support). |
|
||||
| `NO_PROXY` | web, CLI, TUI | unset | Hosts exempted from the proxy. |
|
||||
|
||||
## Node.js variables
|
||||
|
||||
These belong to Node, not the Inspector, but they are the answer to some common connection problems. Because the web client connects to MCP servers **from its Node backend**, not from the browser, they apply to all three clients.
|
||||
|
||||
### Connecting to a server with a self-signed certificate
|
||||
|
||||
A server at `https://localhost`, or anywhere else with a self-signed or privately issued certificate, fails to connect with a generic fetch error, because Node does not trust the certificate. Trusting the browser's exception does not help — the browser is not the one connecting ([#1936](https://github.com/modelcontextprotocol/inspector/issues/1936)).
|
||||
|
||||
**Prefer trusting the certificate's CA.** `NODE_EXTRA_CA_CERTS` adds certificates to the ones Node trusts, and leaves verification on for every other connection:
|
||||
|
||||
```sh
|
||||
NODE_EXTRA_CA_CERTS=/path/to/your-ca.pem npx @modelcontextprotocol/inspector
|
||||
```
|
||||
|
||||
For a self-signed certificate with no separate CA, point it at the certificate itself. Node reads the variable once at startup, so it must be set before launching.
|
||||
|
||||
**Or turn verification off, for local development only:**
|
||||
|
||||
```sh
|
||||
NODE_TLS_REJECT_UNAUTHORIZED=0 npx @modelcontextprotocol/inspector
|
||||
```
|
||||
|
||||
⚠️ This disables certificate verification for **every** TLS connection the Inspector process makes — every MCP server, every OAuth authorization server and token endpoint — not just the one you are testing, and Node prints a warning saying so. A connection that would have been refused for impersonation is then accepted. Never set it in a shell profile, a shared environment, or a deployment.
|
||||
|
||||
| Variable | Default | Effect |
|
||||
| ------------------------------ | ------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| `NODE_EXTRA_CA_CERTS` | unset | PEM file of extra CA certificates to trust, in addition to Node's bundled ones. Read once at startup. |
|
||||
| `NODE_TLS_REJECT_UNAUTHORIZED` | `1` | `0` disables TLS certificate verification for the whole process. Development only. |
|
||||
|
|
@ -268,6 +268,8 @@ Stdout is otherwise compatible: the default `text` format still pretty-prints th
|
|||
|
||||
## Environment variables
|
||||
|
||||
This table maps v1 names to v2. Defaults, accepted values, and the variables with no v1 counterpart are in [Environment variables](./environment-variables.md).
|
||||
|
||||
| v1 | v2 | Notes |
|
||||
| ------------------------ | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `MCP_PROXY_AUTH_TOKEN` | **`MCP_INSPECTOR_API_TOKEN`** | Renamed, but the **old name still works** as a deprecated fallback when the new one is unset — an existing deployment keeps running while you migrate. Guards `/api/*` via `x-mcp-remote-auth: Bearer <token>`; the browser also receives it injected into `index.html`, so a bare reload keeps working. (The `?MCP_PROXY_AUTH_TOKEN=` **query param** has no such fallback — see [Web UI](#web-ui).) |
|
||||
|
|
|
|||
Loading…
Reference in a new issue