Connect Claude to your app over MCP
Start state: nothing. End state: Claude (the chat app) reading and writing your app's data as you, via OAuth — and Claude Code doing the same via an API key. The mcp feature makes every scaffolded app its own MCP server.
1. Scaffold with mcp
pnpm new assistant --title "Assistant" --type webapp --features example,mcp
pnpm install
cd apps/assistant && npx convex dev --once && pnpm exec tsc --noEmit
Expected during scaffold: "(mcp requires api-keys — added automatically)" — MCP dev-tool auth uses API keys, so the dependency is pulled in for you.
2. Deploy
cd ../.. && pnpm deploy assistant
Chat apps need a public endpoint, so deploy first. Expected: a live frontend URL plus a Convex backend URL like https://brave-otter-123.convex.cloud.
3. Find the endpoint
The MCP endpoint is your Convex site URL + /mcp — e.g. https://brave-otter-123.convex.site/mcp (note .convex.site, not .convex.cloud). You never have to guess: sign in to the deployed app and open the MCP page in the nav — it shows the exact endpoint, a copyable client config, the live tool list, and your connected apps. It reads the same metadata the server serves, so it can never drift.
4. Connect a chat app (OAuth)
- 1. In Claude (or ChatGPT, Gemini, Grok), add a custom connector and paste the endpoint URL
- 2. The chat app registers itself automatically (OAuth 2.1 dynamic client registration + PKCE — no client id to copy)
- 3. Your browser opens your app's consent screen: sign in if needed, review the access, click Approve
- 4. Back in the chat, the connector shows as connected with your app's tools listed
5. Alternative: API key for Claude Code
Dev tools authenticate with Authorization: Bearer <api key> instead of OAuth. Create a key on the app's API Keys page (the full key exists exactly once — in your clipboard; only its SHA-256 hash reaches the server), then either drop a .mcp.json in your project:
{
"mcpServers": {
"assistant": {
"type": "http",
"url": "https://brave-otter-123.convex.site/mcp",
"headers": { "Authorization": "Bearer <your api key>" }
}
}
}
or use the one-liner:
claude mcp add --transport http assistant https://brave-otter-123.convex.site/mcp
# then set the Authorization header when prompted / in the generated config
6. Call a tool from chat
Ask Claude something that maps to a tool — e.g. "list my items" or "create an item called Follow up with Dana". Claude calls the tool over MCP, the server resolves your identity from the token, and the result comes back in chat. Open the app in a browser tab at the same time: Convex is reactive, so the new item appears in the UI the moment the tool call lands.
7. Revoke access
On the app's MCP page, Connected apps lists every approved connection with its active token count and a Disconnect button — clicking it revokes the tokens; the chat app must re-run the consent flow to reconnect. API keys are revoked from the API Keys page.
Adding your own tools
Add tools for your own tables in convex/mcp.ts; their metadata lives in convex/mcpToolList.ts, and the MCP page's tool list updates itself. (On Supabase apps the server is the supabase/functions/mcp Edge Function — deploy it with --no-verify-jwt and set the APP_URL secret; tools live in src/lib/mcpTools.ts.)
What you built
- A deployed app that is also an MCP server, with OAuth 2.1 (DCR + PKCE + consent) for chat apps and API keys for dev tools
- A working Claude connection in both directions: chat connector and Claude Code config
- A revocation story: Connected apps → Disconnect