Skip to content

feat(fe): legal links from the app metadata on the MCP connect screen - #4302

Merged
aterga merged 4 commits into
mainfrom
feat/mcp-legal-links
Aug 28, 2026
Merged

feat(fe): legal links from the app metadata on the MCP connect screen#4302
aterga merged 4 commits into
mainfrom
feat/mcp-legal-links

Conversation

@sea-snake

@sea-snake sea-snake commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Motivation

The MCP connect screen ("Connect mcp.example.com") asks the user to hand an AI agent a standing delegation to their identity. What it says about the server it is connecting is the origin in its heading and nothing else: the server has no way to point the user at its privacy policy or its terms of service on the screen where the decision is actually made.

Apps already publish their own presentation permissionlessly, in the /.well-known/ii-app-metadata document introduced by #4221. This extends that document with the two legal links and renders them on the connect screen, which until now consumed no app metadata at all.

Changes

  • Two new optional fields, privacyPolicyUrl and termsOfServiceUrl, validated in $lib/utils/appMetadata.ts like the rest of the document: absent is fine, present-and-invalid rejects the whole document with a console warning naming the field, and a document carrying nothing but a policy URL is still usable.
  • They may point at any origin. Unlike the logo, II never fetches these documents — it renders a link the user opens themselves — so the same-origin rule that keeps the sign-in private from third parties does not apply, and requiring it would only lock out the many apps whose policies live on a separate domain. What is pinned instead is the scheme: https, which rules out javascript:, data: and anything else a link must never carry, along with plain http — II's production CSP (connect-src 'self' https:) means it would never have read the document over http to begin with, and the only screen rendering these links takes its origin from parseMcpServerUrl, which is https-only. An app served over http in local development publishes neither field.
  • The fine print above "Allow access" becomes the server's own legal links, replacing the "Revoke access anytime in your settings" note: Review <name>'s Privacy Policy and Terms of Service., in three translatable variants so a server publishing one link reads correctly. A server that publishes neither gets no fine print, and the spacing goes with it. Both links open in a new tab, so the connect request in this one isn't lost.
  • The name comes from the same document, falling back to the host when the server publishes none — it only ever says whose documents these are. The metadata is exactly as trustworthy as the origin serving it, which is why the verified host stays in the heading above, unchanged.
  • Spec: docs/ii-spec.mdx documents the fields, the scheme rule and its local-development exception, and extends the published JSON Schema. The schema preamble now names two requirements it cannot express (isolate balance, and the policy-URL scheme) rather than one.

Tests

  • Unit tests in appMetadata.test.ts for any-origin acceptance (with nothing fetched for it), relative resolution against the origin that served the document, a policy-URL-only document, and the scheme rejections: http both cross-origin and on the app's own origin, javascript:, data:, mailto:, unparseable, and empty.
  • E2E: the MCP fixture gains serveAppMetadata, and mcp.spec.ts covers the links rendering with the published hrefs and staying pinned above the CTA when the panel scrolls, plus a server publishing nothing showing no fine print. The "keeps the consent in view" spec no longer asserts on the revoke link this removes; that sticky-footer coverage moved to the new spec.

🤖 Generated with Claude Code

The `/.well-known/ii-app-metadata` document gains two optional fields,
`privacyPolicyUrl` and `termsOfServiceUrl`, and the MCP connect screen
links them above "Allow access" in place of its revoke note.

Unlike the logo, the documents are only ever linked to, never fetched, so
they may live on any origin, as a policy on a separate domain commonly
does. They must be `https` (or on the origin that served the document, so
an app served over plain `http` in local development still works), which
is what rules out `javascript:`, `data:` and the other schemes a link must
never carry. A field that is present and invalid rejects the whole
document, like every other field.

The connect screen consumed no app metadata before this, so it now also
resolves the server's name from the same document to say whose documents
these are, next to the host it has verified and displays above. Each link
appears only for the field the server publishes; a server publishing
neither gets no fine print.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sea-snake
sea-snake requested a review from a team as a code owner August 28, 2026 14:06
Copilot AI lite review requested due to automatic review settings August 28, 2026 14:06
@zeropath-ai

zeropath-ai Bot commented Aug 28, 2026

Copy link
Copy Markdown

No security or compliance issues detected. Reviewed everything up to 372f3fd.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► docs/ii-spec.mdx
    Extend app metadata with privacyPolicyUrl and termsOfServiceUrl fields and related validation rules
