Skip to content

Read the II pairing from /version instead of guessing it from the hostname - #94

Merged
aterga merged 2 commits into
mainfrom
claude/mcp-production-deployment-d74205
Jul 28, 2026
Merged

Read the II pairing from /version instead of guessing it from the hostname#94
aterga merged 2 commits into
mainfrom
claude/mcp-production-deployment-d74205

Conversation

@aterga

@aterga aterga commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

The production dashboard reports:

II /mcp connect page served — FAIL · GET https://internetcomputer.org/mcp404, /mcp connect page not served

Production is healthy. The check was probing the wrong host.

The dashboard derived the Internet Identity origin by stripping the leading mcp. label off the MCP host. That encodes an assumption which only holds when the MCP server is a subdomain of its II — it maps mcp.beta.id.aibeta.id.ai correctly, but maps mcp.internetcomputer.orginternetcomputer.org, the marketing site, which legitimately 404s on /mcp. A red II section on a healthy deployment is worse than no check: it trains operators to ignore the dashboard.

Fixing only the value would have relocated the bug. One origin cannot describe a server that mounts more than one II, so whichever mount the hostname guess didn't name went unmonitored — silently, on every deployment. So the server now advertises its pairing and the dashboard reads it.

Related issues

None filed. Surfaced by the live production dashboard. Builds on #92, which made /mcp serve production II and put beta behind $MCP_SERVE_BETA.

Changes

  • GET /version gains an instances array — one entry per served mount: {name, mcp_path, ii_origin, ii_canister}. Built from the live McpServer handles, so it tracks $MCP_SERVE_BETA and honours II_URL / II_URL_PROD overrides instead of restating defaults a monitor could drift from. McpServer::instance() exposes what the server was built with.
  • The dashboard reads that array and runs the II-health section once per instance, with check ids and section titles suffixed per instance name (ii-mcp-flow:prod). deriveIiOrigin is deleted — no hostname arithmetic remains anywhere.
  • Advertised origins are still allowlist-validated before anything is fetched. They arrive in a remote response, so a misconfigured or compromised server must not be able to steer these probes at a third party. A rejected entry is reported as a warn rather than dropped, so the section never reads as fully monitored while covering less than it claims.
  • Honest degradation: a build with no instances[] (or an unreachable /version) now fails with "the server advertises no II instances" instead of confidently probing a guess. --ii / II_ORIGIN pins an origin for that case.
  • deploy.sh allowlist tightened. It pinned the dashboard's SSRF allowlist to the parent domain, because the derived II origin was a sibling host — on mcp.internetcomputer.org that quietly allowlisted all of internetcomputer.org. The II origins are now covered by the built-in id.ai suffixes, so it pins the deployment's own host instead.

Testing

Verified end to end against real hosts, not only by unit test.

  • Local server, MCP_SERVE_BETA=1 — dashboard discovers both mounts from /version and passes every check against the real id.ai and beta.id.ai:
    iiOrigins: ["https://id.ai", "https://beta.id.ai"]   source: advertised by the server at /version
    [pass] ii-health:prod   ii-mcp-flow:prod  200, /mcp connect page served   (backend rdmx6-jaaaa-aaaaa-aaadq-cai)
    [pass] ii-health:beta   ii-mcp-flow:beta  200, /mcp connect page served   (backend fgte5-ciaaa-aaaad-aaatq-cai)
    
  • Live production (mcp.internetcomputer.org, still running 80b8586, which predates instances[]) — now reports "advertises no II instances at /version (a build predating the instances[] field…)" instead of a false II outage.
  • Live production with --ii https://id.ai — all six II checks pass, confirming the pinned-override path.
  • /mcp-prod confirmed 404 on this build in both modes (production and MCP_SERVE_BETA=1); the instances list contains only what is actually routed.
  • Allowlist guard confirmed live: omitting MCP_STATUS_ALLOWED_HOSTS refuses the run rather than probing.
  • cargo build --locked --all-targets
  • cargo test --locked --all-targets — 139 + 7 pass
  • npm test --prefix monitoring/mcp-status — 25 pass (was 19; added coverage for advertised-instance parsing, allowlist rejection, missing instances[], per-instance id suffixing, and instance-aware suggestions)
  • cargo fmt --all / cargo clippy --all-targets: the tree is not clean at baselinecargo fmt --check flags 11 files and clippy reports 8 warnings on main, none in code this PR touches. Confirmed every fmt hunk in main.rs/lib.rs (lines 19, 39, 94, 101, 208, 518, 561, 569) falls outside the lines added here, and no clippy warning points at either file. Not running cargo fmt --all, which would reformat unrelated files and swamp the diff; there is no CI fmt gate.

Checklist

  • I have read the Contributing guidelines.
  • Docs (README / comments) updated for any user-visible change — monitoring/mcp-status/README.md question 2 rewritten, CLI --ii help updated, and the reasoning recorded next to the code.
  • No secrets, credentials, or internal-only information are included.

Note on rollout

The dashboard and the binary ship from the same commit, so they move together. Until a release-* tag carries this to production, the live dashboard will show the II section as undetermined rather than failed — accurate, and no longer a false alarm.


Generated by Claude Code

…tname

The production dashboard reported "II /mcp connect page served: FAIL — GET
https://internetcomputer.org/mcp → 404". Production was healthy; the check was
probing the wrong host.

The dashboard derived the Internet Identity origin by stripping the leading
`mcp.` label off the MCP host. That encodes an assumption which only holds when
the MCP server is a subdomain of its II: it maps mcp.beta.id.ai to beta.id.ai
correctly, but maps mcp.internetcomputer.org to internetcomputer.org — the
marketing site, which legitimately 404s on /mcp. A red II section on a healthy
deployment is worse than no check, because it trains operators to ignore it.

