connect-claude
by felixtosh
README.md
# 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
```bash
pnpm install
cp .env.example .env.local # then set AUTH_SECRET
pnpm dev
```
App runs at `http://localhost:3000`.
## Connect from Claude
**Claude CLI:**
```bash
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)
```
## Spec links
- MCP spec: https://modelcontextprotocol.io/specification
- MCP authorization: https://modelcontextprotocol.io/specification/draft/basic/authorization
- OAuth 2.1: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1
- RFC 8414 (AS metadata): https://www.rfc-editor.org/rfc/rfc8414
- RFC 7591 (DCR): https://www.rfc-editor.org/rfc/rfc7591
- RFC 9728 (protected resource metadata): https://www.rfc-editor.org/rfc/rfc9728
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues