Skip to main content
Glama
dyeoman2

Clerk MCP Server Template

by dyeoman2
index.ts4.1 kB
import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' import { DurableMCP } from 'workers-mcp' import { z } from 'zod' import { AuthHandler } from './auth' import { getToken } from './clerk' import { type Env, type UserProps } from './types' export class ClerkAppMCP extends DurableMCP<UserProps, Env> { server = new McpServer({ name: 'MCP Server with Clerk Auth', // TODO: Replace with your own name version: '1.0.0', // TODO: Replace with your own version }) private async buildAuthHeaders(): Promise<HeadersInit> { const token = await getToken( { sessionToken: this.props.sessionToken, sessionId: this.props.sessionId, updateToken: (newToken: string) => (this.props.sessionToken = newToken), }, (this as any).env.CLERK_SECRET_KEY, // Specify your JWT template name here (create one in Clerk Dashboard) 'default', ) return { Authorization: `Bearer ${token}`, Cookie: `__session=${token}`, Accept: 'application/json', } } private requireAuth<T extends any[], R>( handler: (...args: T) => Promise<R>, ): (...args: T) => Promise<R> { return async (...args: T) => { if (!this.props.user) { return { content: [{ type: 'text', text: 'Error: Not authenticated' }], } as R } return handler(...args) } } private async makeApiRequest<T = any>( path: string, options: { method?: string queryParams?: Record<string, string | number | boolean> body?: any } = {}, ): Promise<T> { const { method = 'GET', queryParams, body } = options const baseUrl = this.props.appUrl const endpoint = `${baseUrl}/${path.startsWith('/') ? path.slice(1) : path}` // Build query string const params = new URLSearchParams() if (queryParams) { for (const [key, value] of Object.entries(queryParams)) { if (value !== undefined && value !== null) { params.set(key, String(value)) } } } const url = params.toString() ? `${endpoint}?${params.toString()}` : endpoint const headers = await this.buildAuthHeaders() const response = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : undefined, }) if (!response.ok) { const errorText = await response.text().catch(() => '') throw new Error( `API request failed: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ''}`, ) } return response.json() } async init() { // Example tool 1: Get current user information this.server.tool( 'getUserInfo', 'Get current authenticated user information from Clerk', {}, this.requireAuth(async () => { return { content: [ { type: 'text', text: JSON.stringify(this.props.user, null, 2), }, ], } }), ) // Example tool 2: Echo/ping test this.server.tool( 'ping', 'Simple ping test that returns the current timestamp', { message: z .string() .optional() .describe('Optional message to include in response'), }, this.requireAuth(async (args) => { const response = { timestamp: new Date().toISOString(), message: args?.message || 'pong', user: this.props.user.firstName, } return { content: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], } }), ) // Example tool 3: Make authenticated API call to your service this.server.tool( 'getUsers', 'Fetch the current users from your application', {}, this.requireAuth(async () => { const users = await this.makeApiRequest('api/users') // TODO: Replace with your own endpoint return { content: [ { type: 'text', text: JSON.stringify(users, null, 2), }, ], } }), ) } } // OAuth provider configuration export default new OAuthProvider({ apiRoute: '/sse', apiHandler: ClerkAppMCP.mount('/sse') as any, defaultHandler: AuthHandler as any, authorizeEndpoint: '/authorize', tokenEndpoint: '/token', clientRegistrationEndpoint: '/register', })

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dyeoman2/clerk-mcp-template'

If you have feedback or need assistance with the MCP directory API, please join our Discord server