Commit graph ext-apps/examples/lazy-auth-server
Author SHA1 Message Date
Olivier Chafik
2be172defb Use the canonical git+https form for repository.url
npm normalizes the bare https URL to git+https://….git at publish time
and warns about it on every package. Storing the canonical form removes
the warning and matches what npm's trusted-publisher check compares
against the repository.
2026-09-08 19:37:47 +01:00
Olivier Chafik
a0dcf16843 CI: build the WSL job on ext4, skip the unused Chromium download, list tsconfig types
The Windows WSL job is the critical path of every CI run (589 to 752 s).
Its checkout sits on the Windows drive, which WSL reaches over 9P, and
writing node_modules there is what makes npm ci take about three minutes
where the Linux job takes 23 s. The step now copies the tree onto the
distro's ext4 filesystem before installing and building, as Microsoft's
WSL guidance recommends for I/O-heavy work.

The e2e job downloaded the full Chromium build (about 165 MB) on every
run, but a headless run with no channel launches chromium-headless-shell
and never opens it. `--only-shell` skips that download.

Every tsconfig now lists the @types packages it uses (node, bun,
vite/client, dom-speech-recognition) instead of relying on TypeScript
auto-including everything under node_modules/@types. On TypeScript 5.9
this is a no-op: identical diagnostics across all 48 projects and
byte-identical emitted .d.ts files. It is a prerequisite for TypeScript 7,
which drops the auto-inclusion; with these lists the Go compiler passes
all 48 projects, so that upgrade becomes a version bump once TypeDoc
supports it.