Fixing the value alone would have relocated the bug: one origin cannot describe
a server that mounts more than one II, so whichever mount the hostname guess
did not name went unmonitored everywhere, silently.

So the server now advertises the pairing and the dashboard reads it:

- GET /version gains an `instances` array — one entry per SERVED mount, with
  its name, mcp_path, ii_origin and ii_canister. Built from the live
  McpServer handles, so it tracks $MCP_SERVE_BETA and honours II_URL /
  II_URL_PROD overrides rather than restating defaults a monitor could drift
  from. McpServer::instance() exposes what it is built with.
- The dashboard reads that array and runs the II-health section once per
  instance, ids and section titles suffixed per instance name. deriveIiOrigin
  is gone; no hostname arithmetic remains.
- Advertised origins are still validated against the probe allowlist before
  anything is fetched — they arrive in a remote response, and a misconfigured
  or compromised server must not be able to steer these probes at a third
  party. A rejected entry is reported as a warn rather than dropped, so the
  section never reads as fully monitored while covering less.
- A build with no instances[] (or an unreachable /version) now fails with
  "cannot determine the pairing" instead of confidently probing a guess.
  --ii / II_ORIGIN pins an origin for that case.

deploy.sh pinned the dashboard's allowlist to the PARENT domain, because the
derived II origin was a sibling host. On mcp.internetcomputer.org that quietly
allowlisted all of internetcomputer.org. The II origins are now covered by the
built-in id.ai suffixes, so it pins the deployment's own host instead.

Verified end to end, not just by unit test: run against a local server with
MCP_SERVE_BETA=1, the dashboard discovers both mounts and passes every check
against the real id.ai and beta.id.ai; against live production (still running
80b8586, which predates instances[]) it now reports "advertises no II
instances" instead of a false II outage, and --ii https://id.ai makes it pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R8ZshwKmjD5fZ4Hs9dS6zh
@aterga
aterga requested a review from Copilot July 28, 2026 16:56
@aterga
aterga marked this pull request as ready for review July 28, 2026 16:57
@aterga
aterga requested a review from a team as a code owner July 28, 2026 16:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes false Internet Identity (II) outage reports in the MCP status dashboard by having the MCP server advertise its II pairing via GET /version and having the dashboard consume that authoritative mapping instead of deriving II origins from the MCP hostname.

Changes:

  • Server: GET /version now includes an instances[] array describing each served mount’s {name, mcp_path, ii_origin, ii_canister}.
  • Dashboard: removes hostname-based II derivation, parses instances[], and runs II health checks per instance (with suffixed IDs).
  • Deployment: tightens the SSRF allowlist pinning in deploy.sh and updates service commentary to reflect instance discovery via /version.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/main.rs Adds instances[] to /version, built from live McpServer handles.
src/lib.rs Exposes McpServer::instance() so /version can report II pairing.
monitoring/mcp-status/report.js Updates CLI rendering to display multiple II origins.
monitoring/mcp-status/README.md Updates docs to describe advertised-instance discovery and allowlist validation.
monitoring/mcp-status/config.js Replaces derived-II logic with parseAdvertisedInstances() + allowlist enforcement.
monitoring/mcp-status/cli.js Updates --ii help text to reflect “pin override” semantics.
monitoring/mcp-status/checks.test.js Adds/updates tests for advertised-instance parsing and per-instance behavior.
monitoring/mcp-status/checks.js Implements instance-aware linkage + II health sections and suggestions handling.
deploy/native/imcp-status.service Updates service commentary to match /version discovery behavior.
deploy/native/deploy.sh Pins SSRF allowlist to the deployment host (not the parent domain).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread monitoring/mcp-status/checks.js
Comment thread monitoring/mcp-status/checks.js Outdated
Comment thread monitoring/mcp-status/checks.js Outdated
Comment thread monitoring/mcp-status/config.js Outdated
Comment thread monitoring/mcp-status/report.js
Copilot caught a real break I missed: I updated the terminal renderer but not
the hosted web UI. public/index.html read `targets.iiOrigin` and
`facts.ii.canisterId`, so on the page Caddy actually serves at /status/ — the
one this whole change exists to fix — the header would have read
"II: (unresolved)" even with both instances discovered, and the canister line
would have vanished.

Rather than keep a back-compat single-instance shape alongside the new one,
update the consumers. Both live in this repo and ship with the server, so there
is no external reader to be compatible with, and a singular `iiOrigin` sitting
next to `iiOrigins` reintroduces exactly the failure this PR removes: a
consumer reads the singular field and silently reports one of N instances.

- public/index.html renders every advertised II with its name and canister id.
- facts.ii is renamed facts.iiInstances. The old key held ONE instance's facts,
  so anything still reaching for `facts.ii.canisterId` would read undefined
  against the new map; an absent key makes that a visible break rather than a
  quiet wrong answer.
- report.test.js's evilReport now carries `iiOrigins`. That test had gone
  vacuous: it pins CWE-150 terminal-injection sanitising on a field the
  renderer no longer reads, so it passed while asserting nothing. Confirmed the
  coverage is real again by mutation — deleting the clean() call on the
  iiOrigins path fails both report tests, restoring it passes.

Also drop the stale "/mcp → beta, /mcp-prod → production" examples from two
comments I wrote before rebasing onto #92. /mcp-prod no longer exists; the repo
now has no reference to it at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R8ZshwKmjD5fZ4Hs9dS6zh
Copilot AI review requested due to automatic review settings July 28, 2026 17:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@aterga
aterga merged commit 3eae059 into main Jul 28, 2026
2 checks passed
@aterga aterga mentioned this pull request Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants