Commit graph inspector/Dockerfile
Author SHA1 Message Date
cliffhall
fafc8367b8
fix(web): move the app origin off 6276, bound the publish route
Copilot review round 2 on #2100, plus a collision the review did not find.

The default app-origin port was 6276 — which is already
RUNNER_OAUTH_CALLBACK_DEFAULT_PORT, the CLI/TUI loopback OAuth callback.
That port is fixed precisely so an OAuth app can pre-register
http://127.0.0.1:6276/oauth/callback, which makes it the one listener here
that cannot move when something takes it first. A running --web would hold
it and break a later --cli/--tui OAuth flow at its registered redirect URI,
and docs/mcp-app-review.md has those two running side by side. This
listener has a documented dynamic fallback, so this listener yields: the
default is now 6278 (6277 skipped — v1's retired proxy port, which the
migration guide tells people to stop forwarding). A test asserts the two
defaults stay distinct.

Review comments:

- POST /api/app-document now applies hono's bodyLimit BEFORE parsing. The
  8MiB check ran after c.req.json() had buffered and parsed the whole
  request, and bounded only `html` — so bulk in `csp`, or in keys the route
  never reads, was fully materialized before the 413.
- `csp` is validated as an HTTP header value and length-bounded. The
  controller hands it to res.writeHead() verbatim, where Node throws
  ERR_INVALID_CHAR synchronously — inside the handler for a later,
  unrelated request, outside this route's error handling.
- The app-origin port now propagates everywhere the other two do: the CLI
  --print-handoff port-forward command (and its test), the Dockerfile's ENV
  and EXPOSE, and the remote-review SSH recipe in docs/mcp-app-review.md.
  Without it an app declaring _meta.ui.domain is unreachable through every
  documented remote workflow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177mPHAdECD18nLLCTwR5rh
Signed-off-by: cliffhall <cliff@futurescale.com>
2026-08-24 08:45:40 -04:00
cliffhall
adde9ccf94 fix: bind loopback by address and pin the sandbox port so containers work
Two defaults that each break the Inspector inside a container.

#1951 — the web UI never loads in a VS Code dev container. `HOST` defaulted
to the NAME `localhost`, and `server.listen(port, host)` resolves a name
through `dns.lookup` and binds the SINGLE address it gets back. Since Node 17
the default result order is `verbatim`, so on a glibc Linux host — whose
/etc/hosts maps localhost to both 127.0.0.1 and ::1 — that is `::1`. The
server bound IPv6 loopback only and refused every IPv4 client, including the
dev container port forwarder. It presents as an endless spinner rather than a
connection error because the forwarder accepts on the host side before failing
to connect inward. Default to the ADDRESS 127.0.0.1 instead; an explicit
HOST=localhost is still honored as typed.

#2008 — the Apps tab is unreachable in any container. The MCP Apps sandbox
listened on an OS-assigned port, so it changed every run and could never be
named in a devcontainer `forwardPorts`, a `docker run -p`, or a tunnel. Default
it to a fixed 6275, mirroring CLIENT_PORT's fixed 6274, and retry once on an
OS-assigned port (loudly) if it is taken — the collision a dynamic default
never had.

Verified in the reporter's exact image
(mcr.microsoft.com/devcontainers/typescript-node:4-24-trixie): both listeners
come up on 127.0.0.1 and answer 200 on 6274 and 6275/sandbox.

Also EXPOSE 6275 and set MCP_SANDBOX_PORT in the image, and document both
defaults in the web README, the migration guide, and the root README's Docker
section.

Signed-off-by: cliffhall <cliff@futurescale.com>
2026-08-15 12:33:27 -04:00
cliffhall
3173b264f7 fix(docker): publish on loopback by default; make a mounted state dir writable
Closes #1964.

- README's Docker recipes dropped v1's `127.0.0.1:` prefix, so
  `-p 6274:6274` published the Inspector on every host interface. The
  container's `HOST=0.0.0.0` governs the container's interfaces, not the
  host's, so `DANGEROUSLY_BIND_ALL_INTERFACES` never covered this. Verified
  against a real container: from a LAN peer, `GET /` served the injected
  `MCP_INSPECTOR_API_TOKEN`, and that token then unlocked `/api/*` with no
  `Origin` header (server.ts:241 allows origin-less requests). Restore the
  prefix on both recipes, on the port-remap examples, and in the web README.

- Adding a server failed inside the container the moment you mounted a
  volume to keep it. Docker seeds a named volume's ownership from the
  image's directory at the mount point, and creates it `root:root` when
  that directory is absent — so the non-root `node` user got
  `EACCES ... open '/home/node/.mcp-inspector/mcp.json.tmp-*'`. Create the
  dir in the image, owned by `node`, and document the volume recipe (plus
  the bind-mount `--user` caveat) — without a volume the catalog lives in
  the writable layer and `--rm` throws it away.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFXDQBEjvjCEBhmYkmw79a
2026-08-11 09:34:41 -04:00
Cliff Hall
cea22d86b8
feat(web): enforce localhost-only binding; expand loopback origin allow-list (#1796)
Closes #1795.

Enforce localhost-only binding in code (the "never 0.0.0.0" rule was only a spec note): refuse an all-interfaces HOST — across every spelling the OS binds as the wildcard (inet_aton decimal/octal/hex, IPv6, IPv4-mapped, zone-scoped, IDNA/Unicode) — unless DANGEROUSLY_BIND_ALL_INTERFACES=true (Docker's sanctioned opt-in). Fixes the reported stdio-connect 403 by expanding the default loopback origin allow-list to all three interchangeable forms.

Hardening surfaced across an extended review: closes an IDNA/fullwidth wildcard bypass, a blank-ALLOWED_ORIGINS fail-open, and the CLI/TUI OAuth-callback wildcard bind (loopback-only now, RFC 8252); canonicalizes the origin allow-list the way a browser does; derives frame-ancestors from the real embedder list (IPv6-literal exclusion verified in headless Chromium); strictPort on all three bind points; and preserves the CLI exit-code map + JSON envelope through the mcp-inspector --cli launcher path. Shared host normalization lives in core/node/hostUrl.ts. Docs + tests + smokes throughout.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-26 22:15:10 -04:00
cliffhall
070f02e85f review: set HOME explicitly + note healthcheck assumes --web
Follow-up on the re-review's two optional notes:

- Set `ENV HOME=/home/node` so the runtime-state dir (core/storage/store-io.ts
  resolves it from HOME) is explicit, rather than relying on the "." → WORKDIR
  fallback that happens to work because Docker doesn't set HOME on USER.
- Document that the HEALTHCHECK assumes the default --web mode; suggest
  `--no-healthcheck` for --cli/--tui runs (no web server to probe).

Re-verified: build ok, HOME=/home/node, runs as node (uid 1000), `/` → 200,
health → healthy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-11 09:47:25 -04:00
cliffhall
f5e095e8f0 review: harden the Docker image + align GHCR action versions
Address PR #1648 review:

- Bump the GHCR action majors to match current v1 (docker/login-action v4,
  docker/metadata-action v6, docker/setup-buildx-action v4,
  actions/attest-build-provenance v4) to avoid Dependabot churn.
- Make the image tags explicit (`type=semver` + `flavor: latest=true`) instead
  of relying on `flavor.latest=auto`, so a release lands both the version tags
  and `:latest` — the README's bare `ghcr.io/…/inspector` URL won't 404.
- Run the container as the non-root `node` user with /home/node as workdir (its
  writable home is where the inspector stores the catalog / OAuth state).
- Add a HEALTHCHECK that probes `/` via Node's global fetch (no curl in slim).
- Trim specification/ and docs/ from the build context. (test-servers/ stays —
  `tsc -b` typechecks the integration tests that import its alias, so excluding
  it breaks the build.)

Re-verified locally: `docker build` succeeds, the container runs as uid 1000
(node), serves `/` (200), and the HEALTHCHECK reports healthy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-11 09:43:04 -04:00
cliffhall
941bf13587 ci(docker): add v2 Dockerfile + GHCR publish job (#1646)
v1 published a container image but v2 arrived with no Dockerfile (the
client/server/cli layout is gone). Add a v2 image for the single-package /
launcher architecture and wire the release-gated GHCR publish job.

- `Dockerfile`: two-stage build — stage 1 installs + `npm pack`s the publishable
  tarball; stage 2 `npm install -g`s that tarball, so the image ships the exact
  same artifact as npm with a clean `mcp-inspector` bin. Defaults to `--web` on
  `0.0.0.0:6274`, browser auto-open off. `.dockerignore` keeps the context lean
  and forces a clean in-container install/build.
- `publish-github-container-registry` job in main.yml (ported from v1):
  release-gated, multi-arch (amd64/arm64) build+push to GHCR with a provenance
  attestation. Independent of the npm `publish` job.
- README: document the image + how to run it; the release section now covers
  both publish jobs.

Verified locally: `docker build` succeeds, the container serves `/` (HTTP 200
with the injected token) and dispatches `--cli` / `--tui`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-11 00:36:39 -04:00
cliffhall
442bb68532 Remove all from v2/main 2025-11-28 13:49:31 -05:00
cliffhall
98becdf5e0 In Dockerfile
- use current-alpine3.22 AS builder
- The node:24-slim has a high vulnerability CVE as yet unfixed which allows running commands on the host as root
2025-08-25 11:41:44 -04:00
cliffhall
6853dcf42d In Dockerfile
- use current-alpine3.22 AS builder
- The node:24-slim has a high vulnerability CVE as yet unfixed which allows running commands on the host as root
2025-08-25 11:31:54 -04:00
Aaron Powell
f73894fbf0 Allowing setting of the ports at the environment variable level so the apps are aware of them 2025-06-04 12:30:44 +10:00
Aaron Powell
769d045c16 Converting to multi-stage dockerfile 2025-06-04 12:18:43 +10:00
Aaron Powell
ef6d84933a
Apply suggestions from code review
Co-authored-by: John McBride <jpmmcbride@gmail.com>
2025-06-02 13:49:38 +10:00
Aaron Powell
6ee7627ee9 Initial work for adding a Dockerfile and creating an image for inspector
Fixes #237
2025-04-03 14:17:19 +11:00