► src/frontend/src/lib/stores/app-metadata.store.ts
    Update metadata description to include privacyPolicyUrl and termsOfServiceUrl
Enhancement ► src/frontend/src/lib/utils/appMetadata.ts
    Add privacyPolicyUrl and termsOfServiceUrl fields to AppMetadata interface and validation logic for policy URLs (https-only, no userinfo, proper origin handling)
Enhancement ► src/frontend/src/lib/utils/appMetadata.ts
    Implement validatePolicyUrl to validate policy URLs per new rules and integrate into fetchAppMetadataFrom
Enhancement ► src/frontend/src/routes/(new-styling)/mcp/+page.svelte
    Pass mcpServerOrigin to MCP authorize view
Enhancement ► src/frontend/src/routes/(new-styling)/mcp/views/McpAuthorizeView.svelte
    Consume metadata from app metadata store; prepare and display Privacy Policy and Terms of Service links when provided
    Adjust UI to show legal document references and links conditionally
Enhancement ► src/frontend/tests/e2e-playwright/fixtures/mcp.ts
    Add serveAppMetadata fixture to publish /.well-known/ii-app-metadata for tests
Enhancement ► src/frontend/tests/e2e-playwright/routes/mcp.spec.ts
    Add test to verify MCP server published Privacy Policy and Terms of Service links and related UI behavior

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

Adds support for MCP servers to publish and display Privacy Policy / Terms of Service links on the MCP connect (“Allow access”) screen by extending the existing permissionless /.well-known/ii-app-metadata document and consuming it from the connect UI.

Changes:

  • Extend app metadata validation to support optional privacyPolicyUrl and termsOfServiceUrl with scheme restrictions and relative resolution.
  • Render server-provided legal links (with translatable variants) in a sticky fine-print area on the MCP connect screen.
  • Add/adjust unit + Playwright E2E coverage and update the public spec/schema docs.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/frontend/tests/e2e-playwright/routes/mcp.spec.ts Adds E2E coverage for rendering legal links (and removes now-obsolete “revoke in settings” assertions).
src/frontend/tests/e2e-playwright/fixtures/mcp.ts Adds fixture helper to serve /.well-known/ii-app-metadata for MCP-origin in tests.
src/frontend/src/routes/(new-styling)/mcp/views/McpAuthorizeView.svelte Fetches app metadata for the MCP server origin and conditionally renders legal links above the CTA.
src/frontend/src/routes/(new-styling)/mcp/+page.svelte Wires mcpServerOrigin into McpAuthorizeView.
src/frontend/src/lib/utils/appMetadata.ts Adds metadata fields + validation logic for policy URLs.
src/frontend/src/lib/utils/appMetadata.test.ts Adds unit tests for policy URL validation/resolution and scheme constraints.
src/frontend/src/lib/stores/app-metadata.store.ts Updates store documentation to reflect the new metadata fields/consumer.
docs/ii-spec.mdx Documents new fields, scheme rules (incl. local-dev exception), and updates the JSON Schema.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/frontend/src/lib/utils/appMetadata.ts Outdated
sea-snake and others added 2 commits August 28, 2026 16:15
The plain-`http` carve-out for the document's own origin served nobody.
The only screen rendering these links takes its origin from
`parseMcpServerUrl`, which is https-only, and II's production CSP
(`connect-src 'self' https:`) means an `http` origin's metadata document is
never read there at all. A local app publishes neither field.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both from the Copilot review. A value with nothing but whitespace in it
resolved to the origin's root, since the URL parser ignores what surrounds
a URL; it is now refused, matching the text fields' "must read as
something" rule.

Userinfo grants an app no destination it couldn't write plainly, as any
https origin is allowed by design, but it is the one way a value could read
as one host and resolve to another, and the link text the user sees is II's
own generic "Privacy Policy".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

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

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread src/frontend/tests/e2e-playwright/fixtures/mcp.ts
The connect screen asks the server's origin for its app-metadata document
as soon as it mounts, so the 12 specs that reach `/mcp` without installing
the server stand-in were left making a real request to mcp.id.ai. The
fixture now answers that path 404 by default, the way an origin publishing
nothing does; `serveAppMetadata` and the connect interceptor register later
and take precedence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread docs/ii-spec.mdx

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

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

@aterga
aterga merged commit 583ad16 into main Aug 28, 2026
46 checks passed
@aterga
aterga deleted the feat/mcp-legal-links branch August 28, 2026 19:09
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.

4 participants