Skip to content

feat(fe): add /mcp delegation flow for MCP server sign-in#4026

Merged
aterga merged 5 commits into
mainfrom
feat/mcp-authorize
Jun 17, 2026
Merged

feat(fe): add /mcp delegation flow for MCP server sign-in#4026
aterga merged 5 commits into
mainfrom
feat/mcp-authorize

Conversation

@sea-snake

Copy link
Copy Markdown
Contributor

Adds an MCP sign-in flow analogous to the recently shipped CLI flow: a single, deploy-configured (operator-trusted) MCP server can obtain an Internet Identity delegation that acts as the user's default account at a given application. This lets AI assistants connecting through the ICP MCP server act on the user's behalf in apps, gated by an explicit per-device opt-in and a per-request consent screen. UI follows the Claude Design handoff (authorize-mcp screens + MCP settings card).

Changes

  • Deploy arg + CSP: new mcp_server_origin on InternetIdentityFrontendArgs / internet_identity_frontend.did, exposed to the frontend via globals.ts, and appended to the form-action CSP for that single origin (never broadened to https:). The shared deploy-arg helper (frontend-arg-helpers.bash) and the local test arg template support it.
  • /mcp route: GET-in / top-level-form-POST-out redirect flow mirroring /cli. Request params live in the URL fragment; the callback must match the configured MCP origin (belt-and-suspenders with the CSP). Screens: McpHero (app · lock · MCP server), the combined "Allow MCP access" authorize screen, success, invalid, error, and the "MCP access not enabled" device gate. The delegation is built via get_default_accountprepare/get_account_delegation, so MCP acts as the same principal /authorize gives for that app.
  • Settings: new MCP access card + confirm dialog with a risk acknowledgement. The existing CLI card and dialog are untouched.
  • Analytics: mcpAuthorizeFunnel.
  • App-mode only (no generic path); no client name (MCP dynamic client registration), framed around the MCP server hostname only.

Tests

  • New CSP unit test asserting the configured MCP origin appears in form-action and that it is never broadened to https:.
  • New Playwright e2e fixture + spec (fixtures/mcp.ts, routes/mcp.spec.ts): invalid params, off-origin callback rejected, device-gate behavior, successful two-hop delegation, TTL honoured, identity-switcher visibility, and principal-parity with /authorize.
  • npm run check, eslint, prettier, cargo clippy, the candid-compat test, and the frontend bin tests all pass locally; the e2e suite runs in CI.

🤖 Generated with Claude Code

Adds a /mcp page that lets a deploy-configured MCP server obtain an
Internet Identity delegation acting as the user's default account at a
given application, plus an MCP access device-gate in Settings.

- New mcp_server_origin frontend deploy arg, appended to the form-action
  CSP and exposed to the frontend; /mcp delivers the delegation to that
  origin only (rejects any other callback origin).
- /mcp route: GET-in/redirect-back flow mirroring /cli — McpHero, the
  combined 'Allow MCP access' authorize screen, success/invalid/error and
  the MCP-access-not-enabled gate; default account resolved via
  get_default_account so MCP acts as the same principal /authorize gives.
- Settings: new MCP access card + confirm dialog (CLI card untouched).
- mcpAuthorizeFunnel analytics; e2e fixture + spec; CSP unit test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sea-snake sea-snake requested a review from a team as a code owner June 17, 2026 12:54
Copilot AI review requested due to automatic review settings June 17, 2026 12:54
@zeropath-ai

zeropath-ai Bot commented Jun 17, 2026

Copy link
Copy Markdown

No security or compliance issues detected. Reviewed everything up to 8e6c0fa.

Security Overview
Detected Code Changes

| Change Type | Relevant files

