Repository navigation
Add v2 Dockerfile + GHCR publish job - #1648
Conversation
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
|
@claude review |
|
Claude finished @cliffhall's task in 1m 46s —— View job Review: v2 Dockerfile + GHCR publish job
Overall this is a clean, well-documented PR. The two-stage "pack-then-install-the-tarball" approach is the right call — the image ships the exact artifact npm publishes, so there's no drift between
Nothing blocking. A few observations, roughly in priority order. 1. Action major versions are behind current v1 (
|
| Action | This PR | v1 (origin/main) |
|---|---|---|
docker/login-action |
@v3 |
@v4 |
docker/metadata-action |
@v5 |
@v6 |
docker/setup-buildx-action |
@v3 |
@v4 |
actions/attest-build-provenance |
@v3 |
@v4 |
(setup-qemu-action@v3 and build-push-action@v6 already match.) These all still work, but bumping to match v1 avoids immediate Dependabot churn and keeps the two workflows consistent. Worth aligning while it's fresh. (Note: I can't edit files under .github/workflows/ — GitHub App permissions block workflow modifications — so this is a suggestion for you to apply.)
2. Confirm the :latest tag actually gets pushed
The README's primary example is docker run --rm -p 6274:6274 ghcr.io/modelcontextprotocol/inspector — i.e. the implicit :latest. docker/metadata-action is used with no tags: input, so it relies on the default tag set + flavor.latest=auto. This mirrors the v1 config exactly, so if ghcr.io/modelcontextprotocol/inspector:latest is populated today it'll keep working — but since the release push can't be exercised outside a real release (as the PR notes), it's worth a conscious check on the first release that both the version tag and latest land, so the README's bare-URL example doesn't 404. If you want to be explicit rather than rely on auto, an explicit tags: block with type=semver + flavor: latest=true removes the ambiguity.
3. Container runs as root (Dockerfile)
node:22-slim ships a non-root node user, but the image doesn't switch to it, so the inspector runs as root and writes its runtime state (catalog file, OAuth token storage) under /root. Adding USER node (and ensuring its home is writable) is a low-effort hardening step. Optional — not a regression from v1 — but good practice for a published image.
Minor / non-blocking
- No
HEALTHCHECK— optional, but aGET /probe would let orchestrators know when the web UI is ready. .dockerignoredoesn't excludetest-servers/orspecification/from the build context. They're installed/built in the builder but not in the packed tarball (rootfilesallowlist), so this is purely a slightly larger context, not a correctness issue.
Nice work verifying the build/run locally — the keyring/libsecret note (glibc via node:22-slim) is exactly the kind of thing that bites Alpine-based images, so good catch keeping it on -slim.
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
|
Thanks for the review! Applied all of it (re-verified with a local 1. Action versions — bumped to match current v1: 2. Explicit 3. Non-root — added 4. HEALTHCHECK — added a 5. Re-requesting review. @claude review |
|
Claude finished @cliffhall's task in 1m 6s —— View job Re-review: v2 Dockerfile + GHCR publish job
All five items are applied correctly. Verified against the code: 1. Action versions ✅ 2. Explicit 3. Non-root ✅ 4. HEALTHCHECK ✅ Node-global 5. One new, non-blocking observationThe Optional belt-and-suspenders: LGTM — clean, well-documented, and the local build/run verification (non-root uid, health |
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
|
Applied both optional notes:
Re-verified locally: build ok, runs as @claude review |
|
Claude finished @cliffhall's task in 39s —— View job Re-review:
|
Closes #1646
Decides + wires the v2 Docker image and GHCR publish (parent tracking: #1636). Decision: keep shipping a container image (v1 had one — dropping it would regress users), now built for the single-package / launcher architecture.
What's added
Dockerfile— two-stage build:npm packthe publishable tarball;npm install -gthat tarball into a slimnode:22-slimruntime.So the image ships the exact same artifact as npm, with a clean
mcp-inspectorbin. Defaults to--webon0.0.0.0:6274with browser auto-open off; args override to run--cli/--tui..dockerignorekeeps the build context lean and forces a clean in-container install/build.publish-github-container-registryjob inmain.yml(ported from v1): release-gated (needs: build), multi-arch (linux/amd64+linux/arm64) build & push toghcr.io/${{ github.repository }}with a provenance attestation. Independent of the npmpublishjob — a container failure won't block the npm publish.README — documents the image +
docker run/docker build, and the release section now covers both publish jobs.Verified locally
dockerwas available, so I build-tested the image (not just wrote it):docker build .→ success (~797 MB,node:22-slimkeeps glibc for the native@napi-rs/keyring).docker run -p 6274:6274→GET /returns 200 with the injected__INSPECTOR_API_TOKEN__; banner shows web + sandbox up, no keyring/libsecret errors.docker run … --cli --help→Usage: inspector-cli;--tui --help→Usage: mcp-inspector-tui.Notes
v2/main(non-default branch) —Closes #1646is a cross-reference, so close Decide + wire v2 Docker image (GHCR) — v2 has no Dockerfile #1646 + move its card to Done manually on merge, per AGENTS.md. This is the last open child of Create v2 publishing pipeline for main.yml (single inspector package) #1636.🤖 Generated with Claude Code