Add lazy-auth example server (mounted at /lazy-auth)#46
Open
ochafik wants to merge 3 commits into
Open
Conversation
Bumps @modelcontextprotocol/ext-apps and the example server packages to 1.7.4, which includes base-path mount support in server-lazy-auth.
Mounts @modelcontextprotocol/server-lazy-auth as an additional example, served at /lazy-auth/mcp alongside the other example MCP App servers. Unlike the other examples (plain createServer() factories behind the stateless handler), lazy-auth's value is its HTTP layer: public tools work unauthenticated, protected tools answer 401 + WWW-Authenticate, and a built-in mock OAuth authorization server completes the flow. So it mounts its full Express app (createApp()) under /lazy-auth. - New src/modules/example-apps/lazy-auth.ts mounts the app before the host middleware and adds the RFC 8414/9728 path-insertion well-known rewrite (/.well-known/oauth-authorization-server/lazy-auth -> into the mount) that SDK clients require for discovery under a base path. - PUBLIC_URL for the example is derived from this server's own BASE_URI, so no extra deploy-time configuration is needed; an explicit PUBLIC_URL still wins (e.g. a tunnel). - Integration test covers the full flow: public initialize/tools/list, 401 on the protected tool, mount-prefixed discovery metadata, and the PKCE -> token -> authed get_secret round trip.
82ab5e4 to
2476540
Compare
- Build the well-known rewrite regex from LAZY_AUTH_SLUG so it can't drift from the mount path if the slug changes. - Clarify in the docstring that an explicit PUBLIC_URL must include the /lazy-auth mount path (the package advertises it verbatim). - Tests: clear/restore ambient PUBLIC_URL around the integration server (was a latent flake); add unit coverage for the PUBLIC_URL derivation (trailing-slash strip, no-trailing-slash, explicit-wins, unset cases); add a public tool-call success case and an invalid-token 401 case.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Adds the
@modelcontextprotocol/server-lazy-authexample, served at/lazy-auth/mcpalongside the other example MCP App servers, and bumps the ext-apps example family to 1.7.4.Unlike the other examples (plain
createServer()factories behind the stateless handler), lazy-auth's value is its HTTP layer — public tools work unauthenticated; protected tools answer401+WWW-Authenticate; a built-in mock OAuth authorization server completes the flow. So it mounts its full Express app (createApp()) under/lazy-auth.Why now / what's blocked without it
The mobile clauditor e2e specs are blocked on reaching lazy-auth at
https://example-server.modelcontextprotocol.io/lazy-auth/ttl/3600/mcp(TTL-scoped path → 1-hour tokens, so 30s defaults don't expire mid-test on slow mobile runs).What changed
src/modules/example-apps/lazy-auth.ts— mounts the app before the host middleware, and adds the RFC 8414/9728 path-insertion well-known rewrite (/.well-known/oauth-authorization-server/lazy-auth→ into the mount). MCP SDK clients only try the insertion form foroauth-authorization-server, so this rewrite is required for discovery under a base path.src/index.ts/example-apps/index.ts— early mount (so the example's CORS/body handling stay self-contained), splash + console listings.PUBLIC_URLfor the example is derived from this server's ownBASE_URI(already the source of truth for the host's OAuth issuer). An explicitPUBLIC_URLstill wins (e.g. a tunnel). Tests on an ephemeral port fall back to the package's per-request Host resolution.server-lazy-auth). Lockfile resolves entirely via npmjs.initialize/tools/list,401on the protected tool, mount-prefixed discovery metadata, TTL-path PRM, and the PKCE → token → authedget_secretround trip.How we know it works
npm ci+npm run build+npm run lint+npm testall green against the published 1.7.4 (7 suites / 85 tests, lazy-auth integration included), verified in a linux container matching CI.@modelcontextprotocol/sdk@1.29.0client discovery machinery (extractResourceMetadataUrl→ PRM →buildDiscoveryUrls→auth()with PKCE) — full discovery + auth + silent refresh pass, on both/lazy-auth/mcpand/lazy-auth/ttl/3600/mcp.BASE_URI=https://example-server.modelcontextprotocol.io/, noPUBLIC_URL) advertises correct mount-prefixed OAuth URLs, trailing slash normalized.