... (code changes summary truncated to fit VCS comment limits.)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 adds an operator-configured /mcp delegation flow to Internet Identity, allowing a single trusted MCP server to obtain an app-scoped Internet Identity delegation on the user’s behalf, gated by a per-device opt-in and a consent screen. It extends the frontend deploy args and CSP to safely allow a single remote form-action destination for the MCP server callback, and adds e2e coverage analogous to the existing /cli flow.

Changes:

  • Add mcp_server_origin deploy arg (Candid + Rust types) and thread it into CSP generation to append exactly one allowed form-action origin.
  • Implement the /mcp route UI + logic (fragment-parsed request, consent screen, device gate, success/error/invalid terminal screens) and identity-switcher behavior.
  • Add MCP device opt-in toggle in Settings + analytics funnel + Playwright e2e fixture/spec for the flow.

Reviewed changes

Copilot reviewed 28 out of 30 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/internet_identity_interface/src/internet_identity/types.rs Adds mcp_server_origin to shared frontend args.
src/internet_identity_frontend/tests/integration/http.rs Updates default args helper to include mcp_server_origin.
src/internet_identity_frontend/src/main.rs Threads mcp_server_origin into CSP generation and adds CSP unit test.
src/internet_identity_frontend/local_test_arg.did.template Enables MCP origin in local test template.
src/internet_identity_frontend/internet_identity_frontend.did Adds mcp_server_origin to the init interface.
src/frontend/tests/e2e-playwright/routes/mcp.spec.ts New e2e spec covering invalid/off-origin/gate/success/TTL/principal parity.
src/frontend/tests/e2e-playwright/fixtures/mcp.ts New fixture to intercept MCP form POST navigation + redirect-back.
src/frontend/tests/e2e-playwright/fixtures/index.ts Registers MCP fixture into merged test fixture.
src/frontend/src/routes/(new-styling)/mcp/views/McpInvalidView.svelte Invalid-request terminal screen.
src/frontend/src/routes/(new-styling)/mcp/views/McpErrorView.svelte Error terminal screen for MCP-reported failure.
src/frontend/src/routes/(new-styling)/mcp/views/McpDisabledView.svelte Device-gated terminal screen when MCP access is disabled.
src/frontend/src/routes/(new-styling)/mcp/views/McpCloseWindowView.svelte Success terminal screen instructing user to close the tab.
src/frontend/src/routes/(new-styling)/mcp/views/McpAuthorizeView.svelte Consent screen (“Allow MCP access”) and submit UI.
src/frontend/src/routes/(new-styling)/mcp/utils.ts Builds and delivers a two-hop delegation chain via top-level form POST.
src/frontend/src/routes/(new-styling)/mcp/mcp-switcher.store.ts Store controlling identity switcher visibility for /mcp layout.
src/frontend/src/routes/(new-styling)/mcp/components/McpHero.svelte Header hero illustrating app ↔ MCP server relationship.
src/frontend/src/routes/(new-styling)/mcp/+page.ts Fragment parsing/validation for MCP request params + status.
src/frontend/src/routes/(new-styling)/mcp/+page.svelte Main /mcp flow state machine, validation, analytics, and navigation.
src/frontend/src/routes/(new-styling)/mcp/+layout.svelte /mcp-scoped layout with identity switcher + dialogs.
src/frontend/src/routes/(new-styling)/manage/(authenticated)/settings/components/McpConfirmDialog.svelte Confirmation dialog with risk acknowledgement checkbox.
src/frontend/src/routes/(new-styling)/manage/(authenticated)/settings/components/McpAccessSection.svelte Settings card + toggle wiring for device-local MCP access.
src/frontend/src/routes/(new-styling)/manage/(authenticated)/settings/+page.svelte Adds MCP access card to Settings page.
src/frontend/src/lib/utils/analytics/mcpAuthorizeFunnel.ts Adds analytics funnel for /mcp flow.
src/frontend/src/lib/stores/mcp-access.store.ts LocalStorage-backed per-identity MCP access enablement store.
src/frontend/src/lib/globals.ts Exposes getMcpServerOrigin() from canister config.
src/frontend/src/lib/generated/internet_identity_frontend_types.d.ts Regenerated types including mcp_server_origin.
src/frontend/src/lib/generated/internet_identity_frontend_idl.js Regenerated IDL including mcp_server_origin.
src/frontend/src/lib/constants/store.constants.ts Adds LocalStorage key for MCP access.
src/frontend/src/lib/components/ui/McpLogo.svelte Adds MCP logo UI component.
scripts/frontend-arg-helpers.bash Adds mcp_server_origin to interactive deploy-arg helper.

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

