A library built on top of the MCP TypeScript SDK that makes it easier to implement MCP with auth into your MCP client and/or server.
It's a protocol that enables AI applications like Claude, ChatGPT, Cursor, etc to ask you for permission to access some of your private information that normally you'd need to sign in with your account to access. For example, your emails, or your private github repositories, etc.
This allows you to provide AI applications with context and abilities that it would normally not have access to. You could, for example, have an AI application use some code in a private repo as context to answer your questions, or have it write and send an email on your behalf, after you review it. It's kind of like this:
We think this is valuable because it enables people to use AI to access a bunch of extra information that it wasn't able to access before, and does so in a safe way where you as the user are in control over what it has access to and what it can do. We're excited to see what new AI use cases become possible as MCP adoption grows, and we built this library to try to help make it easier for people to integrate MCP into their applications.
There are two parties involved in MCP:
- The client, which is the one that wants to get access to another service. In the above example would be Claude, which wants to get access to Gmail.
- The server, which is the one that has something that a client wants access to. In the above example, this would be Gmail. This is sometimes referred to as the "resource server" or "MCP server".
This library has tools for both of these parties, so step one is to be clear on whether you are building a client or server. We'll address each of these use cases separately.
NOTE: In web development, the terms "client" and "server" are often used to refer to the frontend (browser) and backend (web server). This is not the case in this situation, so try not to confuse them!
For detailed implementation guides and examples specific to your framework, see:
- Express.js Integration - Complete guide for building MCP servers with Express.js
- Next.js Integration - Complete guide for building both MCP servers and clients with Next.js
If you are building a server that you'd like to introduce MCP support for, you will want to use the @clerk/mcp-tools/server
import path.
In order for the most up to date authentication flow in the MCP spec to work correctly, your server will need to expose a static metadata file called "protected resource metadata", which is defined by RFC 9728.
This library exposes a tool that can quickly generate such a metadata file for you. Here's an example of how to use it:
import { generateProtectedResourceMetadata } from "@clerk/mcp-tools/server";
const result = generateProtectedResourceMetadata({
resourceUrl: "https://myapp.com/current-route",
authServerUrl: "https://auth.example.com",
});
You will want to set up a route at .well-known/oauth-protected-resource
and make sure to return this result from that route on your server.
If you are using Clerk for authentication in your app, we have a helper that makes this easier:
import { generateClerkProtectedResourceMetadata } from "@clerk/mcp-tools/server";
const result = generateClerkProtectedResourceMetadata({
publishableKey: process.env.CLERK_PUBLISHABLE_KEY,
resourceUrl: "https://myapp.com/current-route",
});
For framework-specific implementations of protected resource metadata handlers, see:
NOTE: This is not yet fully implemented
There is an older version of the MCP spec that specified that the MCP server should be responsible for authentication on its own and instead it should implement a different static metadata file called "authorization server metadata", defined by RFC 8414. While it should not be necessary as long as you have implemented protected resource metadata and are using an authorization service that has properly implemented a authorization server metadata route, there are some scenarios where this might be necessary if you are building your own authorization server, if your authorization server is part of your app directly, or if you are interfacing with a client that has an outdated implementation. This library also provides utilities for this use case.
// return this result from <your-app>/.well-known/oauth-authorization-server
import { generateAuthorizationServerMetadata } from "@clerk/mcp-tools/server";
const result = generateAuthorizationServerMetadata({
authServerUrl: "https://auth.example.com",
scopes: ["email", "profile", "openid"],
});
In the most standard case, the above example will work, but it does make some assumptions about the authorization server, namely that:
- The authorization endpoint is:
<authServerUrl>/authorize
- The registration endpoint is:
<authServerUrl>/register
- The token endpoint is:
<authServerUrl>/token
- The userInfo endpoint is:
<authServerUrl>/userinfo
- The jwks endpoint is:
<authServerUrl>/.well-known/jwks.json
If this isn't the case, you can pass in overrides for any of these values. Passing in false will omit the value, which can be useful in some cases, like if your authorization server does not support dynamic client registration:
// return this result from <your-app>/.well-known/oauth-authorization-server
import { generateAuthorizationServerMetadata } from "@clerk/mcp-tools/server";
const result = generateAuthorizationServerMetadata({
authServerUrl: "https://auth.example.com",
authorizationEndpoint: "foo/bar/authorize",
registrationEndpoint: false,
tokenEndpoint: "tokens",
scopes: ["email", "profile", "openid", "foobar"],
});
If you are using Clerk for authentication in your app, you can use the following helper to fetch Clerk's metadata from your Clerk frontend API and return it.
import { generateClerkAuthorizationServerMetadata } from "@clerk/mcp-tools/server";
const result = generateClerkAuthorizationServerMetadata();
For framework-specific implementations, see:
To create an MCP endpoint that handles the actual MCP protocol communication, you'll need to use framework-specific adapters, since each framework has its own way of handling request and response objects, which are critical parts of implementing the MCP protocol:
- Express.js: Use the
streamableHttpHandler
from@clerk/mcp-tools/express
- see Express.js guide - Next.js: Use Vercel's MCP adapter with Next.js route handlers - see Next.js guide
These adapters handle the MCP protocol details and integrate with your authentication system.
The first step to building MCP compatibility into your AI application is allowing your users to connect with an MCP service. This can be kicked off simply with the URL of an MCP-compatible server, like https://example.com/mcp
. Normally, your app would implement a text field where the user can enter an MCP endpoint, or have a pre-built integration where clicking a button would trigger an MCP connection flow with a baked-in endpoint URL.
The process of actually making the MCP connection using the SDK, however, is fairly arduous, so we expose some tools that can help make this easier.
Here's how you can create an MCP client using the core utilities:
import { createDynamicallyRegisteredMcpClient } from "@clerk/mcp-tools/client";
import { createRedisStore } from "@clerk/mcp-tools/stores/redis";
// Create a persistent store (use appropriate store for your environment)
const store = createRedisStore({ url: process.env.REDIS_URL });
export async function initializeMCPConnection(mcpEndpoint: string) {
const { connect, sessionId } = createDynamicallyRegisteredMcpClient({
mcpEndpoint,
oauthScopes: "openid profile email",
oauthRedirectUrl: "https://yourapp.com/oauth_callback",
oauthClientUri: "https://yourapp.com",
mcpClientName: "My App MCP Client",
mcpClientVersion: "0.0.1",
redirect: (url: string) => {
// Implement redirect logic for your framework
window.location.href = url;
},
store,
});
// Connect to the MCP endpoint
await connect();
return { sessionId };
}
After the user completes the OAuth flow, you'll need to handle the callback:
import { completeAuthWithCode } from "@clerk/mcp-tools/client";
export async function handleOAuthCallback(code: string, state: string) {
const result = await completeAuthWithCode({
state,
code,
store,
});
// OAuth flow is now complete
return result;
}
Once authentication is complete, you can call MCP tools:
import { getClientBySessionId } from "@clerk/mcp-tools/client";
export async function callMCPTool(
sessionId: string,
toolName: string,
args: any
) {
const { client, connect } = getClientBySessionId({
sessionId,
store,
});
await connect();
const toolResponse = await client.callTool({
name: toolName,
arguments: args,
});
return toolResponse;
}
For complete framework-specific implementations with working examples, see:
In order to implement MCP functionality in a client, persistent storage is required. This is because:
- The MCP flow operates across a minimum of three distinct server endpoints (initialization of MCP client, OAuth callback, MCP request), and these server endpoints could be deployed to distinct serverless/edge functions without a shared memory pool.
- Since the MCP connection is intended to be long-running, it must maintain a "session". Relying on in-memory storage for long-running sessions is generally a very bad idea ™️, since it would bloat memory requirements indefinitely as the app scales, and any sort of clearing of memory like a server restart would immediately invalidate all sessions.
As such, each of the client functions require that you pass in a store adapter. There are several built-in store adapters available:
import fsStore from "@clerk/mcp-tools/stores/fs";
This uses a temporary file and is fast, easy, and adequate for local development and testing. However, it's not suitable for production since the file could be deleted at any time.
For production environments, use one of these persistent stores:
import { createRedisStore } from "@clerk/mcp-tools/stores/redis";
const store = createRedisStore({
url: process.env.REDIS_URL,
});
import { createPostgresStore } from "@clerk/mcp-tools/stores/postgres";
const store = createPostgresStore({
connectionString: process.env.DATABASE_URL,
});
import { createSqliteStore } from "@clerk/mcp-tools/stores/sqlite";
const store = createSqliteStore({
filename: "./mcp-sessions.db",
});
If you wish to use a different kind of store, you can implement your own by complying with this simple interface:
type JsonSerializable =
| null
| boolean
| number
| string
| JsonSerializable[]
| { [key: string]: JsonSerializable };
interface McpClientStore {
write: (key: string, data: JsonSerializable) => Promise<void>;
read: (key: string) => Promise<JsonSerializable>;
}
The built-in stores have a few extra methods that may be useful, but only read
and write
are required for this to work.
The above examples are more of a guide for how to implement the tools, but for those digging deeper, this section covers each tool that is exposed out of this package, what it can take in as arguments, and what it will return.
-
createKnownCredentialsMcpClient
-
Description: If dynamic client registration is not desirable, and your interface can collect a client id and secret from an existing OAuth client, you can create a MCP client with this function. Though it does increase friction in the user experience, we recommend allowing MCP services that do not enable dynamic client registration, since it comes with several security/fraud risks that not every provider wants to take on.
-
Arguments:
interface CreateKnownCredentialsMcpClientParams { /** * OAuth client id, expected to be collected via user input */ clientId: string; /** * OAuth client secret, expected to be collected via user input */ clientSecret: string; /** * OAuth redirect URL - after the user consents, this route will get * back the authorization code and state. */ oauthRedirectUrl: string; /** * OAuth scopes that you'd like to request access to */ oauthScopes?: string; /** * The endpoint of the MCP service, expected to be collected via user input */ mcpEndpoint: string; /** * Name passed to the client created by the MCP SDK * @see https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#writing-mcp-clients */ mcpClientName: string; /** * Version number passed to the client created by the MCP SDK * @see https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#writing-mcp-clients */ mcpClientVersion: string; /** * A function that, when called with a url, will redirect to the given url */ redirect: (url: string) => void; /** * A persistent store for auth data * @see https://github.com/clerk/mcp-tools?tab=readme-ov-file#stores */ store: McpClientStore; }
-
Return Type:
interface McpClientReturnType { /** * Represents a session associated with the connected MCP service endpoint. */ sessionId: string; /** * Calling this function will initialize a connect to the MCP service. */ connect: () => void; /** * Lower level primitive, likely not necessary for use * @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/streamableHttp.ts#L119 */ transport: StreamableHTTPClientTransport; /** * Lower level primitive, likely not necessary for use * @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/index.ts#L81 */ client: Client; /** * Lower level primitive, likely not necessary for use * @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/auth.ts#L13 */ authProvider: OAuthClientProvider; }
-
-
createDynamicallyRegisteredMcpClient
-
Description: Creates a new MCP client given only an MCP endpoint url. Registers an OAuth client with the authorization server on-demand via OAuth 2.0 Dynamic Client Registration Protocol.
-
Arguments:
interface CreateDynamicallyRegisteredMcpClientParams { /** * The endpoint of the MCP service, expected to be collected via user input */ mcpEndpoint: string; /** * OAuth redirect URL - after the user consents, this route will get * back the authorization code and state. */ oauthRedirectUrl: string; /** * The name of the OAuth client to be created with the authorization server */ oauthClientName?: string; /** * The URI of the OAuth client to be created with the authorization server */ oauthClientUri?: string; /** * OAuth scopes that you'd like to request access to */ oauthScopes?: string; /** * Whether the OAuth client is public or confidential * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.1 */ oauthPublicClient?: boolean; /** * Name passed to the client created by the MCP SDK * @see https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#writing-mcp-clients */ mcpClientName: string; /** * Version number passed to the client created by the MCP SDK * @see https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#writing-mcp-clients */ mcpClientVersion: string; /** * A function that, when called with a url, will redirect to the given url */ redirect: (url: string) => void; /** * A persistent store for auth data * @see https://github.com/clerk/mcp-tools?tab=readme-ov-file#stores */ store: McpClientStore; }
-
Return Type:
interface McpClientReturnType { /** * Represents a session associated with the connected MCP service endpoint. */ sessionId: string; /** * Calling this function will initialize a connect to the MCP service. */ connect: () => void; /** * Lower level primitive, likely not necessary for use * @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/streamableHttp.ts#L119 */ transport: StreamableHTTPClientTransport; /** * Lower level primitive, likely not necessary for use * @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/index.ts#L81 */ client: Client; /** * Lower level primitive, likely not necessary for use * @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/auth.ts#L13 */ authProvider: OAuthClientProvider; }
-
-
getClientBySessionId
- Description: Given an existing session id, constructs a MCP client that matches the information used to create the client/session initially. Intended to be used in OAuth callback routes and any subsequent MCP calls once the service has been initialized.
- Arguments:
interface GetClientBySessionIdParams { /** * The session id to retrieve the client details for */ sessionId: string; /** * A persistent store for auth data * @see https://github.com/clerk/mcp-tools?tab=readme-ov-file#stores */ store: McpClientStore; /** * If using this function in the OAuth callback route, pass in the state to * ensure that PKCE can run correctly. */ state?: string; }
-
Return Type:
interface McpClientReturnType { /** * Represents a session associated with the connected MCP service endpoint. */ sessionId: string; /** * Calling this function will initialize a connect to the MCP service. */ connect: () => void; /** * Lower level primitive, likely not necessary for use * @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/streamableHttp.ts#L119 */ transport: StreamableHTTPClientTransport; /** * Lower level primitive, likely not necessary for use * @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/index.ts#L81 */ client: Client; /** * Lower level primitive, likely not necessary for use * @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/auth.ts#L13 */ authProvider: OAuthClientProvider; }
-
completeAuthWithCode
-
Description: Designed to be used in the OAuth callback route. Passing in the code, state, and your store will finish the auth process
-
Arguments:
interface CompleteAuthWithCodeParams { /** * The authorization code returned from the auth provider via querystring. * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1 */ code: string; /** * The state returned from the auth provider via querystring. * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1 */ state: string; /** * A persistent store for auth data * @see https://github.com/clerk/mcp-tools?tab=readme-ov-file#stores */ store: McpClientStore; }
-
Return Type:
interface CompleteAuthWithCodeReturnType { /** * Lower level primitive, likely not necessary for use * @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/streamableHttp.ts#L119 */ transport: StreamableHTTPClientTransport; /** * Represents a session associated with the connected MCP service endpoint. */ sessionId: string; }
-
-
generateProtectedResourceMetadata
-
Description: Generates OAuth 2.0 Protected Resource Metadata as defined by RFC 9728. This metadata helps OAuth clients understand how to authenticate with your resource server.
-
Arguments:
interface GenerateProtectedResourceMetadataParams { /** * URL of the OAuth authorization server */ authServerUrl: string; /** * URL of the resource server that the metadata applies to */ resourceUrl: string; /** * Additional properties to include in the metadata */ properties?: Record<string, any>; }
-
Return Type:
{ resource: string; authorization_servers: string[]; token_types_supported: string[]; token_introspection_endpoint: string; token_introspection_endpoint_auth_methods_supported: string[]; jwks_uri: string; authorization_data_types_supported: string[]; authorization_data_locations_supported: string[]; key_challenges_supported: Array<{ challenge_type: string; challenge_algs: string[]; }>; [key: string]: any; // Additional properties if provided }
-
-
generateClerkProtectedResourceMetadata
-
Description: Generates OAuth 2.0 Protected Resource Metadata specifically configured for Clerk authentication. This is a convenience wrapper around
generateProtectedResourceMetadata
that automatically derives the auth server URL from your Clerk publishable key. -
Arguments:
interface GenerateClerkProtectedResourceMetadataParams { /** * Your Clerk publishable key (e.g., pk_test_... or pk_live_...) */ publishableKey: string; /** * URL of the resource server that the metadata applies to */ resourceUrl: string; }
-
Return Type: Same as
generateProtectedResourceMetadata
-
-
fetchClerkAuthorizationServerMetadata
-
Description: Fetches OAuth 2.0 Authorization Server Metadata from Clerk's servers based on your publishable key. This returns the actual metadata from Clerk's authorization server rather than generating it locally.
-
Arguments:
interface FetchClerkAuthorizationServerMetadataParams { /** * Your Clerk publishable key (e.g., pk_test_... or pk_live_...) */ publishableKey: string; }
-
Return Type:
Promise<{ issuer: string; authorization_endpoint: string; token_endpoint: string; userinfo_endpoint: string; jwks_uri: string; registration_endpoint?: string; scopes_supported: string[]; response_types_supported: string[]; response_modes_supported: string[]; grant_types_supported: string[]; subject_types_supported: string[]; id_token_signing_alg_values_supported: string[]; token_endpoint_auth_methods_supported: string[]; claims_supported: string[]; code_challenge_methods_supported: string[]; [key: string]: any; // Additional metadata from Clerk }>;
-
-
corsHeaders
-
Description: Pre-configured CORS headers for OAuth metadata endpoints. These headers allow cross-origin requests to your OAuth metadata endpoints, which is necessary for web-based MCP clients.
-
Value:
{ "Access-Control-Allow-Origin": "*"; "Access-Control-Allow-Methods": "GET, OPTIONS"; "Access-Control-Allow-Headers": "*"; "Access-Control-Max-Age": "86400"; }
-
For detailed documentation on server utilities and framework-specific implementations, see:
For framework-specific utilities and handlers, see:
- Express.js: See Express.js reference documentation
- Next.js: See Next.js reference documentation