How to Test an MCP Server With the MCP Inspector in a Cloud Sandbox
Updated October 27, 2026 .md
Short answer: Install @modelcontextprotocol/inspector with npm in a sandbox, run its web UI as a systemd service on 127.0.0.1:6274 with ALLOWED_ORIGINS set to the hosted origin, and host it with sbx host --localhost. Keep that URL private. The Inspector page hands its API token to anyone who loads it, and its backend runs any stdio command it is given.
The MCP Inspector is the MCP project's own test client. This guide runs it next to the server you are building, in a throwaway VM, and opens it in your browser. If you want an agent to use Sandboxer through MCP instead, see Local or hosted MCP for Cursor, Claude Code and Codex.
What we ran#
Everything below ran on production on October 27, 2026, in one small sandbox (2 vCPU, 4 GB): Inspector 2.12.0, the reference everything and filesystem servers 2.0.1, Node 22.23.3. Timings are single runs; browser timings were measured from our machine with headless Chromium.
| Check | Result |
|---|---|
npm i -g of the Inspector and both servers |
16.6 s |
CLI initialize over stdio, process start included |
0.47 s |
| Hosted URL without its token | 401 |
Hosted URL with its token, before setting ALLOWED_ORIGINS |
403 from the Inspector: host not allowed |
| Same, after setting it | 200, the Inspector page |
API call with a foreign Origin header |
403, "Invalid origin" |
| Connect from the browser, stdio or Streamable HTTP | 0.82 s |
Run the echo tool from the browser |
0.22 s |
sbx stop --wait, then sbx resume |
2.2 s and 1.3 s. Memory restored: same PIDs, page answered 200 |
A small sandbox costs $0.018/hour while it runs and nothing while it is stopped.
1. Install the Inspector and two real servers#
Inspector 2.x needs Node 22.19 or later. The base image has Node 22.23.3. The two reference servers declare Node 24 in their package.json; everything in this guide ran on Node 22 anyway.
sbx new --name mcp-inspector --type small
id=sb_8qpbsenfin # the id that sbx new printed
sbx exec --timeout 600 $id "sudo npm i -g --silent @modelcontextprotocol/inspector@2.12.0 \
@modelcontextprotocol/server-everything@2.0.1 @modelcontextprotocol/server-filesystem@2.0.1 && \
mcp-inspector --version && which mcp-server-everything mcp-server-filesystem"2.12.0
/usr/bin/mcp-server-everything
/usr/bin/mcp-server-filesystemPin the versions. The Inspector's own smoke-testing guide warns that npx @modelcontextprotocol/inspector resolves the latest release on every run. Swap in your own server the same way: install it in the sandbox, or git clone and build it there.
2. Script a check with the CLI first#
The CLI mode needs no browser and nothing hosted. It connects, runs one method, prints the result and exits with a code you can branch on. Run these inside the sandbox (sbx ssh $id, or wrap each in sbx exec $id "…").
Over stdio, the Inspector spawns the server itself:
mcp-inspector --cli mcp-server-everything --method tools/list --format json | jq -r '.result.tools[].name' | tr '\n' ' '
mcp-inspector --cli mcp-server-everything --method tools/call --tool-name echo \
--tool-arg message="hello from a sandbox" --format jsonecho get-annotated-message get-env get-header-params get-resource-links … get-roots-list
{"result":{"content":[{"type":"text","text":"Echo: hello from a sandbox"}]}}The filesystem server takes the directories it may touch as arguments. A read outside them is a tool error, and the CLI exits with 5:
mcp-inspector --cli mcp-server-filesystem /home/user/project --method tools/call \
--tool-name read_text_file --tool-arg path=/etc/hostname --format json; echo "exit=$?"{"result":{"content":[{"type":"text","text":"Access denied - path outside allowed directories: /etc/hostname not in /home/user/project"}],"isError":true}}
…
{"error":{"code":"tool_is_error","message":"Tool 'read_text_file' returned isError:true."}}
exit=5Over Streamable HTTP (the server from step 3 on port 3001), point the CLI at the URL. A path ending in /mcp selects the transport:
mcp-inspector --cli --server-url http://127.0.0.1:3001/mcp --transport http --method initialize --format json \
| jq -c '{server: .result.serverInfo.name, protocol: .result.protocolVersion}'
mcp-inspector --cli http://127.0.0.1:3001/mcp --method tools/call --tool-name get-sum \
--tool-arg a=2 --tool-arg b=40 --format json | jq -r '.result.content[0].text'{"server":"mcp-servers/everything","protocol":"2025-11-25"}
The sum of 2 and 40 is 42.A tool that doesn't exist also exits 5, with {"error":{"code":"tool_not_found",…}} on stderr. The full exit-code table is in the CLI README. That makes the CLI a good gate for an agent loop: change the server, run the check, read the exit code.
3. Run the server and the web UI as services#
Write a catalog with the servers you want in the UI. Passing --catalog also keeps the web UI from seeding its default sample list, which includes a remote server.
{
"mcpServers": {
"everything-stdio": { "type": "stdio", "command": "mcp-server-everything", "args": ["stdio"] },
"filesystem-stdio": { "type": "stdio", "command": "mcp-server-filesystem", "args": ["/home/user/project"] },
"everything-http": { "type": "streamable-http", "url": "http://127.0.0.1:3001/mcp" }
}
}Save it as /home/user/inspector/mcp.json. Then add two systemd units, so both come back if the sandbox ever boots from disk (long-running docs). Use your own sandbox's subdomain in ALLOWED_ORIGINS; sbx new printed it in the sandbox URL.
# /etc/systemd/system/mcp-everything.service
[Unit]
Description=MCP everything server (Streamable HTTP)
After=network-online.target
[Service]
User=user
Environment=PORT=3001
ExecStart=/usr/bin/mcp-server-everything streamableHttp
Restart=always
[Install]
WantedBy=multi-user.target# /etc/systemd/system/mcp-inspector.service
[Unit]
Description=MCP Inspector web UI
After=network-online.target
[Service]
User=user
WorkingDirectory=/home/user/inspector
Environment=HOME=/home/user
Environment=MCP_AUTO_OPEN_ENABLED=false
Environment=ALLOWED_ORIGINS=https://minty-vernal-plum-6274.box.sandboxer.com
ExecStart=/usr/bin/mcp-inspector --web --catalog /home/user/inspector/mcp.json
Restart=always
[Install]
WantedBy=multi-user.targetWe put the catalog and both units in one script, setup.sh, and piped it in with sbx exec -i:
sbx exec -i --timeout 120 $id "bash -s" < setup.sh
# setup.sh ends with:
# sudo systemctl daemon-reload && sudo systemctl enable --now mcp-everything mcp-inspector
# sudo ss -ltnp | grep -E ':(3001|6274|6275|6278) 'LISTEN 0 511 127.0.0.1:6274 0.0.0.0:* users:(("node",pid=864,fd=26))
LISTEN 0 511 127.0.0.1:6275 0.0.0.0:* users:(("node",pid=864,fd=21))
LISTEN 0 511 127.0.0.1:6278 0.0.0.0:* users:(("node",pid=864,fd=22))
LISTEN 0 511 *:3001 *:* users:(("node",pid=863,fd=21))The Inspector binds 127.0.0.1 by default and refuses an all-interfaces bind unless you set DANGEROUSLY_BIND_ALL_INTERFACES. Leave it on loopback. The everything server has no bind option and listens on all interfaces. That is fine here: Sandboxer's proxy reaches only ports you host, and the sandbox firewall closes its public IPv6 address except SSH (Hosting).
There is no keychain in a headless VM, so the Inspector logs that it keeps secrets (OAuth tokens, stdio env values) unencrypted in ~/.mcp-inspector/secrets.json. Its secret storage doc covers encrypting that file. Read the startup banner with sudo journalctl -u mcp-inspector. It prints the API token, so don't paste it anywhere.
4. Host the UI on a private URL#
sbx host $id 6274 --localhost --title "MCP Inspector"https://minty-vernal-plum-6274.box.sandboxer.com?_token=<token>--localhost makes the sandbox relay the connection to 127.0.0.1:6274 (apps that listen on localhost). Without the hosted token, Sandboxer's proxy answers 401 and the Inspector never sees the request. With it, the first response sets a cookie and redirects to /.
Our first run had no ALLOWED_ORIGINS line. The token URL got past Sandboxer's proxy, and then the Inspector refused the page:
403
Forbidden: Host "minty-vernal-plum-6274.box.sandboxer.com" is not allowed. To serve the Inspector under this name, add it to MCP_INSPECTOR_ALLOWED_HOSTS (or to ALLOWED_ORIGINS).The proxy passes your browser's Host header through, and the Inspector checks it to block DNS rebinding. ALLOWED_ORIGINS fixes both checks at once: its hosts are admitted to the Host allow-list, and the origin is accepted on the /api/* calls a browser makes. Use the https:// origin with no port, exactly as the browser sends it. The variable replaces the default list, so add http://localhost:6274 too if you also reach the UI through an SSH tunnel (origin allow-list).
After a restart, the page loaded (200, <title>MCP Inspector</title>). We then called the Inspector's API directly, with the hosted cookie:
Request to /api/servers |
Status |
|---|---|
| No Inspector token | 401 |
Inspector token, no Origin |
200 |
Inspector token, Origin: https://evil.example |
403 Invalid origin. Request blocked to prevent DNS rebinding attacks. |
Inspector token, Origin set to the hosted origin |
200 |
5. Connect and call tools from the browser#
Open the full token URL. The Servers screen lists the three catalog entries. Flip the switch on everything-stdio and the backend spawns mcp-server-everything stdio inside the sandbox. In our runs:
- stdio: connected in 0.82 s, MCP 2025-11-25. The Tools tab listed 21 tools; the CLI saw 17. The web client advertises more client capabilities (sampling, elicitation), and the server adds tools for them.
echoreturnedEcho: hello from the hosted Inspectorin 0.22 s. - Streamable HTTP:
everything-httpconnected in 0.82 s andechoreturnedEcho: over Streamable HTTP. The Inspector's backend, not your browser, makes this connection, so the server sees no browserOrigin. The everything server refuses browser origins other than localhost. - Filesystem over stdio: 14 tools.
list_allowed_directoriesreturned/home/user/project.
The Logs, Protocol and Console panels on the right show the JSON-RPC traffic and the server's log messages. Out of scope here: the MCP Apps tab. It renders in a second listener on port 6275, which this setup doesn't host.
Why the Inspector must never be public#
Don't run sbx host $id 6274 --public, and never set DANGEROUSLY_OMIT_AUTH. Two facts from the Inspector's own docs and our run explain why:
- The page gives away the token. The server injects its API token into
index.htmlon every page load (auth token). We found it in the HTML we fetched. The token stops other websites from calling your Inspector. It does not stop anyone who can open the page. - The backend launches commands. A stdio server is a command line, and the backend runs it as
user, who hassudoin a sandbox. Anyone who can reach the UI can add a "server" whose command is any shell command.
So a public Inspector URL is a remote shell on that VM. The private hosted URL is the lock. Treat the full URL like a password (Hosting). Run sbx host hide $id 6274 when you are done, or delete the sandbox. A sandbox for testing also keeps the blast radius small: an unknown MCP server you try runs in the VM, not on your laptop.
After stop and resume#
sbx stop --wait $id
sbx resume $idStop took 2.2 s and resume 1.3 s, and resume reported memoryRestored: true. Both services kept their PIDs (948 and 863), and the hosted page answered 200 with the same cookie. The hosted URL and its token are kept across stop and resume. If a resume boots from disk instead (it can on a different CPU type), the processes are gone but the enabled units start again. Your browser tab then needs a reload, because the Inspector makes a new API token each time it starts.
FAQ#
Does the MCP Inspector need two hosted ports?#
Not in version 2. One port, 6274, serves the UI and the /api/* backend, so one hosted URL is enough. Version 1 had a separate MCP proxy on 6277 with MCP_PROXY_AUTH_TOKEN; version 2 has no proxy, and that variable is a deprecated name for MCP_INSPECTOR_API_TOKEN (v1 to v2 migration).
Can I use the Inspector's CLI against a server outside the sandbox?#
Yes. --server-url takes any Streamable HTTP or SSE URL, and --header adds headers such as an API key. We only tested servers inside the sandbox.
How do I test my own MCP server instead of the reference ones?#
Clone and build it in the sandbox, then add it to mcp.json as a stdio command or an HTTP URL. Or pass it to the CLI directly, as in step 2.
Is it safe to run an MCP server I don't trust this way?#
It runs in a microVM, not on your laptop, which is the point. Don't give that sandbox secrets you wouldn't hand the server; create it with sbx new --no-env so it gets none of your secrets (CLI).
Related#
More guides
For running AI-written code we rank Sandboxer, Freestyle and E2B first: each gives code its own VM with an API or SDK and can pause with memory. Pick Daytona for Windows or GPUs, Modal or Northflank for GPUs at scale, E2B or Northflank to run in your own cloud, and Vercel Sandbox if your app lives on Vercel.
For coding agents we rank Sandboxer first: a full Ubuntu VM with Claude Code and Codex preinstalled, Docker, and stop-and-resume that keeps the agent's session in memory. Freestyle and Amp Orbs also keep memory across pauses. Pick E2B for prebuilt agent templates on open source, Docker Sandboxes for free local runs, and Codespaces if you live in GitHub.
The best Daytona alternatives depend on why you're switching. For a full Linux microVM on every sandbox with memory-saving stop and Docker, we rank Sandboxer first. E2B suits open source and self-hosting, Modal massive short-job fleets, Northflank bring-your-own-cloud with GPUs, and Vercel Sandbox apps on Vercel.
If you're leaving E2B for longer sessions or a fuller machine, we rank Sandboxer first, then Freestyle: both give each sandbox a full Linux VM that keeps memory across pauses, with Docker and no short session cap. Pick Daytona for Windows or GPUs, Northflank for bring-your-own-cloud, and stay on E2B if you need open source.