Comment thread src/internet_identity_frontend/src/main.rs
Comment thread src/frontend/src/routes/(new-styling)/mcp/+page.svelte Outdated
sea-snake and others added 2 commits June 17, 2026 13:19
- Move the MCP logo from lib/components/ui/McpLogo.svelte to
  lib/components/icons/McpIcon.svelte, alongside PasskeyIcon/SsoIcon, and
  match their SVGAttributes/size-5 convention.
- main.rs: sanitize mcp_server_origin to a strict scheme://host[:port]
  origin before splicing into the form-action CSP; drop (fail closed) any
  value carrying whitespace/';'/','/path/query/fragment/userinfo or a
  disallowed scheme, so a misconfigured deploy arg can't break or inject
  the policy. Extend the CSP test with malformed cases. (Copilot)
- /mcp +page.svelte: parse the configured origin defensively (try/catch);
  a malformed value is treated as unset so the route shows the invalid
  screen instead of throwing during render. (Copilot)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
build_fe_install_arg omitted mcp_server_origin, so staging deploys via
deploy-pr-to-beta always sent null and never asked for it — leaving /mcp
disabled. Add the prompt (default parsed from the live /.config) and emit
the field in the FE install arg.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread src/internet_identity_frontend/local_test_arg.did.template Outdated
sea-snake added a commit to aterga/imcp2 that referenced this pull request Jun 17, 2026
…ity + sign_in + list_identities)

New auth model (PR dfinity/internet-identity#4026): the MCP server holds a
per-OpenID-session Ed25519 key; the user signs into a domain via II's /mcp flow,
which delegates to that key as their default account for the app. The server
then signs calls as that identity with ic-agent's DelegatedIdentity.

- identities.rs: per-session key + per-domain delegation store; sign-in state
  machine (link -> GET /signin -> II redirect -> form-POST callback); parses
  DelegationChain JSON into ic-agent SignedDelegations; builds DelegatedIdentity.
- call_canister gains an  arg (anonymous | <domain>); list_identities;
  sign_in(domain) returns the short URL.
- Session binding: OpenID approve mints a session_id + sets an httpOnly
  mcp_session cookie carried to the token; GET /signin requires that cookie to
  match the link's session; a SameSite=None flow cookie is required on the
  cross-site callback POST; state is single-use and routes to the session.
- II staging target uhh2r-oyaaa-aaaad-agbva-cai.icp0.io (II_MCP_URL overridable).

Builds clean; 7 existing tests pass. propose_call/check_proposal/the /app flow
still present — trimmed in a follow-up. Real round-trip needs the staging II
deployed with mcp_server_origin = our origin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
aterga added 2 commits June 18, 2026 01:11
The e2e job installs the frontend canister with inline --args that omitted mcp_server_origin, so getMcpServerOrigin() was undefined and the /mcp flow always rendered the invalid screen, timing out all interactive mcp.spec.ts tests. Add mcp_server_origin = opt "https://mcp.id.ai" to match the origin the mcp fixture posts to.
Apply reviewer suggestion from aterga.
@aterga aterga enabled auto-merge (squash) June 17, 2026 23:18
@aterga aterga merged commit 208d1d7 into main Jun 17, 2026
46 checks passed
@aterga aterga deleted the feat/mcp-authorize branch June 17, 2026 23:26
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