Skip to main content
Glama
felixtosh
by felixtosh

connect-claude — Foundation MCP + OAuth scaffold

One Next.js 15 app that does two things:

  1. MCP server at POST /mcp — JSON-RPC 2.0, Streamable-HTTP style, bearer-token protected. Exposes stubbed tools: list_sources, search_sources, summarize_source.

  2. OAuth 2.1 Authorization Server that Claude uses to authenticate users onto the MCP:

    • GET /.well-known/oauth-authorization-server (RFC 8414)

    • GET /.well-known/openid-configuration

    • GET /.well-known/oauth-protected-resource (RFC 9728, referenced from the WWW-Authenticate challenge on 401s from /mcp)

    • POST /oauth/register — Dynamic Client Registration (RFC 7591)

    • GET /oauth/authorize — the multi-step "connect your sources" wizard

    • POST /oauth/token — authorization-code (PKCE S256 required) + refresh grants

Important: only the MCP-side OAuth (Claude → this app) is real. All provider connectors (Google Drive, Slack, …) are faked — a 1.5s mock spinner flips in-memory state. No real Google/Slack OAuth happens.

Quick start

pnpm install
cp .env.example .env.local   # then set AUTH_SECRET
pnpm dev

App runs at http://localhost:3000.

Connect from Claude

Claude CLI:

claude mcp add --transport http foundation http://localhost:3000/mcp

Claude.ai / Claude Desktop (custom connector): add the URL https://<your-host>/mcp. Claude will:

  1. Probe /mcp, receive a 401 with a WWW-Authenticate header pointing at the protected-resource metadata.

  2. Discover the AS metadata, dynamically register a client at /oauth/register.

  3. Send the user's browser to /oauth/authorize?... → the wizard.

  4. The user "connects" providers (mocked), hits Next, sees the celebration screen, clicks Back to Claude → we redirect to Claude's redirect_uri with code + state.

  5. Claude exchanges the code at /oauth/token (PKCE verified) for a JWT bearer token and calls the MCP tools.

Env vars

See .env.example. AUTH_SECRET is required (JWT HS256 signing key in dev — use RS256 with a proper JWKS endpoint for production).

Storage

Everything is in-memory (codes, tokens, provider connections) behind a small adapter interface (lib/oauth/store.ts) so it can be swapped for Cloudflare D1/KV later. Client registrations are additionally persisted to data/clients.json so they survive dev-server restarts.

Repo map

app/mcp/route.ts                  MCP JSON-RPC endpoint
app/oauth/authorize/page.tsx      the wizard (UI mechanics from the prototype)
app/oauth/authorize/finalize/...  POST: issues the auth code, returns redirect URL
app/oauth/token/route.ts          token endpoint
app/oauth/register/route.ts       dynamic client registration
app/.well-known/...               AS / OIDC / protected-resource metadata
lib/oauth/*                       store, codes, tokens (jose), PKCE
lib/mcp/*                         protocol dispatch, tools, bearer auth
lib/providers/data.ts             static provider catalog
components/*                      wizard UI components
reference/                        hand-committed reference PDFs (do not touch)