Two cache-based approaches were evaluated and rejected: an npm cache for
the WSL job (npm ci is slow there because of 9P, not downloads, and a
prefix-restored cache grows without bound) and a Playwright browser cache
(Playwright's docs advise against it; the download is under 10 s).
2026-09-08 18:38:58 +01:00
Olivier Chafik
50eb30ec8d Bump to 2.0.0 and widen SDK peer ranges to ^2.0.0
The client, core and server packages become ^2.0.0 peers and client is
now a required peer (App and AppBridge extend its Protocol class);
server stays optional for View-only consumers. Examples move to
zod ^4.2.0 to match the library peer range.

The root package is not yet published at 2.0.0, so the lockfile now
links the examples' @modelcontextprotocol/ext-apps dependency to the
repository root instead of a registry copy. This drops the stale 1.x
tarball (and its sdk 1.x peer) from the lockfile and makes
scripts/link-self.mjs a no-op.
2026-09-08 13:58:22 +01:00
Olivier Chafik
1a713173cc
Migrate to MCP SDK 2.0 with official Protocol and isolated role peers (#720)
App and AppBridge extend the public Protocol from @modelcontextprotocol/client; the iframe wire protocol (ui/initialize only) is unchanged. Peers move to the split SDK 2.0 packages with client and server as optional role peers. Fixups (lockfile, peer ranges, restored handler guards, docs, wire-compat tests) follow in a separate PR.

Co-authored-by: Andrew Khadder <andrew@manufact.com>
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
Closes #702
2026-09-08 12:44:49 +01:00
Enrico
4e4886c5ff chore: update MCP SDK packages to stable 2.0.0 2026-07-28 15:02:48 -07:00
Olivier Chafik
92f46a5745
chore: bump ext-apps to 1.7.5 (#724) 2026-07-23 12:25:07 +01:00
Enrico
e7ffe7c738 chore: update MCP SDK packages to beta.5 2026-07-22 15:24:49 -07:00
Mel Ludowise
9778cf97a2
Make lazy-auth-server usable on mobile (#714)
The consent page shipped without a viewport meta tag, so mobile browsers
laid it out at a desktop width. Its Approve/Deny buttons were 37px tall
and nested a <button> inside an <a>, which is invalid HTML — interactive
content is not permitted inside a link.

- Add a viewport meta tag to the consent page
- Replace the <a><button> nesting with anchors styled as buttons
  (role="button" plus a Space-key handler to keep button keyboard
  semantics)
- Lay the actions out as a wrapping flex row with equal 56px tap targets
- Add touch-action: manipulation so a tap resolves to a click immediately
  rather than waiting on the double-tap-to-zoom gesture
- Suppress long-press text selection and the iOS callout menu on the
  buttons, scoped so the card's client/redirect details stay copyable
- Give buttons an :active style since removing the native tap highlight
  otherwise leaves no press feedback
- Apply the same 56px minimum and touch handling to the in-app view's
  buttons (the fullscreen toggle was ~33x33)
2026-07-16 17:00:17 +01:00
Andrew Khadder
f94cb38b0b
refactor: finish MCP SDK v2 migration 2026-07-14 17:07:30 -07:00
Andrew Khadder
b383847bf2
chore: prepare SDK v2 dependencies 2026-07-14 13:35:51 -07:00
Olivier Chafik
ca1d29894f
chore: bump ext-apps to 1.7.4 (#685)
* chore: bump ext-apps to 1.7.4

* fix: resolve all npm audit vulnerabilities

- Remove unused vitest devDependency from basic-host (critical advisory,
  package was never referenced by any test or config)
- systeminformation ^5.31.5 -> ^5.31.6 (command injection, GHSA-hvx9-hwr7-wjj9)
- Lockfile-only bumps: devalue 5.8.1, fast-uri 3.1.2, hono 4.12.23,
  ip-address 10.2.0, express-rate-limit 8.5.2, postcss 8.5.15, qs 6.15.2,
  svelte 5.56.1

npm audit now reports 0 vulnerabilities.
2026-06-04 22:10:06 +01:00
Olivier Chafik
0ffe16781e
lazy-auth-server: support mounting under a base path (#683)
* lazy-auth-server: support mounting under a base path

createApp() previously built every advertised URL (AS metadata
endpoints, PRM resource, WWW-Authenticate resource_metadata, consent
approve link, elicitation callbacks) from the bare origin, so mounting
the app inside another Express app — app.use("/lazy-auth",
createApp()) — advertised URLs that pointed at the host's root and
collided with the host's own OAuth endpoints.

Derive the public base URL from Express's req.baseUrl instead (empty
when standalone, so standalone behavior is unchanged), and treat
PUBLIC_URL as including any mount path. Document the mounting pattern,
including the root well-known rewrite hosts must add for RFC 8414/9728
path-insertion discovery.

* lazy-auth-server: derive DIST_DIR from import.meta.url

import.meta.filename and import.meta.dirname are undefined in some
module-VM contexts (e.g. importing this package from jest), which made
createApp() throw at import time. import.meta.url is defined
everywhere; derive the dist directory from it instead.
2026-06-04 19:47:57 +01:00
Mel Ludowise
a990780293
Require PKCE and bind redirect_uri in lazy-auth-server token exchange (#681)
* Require PKCE and bind redirect_uri in lazy-auth-server token exchange

Hardens the example's mock authorization server:

- /authorize now requires a PKCE S256 code_challenge (the MCP auth
  spec mandates PKCE for clients; previously a code issued without a
  challenge skipped verification at the token endpoint)
- /token rejects authorization-code exchanges where a provided
  redirect_uri does not match the authorization request (RFC 6749
  §4.1.3); OAuth 2.1 clients that omit it still work, relying on the
  now-mandatory PKCE binding

* Enforce single-use authorization codes

Track redeemed code IDs (jti) in memory until the code's own 5-minute
expiry; replaying a code at the token endpoint now fails with
invalid_grant (RFC 6749 §4.1.2). Also documents why wildcard CORS is
intentional for this demo (browser-based hosts must read
WWW-Authenticate; no ambient credentials exist to protect).
2026-06-02 13:30:49 +01:00
Olivier Chafik
7d4434e991
chore: bump ext-apps to 1.7.3 (#680)
No SDK API changes since 1.7.2.

Examples:
- Add lazy-auth-server example (#679)
- Publish lazy-auth-server to npm (added to publish workflows)

Also syncs example dependency ranges to ^1.7.0 (output of npm run bump).
2026-06-01 12:10:47 +01:00
Mel Ludowise
fcc4b03bde
Add lazy-auth-server example (#679)
Adds an example MCP App server demonstrating lazy (on-demand) OAuth:
the server connects and lists tools without authentication, and only
prompts for auth when a protected tool is called, by responding 401
with a WWW-Authenticate header pointing at protected-resource metadata.

- Public `show_auth_button` app calls the protected `get_secret` tool
  via callServerTool; the host runs the OAuth flow on 401 and retries
- Embedded mock authorization server (authorization-code + PKCE,
  short-lived HS256 tokens, refresh + session revocation) so the whole
  flow runs from a single process
- TTL-scoped endpoint paths (/ttl/<seconds>/mcp) let slow or automated
  clients request longer-lived tokens per connection, threaded through
  OAuth via RFC 8707 resource indicators
- `elicit_url` / `elicit_by_error` tools demonstrating URL elicitation
2026-06-01 11:19:53 +01:00