Follow Discord
Sweep 08 Oct 2026 · 18:53Z Build v2.1.295 516 read Stable v2.1.286 Latest v2.1.295 Next v2.1.295 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · mcp

Recipes changeddocs/2026-07-28/tools/inspector/recipes

Nearest release: v2.1.295, published 5 hours after upstream edited the page. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Upstream edited this page at 8 Oct 2026 12:58 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 8 Oct 2026 13:07 UTC.

Upstream edited
Recorded here
Lines+132added
Lines−24removed
From line 1 where the diff opens
First seen 14 Aug 2026 this site's first read of the page
Recorded edits3to this page, all time

### Keeping your servers and secrets ### Health checks and other modes ## Keeping a connection open with mcpdo

The whole hunk

from line 1, old and new numbered
/
lines
from line 1
11# Recipes
22 
3> Practical guides for transports, importing configs, reviewing MCP Apps, Docker, and network hosting
3> Practical guides for transports, importing configs, reviewing MCP Apps, Docker, network hosting, and persistent connections with mcpdo
44 
55## Connecting stdio vs. HTTP servers
66 
from line 33
3333 
3434`--transport` accepts `http` (Streamable HTTP) and `sse`. If the server is protected, see [Authorization](/docs/2026-07-28/tools/inspector/authorization): no setup is needed in advance, because when the server answers `401` the Inspector runs the OAuth flow described there and retries the connection.
3535 
36For an HTTP server, also decide its [protocol era](/docs/2026-07-28/tools/inspector/protocol-eras). The default is `legacy`; set `modern` or `auto` in Server Settings (or `protocolEra` in the catalog file) to exercise the 2026-07-28 behavior.
36For an HTTP server, also decide its [protocol era](/docs/2026-07-28/tools/inspector/protocol-eras). The default is `legacy`; set `modern` or `auto` in Server Settings (or `protocolEra` in the catalog file) to exercise the 2026-07-28 behavior. For an ad-hoc target, pass it at launch with `--protocol-era legacy|auto|modern`.
3737 
3838## Importing an existing client config
3939 
from line 42
4242configs directly, and it also reads a server's own [MCP Registry](/registry/about) `server.json`.
4343 
4444Import merges into the active [catalog](/docs/2026-07-28/tools/inspector/configuration#choosing-servers)
45(the Inspector's writable server list), so existing entries aren't clobbered. If you'd rather
45(the Inspector's writable server list). When an imported server's id is already taken, you
46choose whether to overwrite, skip or rename it. If you'd rather
4647not touch your catalog at all, launch against the foreign file read-only instead:
4748 
4849```bash theme={null}
from line 64
6364 <Step title="Probe the security posture without calling the tool">
6465 ```bash theme={null}
6566 mcp-inspector --cli --transport http --server-url https://example.com/mcp \
66 --method tools/call --tool-name <tool> --app-info
67 --method tools/call --tool-name <tool> --app-info --advertise-apps
6768 ```
6869 
70 `--advertise-apps` makes the CLI claim MCP Apps support at `initialize`. It is off by default because the CLI cannot render an app, but a server that shows its app tools only to app-capable clients would otherwise report no app.
71 
6972 One JSON line on stdout; exit `0` if the tool has an app, `2` if not, so an `&&` chain short-circuits:
7073 
7174 ```json theme={null}
from line 101
98101 mcp-inspector --web &
99102 ```
100103 
101 Pinning `MCP_SANDBOX_PORT` matters here: the app's UI is served from a separate sandbox port that is dynamic by default, and your automation needs a fixed address to reach it.
104 Pinning `MCP_SANDBOX_PORT` keeps the address explicit: the app's UI is served from a separate sandbox port, and your automation needs to know where it is.
102105 </Step>
103106 
104107 <Step title="Navigate one deep link to a rendered widget">
from line 117
114117 
115118 | Selector | Attribute | Values |
116119 | - | - | - |
117 | `[data-testid="apps-form"]` | `data-app-status` | `ready` (on failure, `data-app-error` carries the reason) |
118 | `[data-testid="connection-status"]` | `data-status` | `connecting`, then `connected` or `error` (`data-error-message` has the detail) |
120 | `[data-testid="apps-form"]` | `data-app-status` | `idle`, then `loading`, then `ready` or `error` (on `error`, `data-app-error` carries the reason) |
121 | `[data-testid="connection-status"]` | `data-status` | `disconnected`, `connecting`, then `connected` or `error` (`data-error-message` has the detail) |
119122 | `[data-testid="connection-status"]` | `data-deeplink` | `parsed`, `rejected`, or `none` (`none` means no deep link was given, `rejected` means one was refused) |
120123 </Step>
121124</Steps>
from line 128
125128A container image is published to GitHub Container Registry for `linux/amd64` and `linux/arm64`:
126129 
127130```bash theme={null}
128docker run --rm -p 6274:6274 ghcr.io/modelcontextprotocol/inspector
131docker run --rm -p 127.0.0.1:6274:6274 ghcr.io/modelcontextprotocol/inspector
129132```
130133 
131134Read the [session token](/docs/2026-07-28/tools/inspector/web#the-session-token) from the container logs, or pin it with `-e MCP_INSPECTOR_API_TOKEN=<value>`.
132135 
133The image defaults to `--web`, bound to `0.0.0.0:6274` with browser auto-open off, and runs as a non-root user. It sets `DANGEROUSLY_BIND_ALL_INTERFACES=true` because a container must bind the wildcard address to be reachable through `-p`.
136The image defaults to `--web`, bound to `0.0.0.0:6274` with browser auto-open off, and runs as the non-root `node` user (uid `1000`). It sets `DANGEROUSLY_BIND_ALL_INTERFACES=true` because a container must bind the wildcard address to be reachable through `-p`.
134137 
135Its `HEALTHCHECK` probes the web UI, so add `--no-healthcheck` when running `--cli` or `--tui` (neither has a web server). `<target>` below is an [ad-hoc target](/docs/2026-07-28/tools/inspector/configuration#ad-hoc-targets): a positional stdio command, or `--server-url <url> --transport http`.
138<Warning>
139 **Keep the `127.0.0.1:` prefix on every published port.** A bare `-p
140 6274:6274` publishes on every host interface, putting a backend that spawns
141 processes, and the page that discloses its token, on your local network. The
142 image's `DANGEROUSLY_BIND_ALL_INTERFACES` covers the container's interfaces,
143 not the host's. See [Publish the port on loopback
144 only](/docs/2026-07-28/tools/inspector/security#publish-the-port-on-loopback-only).
145</Warning>
136146 
147To use the **Apps** tab, also publish the MCP Apps sandbox port, `6275`, and `6278` for an app that declares `_meta.ui.domain`. Publish each on the same port number inside and out, since the browser is handed the in-container port:
148 
137149```bash theme={null}
138docker run --rm --no-healthcheck ghcr.io/modelcontextprotocol/inspector --cli <target> --method tools/list
150docker run --rm -p 127.0.0.1:6274:6274 -p 127.0.0.1:6275:6275 \
151 ghcr.io/modelcontextprotocol/inspector
139152```
140153 
154### Keeping your servers and secrets
155 
156The server list, OAuth state and secrets live under `/home/node/.mcp-inspector`, in the container's writable layer, so `--rm` discards them and every run starts empty. Mount a volume there to keep them:
157 
158```bash theme={null}
159docker run --rm -p 127.0.0.1:6274:6274 \
160 -v mcp-inspector-data:/home/node/.mcp-inspector \
161 ghcr.io/modelcontextprotocol/inspector
162```
163 
164A container has no OS keychain, so where secrets go depends on that volume:
165 
166| Situation | Secret store | Survives a restart? |
167| - | - | - |
168| **No volume** on `/home/node/.mcp-inspector` | Memory | No, session only |
169| **With** that volume | `secrets.json` on the volume, mode `0600` | Yes |
170 
171If you bind-mount a host directory instead of a named volume, it keeps its host ownership, so on Linux add `--user "$(id -u):$(id -g)"` or `chown` it to uid `1000`, or saves fail with `EACCES`. Don't bind-mount the secrets file on its own: it isn't recognized as durable, and it can't be replaced atomically.
172 
141173<Warning>
174 **Mounting that volume turns on file storage of secrets, and without a key the file is plaintext.** Every OAuth token acquired, and every client secret and stdio `env:` value you save, is then written to `secrets.json` on the volume. It is readable by root and every member of the host's `docker` group, and by anyone who gets a backup, snapshot or copy of the volume.
175 
176 Give it a key, generated into a file that only you can read and that sits outside the volume, its backups and any repository:
177 
178 ```bash theme={null}
179 mkdir -p ~/.config/mcp-inspector
180 (umask 077 && openssl rand -base64 32 > ~/.config/mcp-inspector/secret-key)
181 ```
182 
183 Even encrypted, secrets on disk carry moderate risk. See [what the file store protects against](/docs/2026-07-28/tools/inspector/security#what-the-file-store-protects-against).
184</Warning>
185 
186Hand the key to the container **as a file** with `MCP_INSPECTOR_SECRET_KEY_FILE`, not as an environment variable. A key passed with `-e MCP_INSPECTOR_SECRET_KEY=…` is readable by anyone who can run `docker inspect` or `docker exec`.
187 
188<Tabs>
189 <Tab title="docker run">
190 ```bash theme={null}
191 docker run --rm -p 127.0.0.1:6274:6274 \
192 -v mcp-inspector-data:/home/node/.mcp-inspector \
193 -v "$HOME/.config/mcp-inspector/secret-key:/run/secrets/mcp_inspector_secret_key:ro" \
194 -e MCP_INSPECTOR_SECRET_KEY_FILE=/run/secrets/mcp_inspector_secret_key \
195 ghcr.io/modelcontextprotocol/inspector
196 ```
197 </Tab>
198 
199 <Tab title="Compose secrets">
200 ```yaml theme={null}
201 services:
202 inspector:
203 image: ghcr.io/modelcontextprotocol/inspector
204 ports: ["127.0.0.1:6274:6274"]
205 volumes: ["mcp-inspector-data:/home/node/.mcp-inspector"]
206 environment:
207 MCP_INSPECTOR_SECRET_KEY_FILE: /run/secrets/mcp_inspector_secret_key
208 secrets: [mcp_inspector_secret_key]
209 secrets:
210 mcp_inspector_secret_key:
211 file: ${HOME}/.config/mcp-inspector/secret-key
212 volumes:
213 mcp-inspector-data:
214 ```
215 </Tab>
216</Tabs>
217 
218Without Swarm, Compose secrets are bind mounts that keep the host file's owner and mode, so the `0600` key file must be owned by uid `1000`. On a Linux host where your uid is different, run `sudo chown 1000 ~/.config/mcp-inspector/secret-key` rather than loosening its mode. Supply the **same** key on every run.
219 
220If the key file is missing, unreadable or empty, or both key variables are set, the Inspector **refuses to read or write the secrets file** rather than falling back to plaintext, and says why in the log and in the settings dialogs' footer. Everything else about the store (selection order, location, permissions) is under [Where secrets are stored](/docs/2026-07-28/tools/inspector/configuration#where-secrets-are-stored).
221 
222### Health checks and other modes
223 
224The image's `HEALTHCHECK` probes the web UI at the address `HOST` binds. `--cli` and `--tui` have no web server, so the probe detects those modes from the container's arguments and reports healthy while they run. An external orchestrator (a Kubernetes probe, a Compose `healthcheck`) can call `GET /healthz` on the web port, which needs no token and returns only `{"status":"ok"}`.
225 
226`<target>` below is an [ad-hoc target](/docs/2026-07-28/tools/inspector/configuration#ad-hoc-targets): a positional stdio command, or `--server-url <url> --transport http`.
227 
228```bash theme={null}
229docker run --rm ghcr.io/modelcontextprotocol/inspector --cli <target> --method tools/list
230```
231 
232<Warning>
142233 **If you remap the published port, set `ALLOWED_ORIGINS`.** With `-p
143 8080:6274` the browser's origin becomes `http://localhost:8080`, which no
144 longer matches the in-container port, and connects will `403`. Either run `-e
145 CLIENT_PORT=8080 -p 8080:8080`, or set `-e
234 127.0.0.1:8080:6274` the browser's origin becomes `http://localhost:8080`,
235 which no longer matches the in-container port, and connects will `403`. Either
236 run `-e CLIENT_PORT=8080 -p 127.0.0.1:8080:8080`, or set `-e
146237 ALLOWED_ORIGINS=http://localhost:8080,http://127.0.0.1:8080`.
147238</Warning>
148239 
149240## Hosting on a network
150241 
151The Inspector binds `localhost` by default and its backend spawns processes, so treat exposing it to a network as a deliberate decision.
242The Inspector binds `127.0.0.1` by default and its backend spawns processes, so treat exposing it to a network as a deliberate decision.
152243 
153244The Inspector refuses to bind the **wildcard** all-interfaces addresses (`0.0.0.0`, `::`, and every equivalent spelling) unless you set `DANGEROUSLY_BIND_ALL_INTERFACES=true`. Binding a **specific** address is allowed with no opt-in, because that's one deliberate exposure rather than every interface at once, which is the shape DNS-rebinding attacks target.
154245 
from line 246
155246| Goal | What to do |
156247| - | - |
157248| **Reach it from another machine on the LAN** | `HOST=192.168.1.50`. The default origin allow-list follows the bind host, so `http://192.168.1.50:6274` is accepted with no further config. |
158| **Behind TLS or a reverse proxy** | The browser's `Origin` becomes the public origin, which won't match the bind host. Set `ALLOWED_ORIGINS=https://inspector.example.com`. |
249| **Behind TLS or a reverse proxy** | The browser's `Origin` becomes the public origin, which won't match the bind host. Set `ALLOWED_ORIGINS=https://inspector.example.com`, and for MCP Apps set `MCP_SANDBOX_FULL_ADDRESS` (see below). |
159250| **Wildcard bind (containers)** | Set `DANGEROUSLY_BIND_ALL_INTERFACES=true`. Loopback access still works out of the box; reaching it at a non-loopback address needs `ALLOWED_ORIGINS`. |
160251 
161252<Warning>
from line 261
170261 
171262Two further caveats when going off loopback:
172263 
173* **MCP Apps need their sandbox port reachable too.** It's a separate, dynamic-by-default port; pin it with `MCP_SANDBOX_PORT` and expose or forward it. The Docker image publishes only `6274`.
174* **MCP Apps can't render over TLS or at a bare IPv6 literal.** The sandbox URL is always plain `http`, so an `https://` page blocks the iframe as mixed content; and a bracketed IPv6 literal isn't a valid CSP host-source, so browse at a name or an IPv4 address.
264* **MCP Apps need their sandbox port reachable too.** It's a separate listener (`6275` by default, set with `MCP_SANDBOX_PORT`), so expose or forward it alongside the web port.
265* **Behind TLS or a reverse proxy, give MCP Apps their public addresses.** By default the sandbox is advertised as `http://<bind host>:6275`, which an `https://` page blocks as mixed content. Set `MCP_SANDBOX_FULL_ADDRESS` (for example `https://inspector-sandbox.example.com/sandbox`) and, for apps that declare `_meta.ui.domain`, `MCP_APP_ORIGIN_FULL_ADDRESS` (for example `https://inspector-apps.example.com`). Each needs its own hostname or port; an address that shares the Inspector's origin is refused.
266* **MCP Apps can't render at a bare IPv6 literal.** A bracketed IPv6 literal isn't a valid CSP host-source, so browse at a name or an IPv4 address.
175267 
176Whatever the shape: keep authentication on. Do not set `DANGEROUSLY_OMIT_AUTH` on anything reachable by anyone but you.
268Whatever the shape: keep authentication on. Do not set `DANGEROUSLY_OMIT_AUTH` on anything reachable by anyone but you. The reasoning is under [Security](/docs/2026-07-28/tools/inspector/security#the-web-backend-and-its-api-token).
269 
270## Keeping a connection open with mcpdo
271 
272When an exploration spans many calls (a multi-step flow over one session, a log you watch, an agent using a server's tools mid-session), reconnecting for every `--cli` invocation gets in the way. [mcpdo](/docs/2026-07-28/tools/inspector/mcpdo) holds the connection for you:
273 
274```bash theme={null}
275mcpdo connect my-server
276mcpdo @my-server tools/call --task start_job size:=large # task-augmented; blocks until the task finishes
277mcpdo @my-server tools/call get_job_report
278mcpdo @my-server logging/tail # long-lived; Ctrl-C to stop
279mcpdo disconnect my-server
280```
281 
282A connection runs one call at a time, so commands against the same connection from other shells wait their turn.
283 
284The daemon that holds the connection starts automatically and exits about a minute after the last connection closes. Read [The mcpdo connection daemon](/docs/2026-07-28/tools/inspector/security#the-mcpdo-connection-daemon) before using it on a shared machine.
177285 
178286## Development workflow
179287 
Feedback