
Clerk MCP Server Template
A production-ready template for building Model Context Protocol servers with Clerk authentication on Cloudflare Workers, allowing AI assistants to securely access user data and business logic in Clerk-authenticated applications.
README
Clerk MCP Server Template
A production-ready template for building Model Context Protocol (MCP) servers with Clerk authentication on Cloudflare Workers. This template provides everything you need to create secure, authenticated MCP tools that integrate with your existing Clerk-powered applications.
Features
- ✅ Clerk Authentication Integration - Complete OAuth 2.0 flow with Clerk
- ✅ Cloudflare Workers - Serverless edge computing with global distribution
- ✅ Durable Objects - Persistent MCP session state management
- ✅ KV Storage - Temporary OAuth session storage
- ✅ Security - HMAC-signed state parameters and automatic token refresh
- ✅ TypeScript - Full type safety throughout the codebase
- ✅ Example Tools - Ready-to-use example MCP tools
- ✅ Development Tools - ESLint, Prettier, and MCP Inspector integration
Why This Template?
This template bridges your existing Clerk-authenticated application with Claude AI through MCP tools. Perfect for:
- SaaS Applications: Give Claude access to your user data and business logic
- Customer Support: Let Claude query your systems with proper user context
- Data Analysis: Provide Claude with authenticated access to your APIs
- Workflow Automation: Create secure, user-specific automations
Quick Start
1. Prerequisites
- Node.js 22.x or later
- A Clerk account with API keys
- A Cloudflare account with Workers enabled
- An existing application using Clerk for authentication
2. Use This Template
git clone https://github.com/your-username/clerk-mcp-template.git my-mcp-server
cd my-mcp-server
npm install
3. Configure Environment Variables
Copy the example environment file:
cp .dev.vars.example .dev.vars
Update .dev.vars
with your Clerk keys and app URL:
CLERK_SECRET_KEY=sk_test_your_actual_clerk_secret_key
CLERK_PUBLISHABLE_KEY=pk_test_your_actual_clerk_publishable_key
APP_URL=https://your-app.com
Important:
APP_URL
should point to your existing Clerk-authenticated application where you'll implement the MCP auth flow.
4. Create KV Namespace
Create a KV namespace for OAuth session storage:
wrangler kv:namespace create "OAUTH_KV"
Update the id
in wrangler.jsonc
with the generated namespace ID.
5. Update Configuration
wrangler.jsonc
:
- Change
name
from"your-mcp-server"
to your desired worker name - Update the KV namespace ID with the one generated above
src/index.ts
:
- Update the server name and version in the
McpServer
constructor - Replace example tools with your own (see examples below)
6. Start Development
npm run dev
The server will be available at http://localhost:8788
Architecture
graph TB
A[MCP Client] --> B[Cloudflare Worker]
B --> C[OAuth Provider]
C --> D[Clerk Authentication]
B --> E[Durable Objects]
B --> F[KV Storage]
B --> G[Your API]
E --> H[MCP Session State]
F --> I[OAuth Sessions]
D --> J[User Authentication]
G --> K[Your Application Data]
Authentication Flow
- MCP Client connects to
/sse
endpoint - OAuth redirect to
/authorize
endpoint - User authentication via Clerk (you implement this part)
- Token exchange at
/callback
endpoint - Session creation in Durable Objects
- MCP tools become available with authenticated context
Integrating with Your Application
Step 1: Add MCP Authentication Route
Create an authentication route in your existing Clerk application at
/auth/mcp
. This route handles the OAuth flow initiated by the MCP server.
React Router v7 (Framework Mode) Example
This example shows integration with React Router v7 in framework mode (formerly Remix), but you can adapt it to Next.js, Express, or any framework.
app/routes/auth.mcp.tsx
:
import { createClerkClient } from '@clerk/express'
import { redirect, type LoaderFunctionArgs } from 'react-router'
export async function loader({ request }: LoaderFunctionArgs) {
const url = new URL(request.url)
const state = url.searchParams.get('state')
const callbackUrl = url.searchParams.get('callback_url')
const clientName = url.searchParams.get('client_name')
if (!state || !callbackUrl) {
throw new Error('Missing required parameters')
}
// Get the authenticated user's session token
const clerkClient = createClerkClient({
secretKey: process.env.CLERK_SECRET_KEY,
publishableKey: process.env.CLERK_PUBLISHABLE_KEY,
})
const clerkAuth = (await clerkClient.authenticateRequest(request)).toAuth()
const sessionToken = await clerkAuth?.getToken()
if (!sessionToken) {
// Redirect to sign-in if not authenticated
const signInUrl = new URL('/sign-in', request.url)
signInUrl.searchParams.set('redirect_url', request.url)
return redirect(signInUrl.toString())
}
// Redirect back to MCP server with token
const redirectUrl = new URL(callbackUrl)
redirectUrl.searchParams.set('clerk_token', sessionToken)
redirectUrl.searchParams.set('state', state)
return redirect(redirectUrl.toString())
}
// Optional: Add a component for showing consent screen
export default function McpAuth() {
return (
<div className="max-w-md mx-auto mt-8 p-6 bg-white rounded-lg shadow-md">
<h1 className="text-xl font-bold mb-4">Authorize MCP Access</h1>
<p className="text-gray-600 mb-4">
Claude AI is requesting access to your account data.
</p>
<p className="text-sm text-gray-500">
This will redirect you automatically...
</p>
</div>
)
}
Step 2: Create API Endpoints
Add protected API endpoints in your application that the MCP server can call with authenticated requests.
app/routes/api.users.tsx
:
import { createClerkClient } from '@clerk/express'
import { json, type LoaderFunctionArgs } from 'react-router'
export async function loader({ request }: LoaderFunctionArgs) {
try {
const clerkClient = createClerkClient({
secretKey: process.env.CLERK_SECRET_KEY,
publishableKey: process.env.CLERK_PUBLISHABLE_KEY,
})
// Verify the request is authenticated
const clerkAuth = await clerkClient.authenticateRequest(request)
const userId = clerkAuth.toAuth()?.userId
if (!userId) {
return json({ error: 'Unauthorized' }, { status: 401 })
}
// Your business logic here
const users = await getUsersForCurrentUser(userId)
return { users }
} catch (error) {
return json({ error: 'Internal server error' }, { status: 500 })
}
}
Step 3: Customize MCP Tools
Replace the example tools in src/index.ts
with your own:
// Custom tool example
this.server.tool(
'getUsers',
'Fetch all users from your application',
{},
this.requireAuth(async () => {
const users = await this.makeApiRequest('api/users')
return {
content: [
{
type: 'text',
text: `Found ${users.length} users:\n${JSON.stringify(users, null, 2)}`,
},
],
}
}),
)
Configuration Reference
Environment Variables
Variable | Description | Required |
---|---|---|
CLERK_SECRET_KEY |
Your Clerk secret key | ✅ |
CLERK_PUBLISHABLE_KEY |
Your Clerk publishable key | ✅ |
APP_URL |
Your application URL | ✅ |
Add your own application-specific environment variables to the Env
interface
in src/types.ts
.
Clerk JWT Templates
Create a JWT template in your Clerk Dashboard for token generation:
- Go to JWT Templates in your Clerk Dashboard
- Create a new template (e.g., "mcp-server")
- Update the template name in
src/index.ts
:
const token = await getToken(
// ... token manager
(this as any).env.CLERK_SECRET_KEY,
'your-template-name', // Update this
)
Development
Available Scripts
npm run dev # Start development server
npm run deploy # Deploy to Cloudflare Workers
npm run inspect # Launch MCP Inspector
npm run lint # Run ESLint + format
npm run typecheck # Run TypeScript type checking
npm run validate # Run typecheck + lint
Testing with MCP Inspector
- Start the development server:
npm run dev
- Open MCP Inspector
- Set transport type to SSE
- Connect to
http://localhost:8788/sse
- Complete the authentication flow
- Test your tools
Deployment
1. Set Production Secrets
wrangler secret put CLERK_SECRET_KEY
wrangler secret put CLERK_PUBLISHABLE_KEY
wrangler secret put APP_URL
2. Create Production KV Namespace
wrangler kv:namespace create "OAUTH_KV" --env production
Update the production KV namespace ID in wrangler.jsonc
.
3. Deploy and Configure
npm run deploy
Add your deployed server to Claude Desktop MCP configuration:
{
"mcpServers": {
"my-app": {
"command": "npx",
"args": [
"@modelcontextprotocol/server-remote",
"https://your-mcp-server.your-subdomain.workers.dev/sse"
]
}
}
}
Project Structure
clerk-mcp-template/
├── src/
│ ├── index.ts # Main MCP server class and tools
│ ├── auth.ts # OAuth authentication handlers
│ ├── clerk.ts # Clerk authentication utilities
│ ├── utils.ts # Utility functions (HMAC, logging, etc.)
│ └── types.ts # TypeScript type definitions
├── wrangler.jsonc # Cloudflare Worker configuration
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── eslint.config.js # ESLint configuration
├── .dev.vars # Development environment variables
└── README.md # This file
Security Considerations
- OAuth 2.0 ensures secure authentication flow
- HMAC signatures protect state parameters from tampering
- Automatic token refresh handles session expiration
- Session cleanup removes expired OAuth sessions
- Secure headers include proper CORS and authentication headers
Troubleshooting
Common Issues
Authentication fails:
- Verify Clerk API keys are correct
- Ensure your authentication route is implemented
- Check that JWT template exists in Clerk Dashboard
KV namespace errors:
- Verify namespace ID in
wrangler.jsonc
- Ensure namespace is created and bound
Tools not working:
- Check that user is authenticated
- Verify API endpoints are correct
- Review Cloudflare Workers logs
Debugging
# View real-time logs
wrangler tail
# Check deployment status
wrangler deployments list
# Test locally with debugging
npm run dev
Contributing
- Fork this repository
- Create a feature branch:
git checkout -b feature/amazing-feature
- Commit your changes:
git commit -m 'Add amazing feature'
- Push to the branch:
git push origin feature/amazing-feature
- Open a Pull Request
Resources
- Model Context Protocol Specification
- Clerk Authentication Documentation
- Cloudflare Workers Documentation
- Cloudflare MCP Examples
- React Router v7 Documentation
License
MIT License - see LICENSE file for details.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.