一个基于MCP TypeScript SDK的库,使您更容易在MCP客户端和/或服务器中实现带有认证的MCP。
它是一种协议,允许像Claude、ChatGPT、Cursor等AI应用程序请求访问您的某些私人信息,这些信息通常需要您登录账户才能访问。例如,您的电子邮件或私有GitHub仓库等。
这使得您可以向AI应用程序提供其通常无法访问的上下文和能力。例如,您可以让AI应用程序使用某个私有仓库中的代码作为上下文来回答您的问题,或者在您审查后代表您编写并发送电子邮件。大致流程如下:
我们认为这很有价值,因为它使人们能够使用AI访问以前无法访问的一系列额外信息,并且以一种安全的方式进行,让用户控制AI可以访问什么以及它可以做什么。我们期待随着MCP采用的增长,新的AI用例会变得可能,因此我们构建了这个库,以帮助人们更轻松地将其集成到他们的应用程序中。
MCP涉及两个方面:
此库为这两个方面提供了工具,因此第一步是明确您是在构建客户端还是服务器。我们将分别处理这两种情况。
注意: 在Web开发中,“客户端”和“服务器”这两个词通常指的是前端(浏览器)和后端(Web服务器)。但在这种情况下并非如此,请尽量不要混淆!
对于特定框架的详细实施指南和示例,请参阅:
如果您正在构建一个希望引入MCP支持的服务器,您将需要使用@clerk/mcp-tools/server导入路径。
为了让MCP规范中的最新身份验证流程正确运行,您的服务器需要公开一个名为“受保护资源元数据”的静态元数据文件,该文件由RFC 9728定义。
此库提供了一个工具,可以快速生成这样的元数据文件。以下是如何使用它的示例:
import { generateProtectedResourceMetadata } from "@clerk/mcp-tools/server";
const result = generateProtectedResourceMetadata({
resourceUrl: "https://myapp.com/current-route",
authServerUrl: "https://auth.example.com",
});
您需要在.well-known/oauth-protected-resource设置一个路由,并确保从该路由返回此结果。
如果您在应用程序中使用Clerk进行身份验证,我们有一个辅助函数使其更容易:
import { generateClerkProtectedResourceMetadata } from "@clerk/mcp-tools/server";
const result = generateClerkProtectedResourceMetadata({
publishableKey: process.env.CLERK_PUBLISHABLE_KEY,
resourceUrl: "https://myapp.com/current-route",
});
有关受保护资源元数据处理器的具体框架实现,请参阅:
注意: 这尚未完全实现
有一个较旧版本的MCP规范,规定MCP服务器应自行负责身份验证,并且应实现另一种名为“授权服务器元数据”的静态元数据文件,该文件由RFC 8414定义。只要您已实现受保护资源元数据并且使用了正确实现了授权服务器元数据路由的身份验证服务,通常就不需要这样做。但在某些情况下,如果您正在构建自己的授权服务器,如果您的授权服务器是应用程序的一部分,或者如果您正在与具有过时实现的客户端交互,这可能是必要的。此库还为此用例提供了实用程序。
// 从此处返回结果:<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"],
});
在最标准的情况下,上述示例将起作用,但它确实对授权服务器做了一些假设,即:
<authServerUrl>/authorize<authServerUrl>/register<authServerUrl>/token<authServerUrl>/userinfo<authServerUrl>/.well-known/jwks.json如果情况并非如此,您可以传递这些值的覆盖。传递false将省略该值,在某些情况下这可能会有用,比如当您的授权服务器不支持动态客户端注册时:
// 从此处返回结果:<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"],
});
如果您在应用程序中使用Clerk进行身份验证,您可以使用以下辅助函数从Clerk前端API获取Clerk的元数据并返回它。
import { generateClerkAuthorizationServerMetadata } from "@clerk/mcp-tools/server";
const result = generateClerkAuthorizationServerMetadata();
有关具体框架实现,请参阅:
要创建一个处理实际MCP协议通信的MCP端点,您需要使用特定于框架的适配器,因为每个框架都有自己的方式处理请求和响应对象,而这些对象是实现MCP协议的关键部分:
@clerk/mcp-tools/express的streamableHttpHandler - 请参阅Express.js 指南这些适配器处理MCP协议细节并与您的身份验证系统集成。
将MCP兼容性构建到您的AI应用程序中的第一步是允许用户连接到MCP服务。这可以通过MCP兼容服务器的URL简单启动,如https://example.com/mcp。通常,您的应用程序会实现一个文本字段,用户可以在其中输入MCP端点,或者有一个预构建的集成,点击按钮会触发具有内置端点URL的MCP连接流程。
然而,实际上使用SDK进行MCP连接的过程相当繁琐,所以我们提供了一些工具来帮助简化这一过程。
以下是使用核心工具创建MCP客户端的方法:
import { createDynamicallyRegisteredMcpClient } from "@clerk/mcp-tools/client";
import { createRedisStore } from "@clerk/mcp-tools/stores/redis";
// 创建持久存储(根据环境选择合适的存储)
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) => {
// 实现适合您框架的重定向逻辑
window.location.href = url;
},
store,
});
// 连接到MCP端点
await connect();
return { sessionId };
}
用户完成OAuth流程后,您需要处理回调:
import { completeAuthWithCode } from "@clerk/mcp-tools/client";
export async function handleOAuthCallback(code: string, state: string) {
const result = await completeAuthWithCode({
state,
code,
store,
});
// OAuth流程已完成
return result;
}
一旦身份验证完成,您可以调用MCP工具:
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;
}
有关完整的框架特定实现和工作示例,请参阅:
要在客户端实现MCP功能,需要持久存储。这是因为:
因此,每个客户端函数都需要您传入一个存储适配器。有几种内置的存储适配器可用:
import fsStore from "@clerk/mcp-tools/stores/fs";
这使用临时文件,速度快,易于使用,适用于本地开发和测试。但是,它不适合生产环境,因为文件随时都可能被删除。
对于生产环境,请使用以下持久存储之一:
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",
});
如果您想使用不同类型的存储,可以通过遵守以下简单接口来实现自己的存储:
type JsonSerializable =
| null
| boolean
| number
| string
| JsonSerializable[]
| { [key: string]: JsonSerializable };
interface McpClientStore {
write: (key: string, data: JsonSerializable) => Promise<void>;
read: (key: string) => Promise<JsonSerializable>;
}
内置存储有一些额外的方法可能有用,但只有read和write是必需的。
以上示例更多是一个如何实现工具的指南,但对于深入挖掘的人来说,本节涵盖了从这个包中暴露的每个工具,它们接受哪些参数,以及它们返回什么。
@clerk/mcp-tools/clientcreateKnownCredentialsMcpClient
描述: 如果不想动态注册客户端,并且您的界面可以从现有OAuth客户端收集客户端ID和密钥,可以使用此函数创建一个MCP客户端。虽然这会增加用户体验的摩擦,但我们建议允许那些不启用动态客户端注册的MCP服务,因为这伴随着几个安全/欺诈风险,不是每个提供商都愿意承担。
参数:
interface CreateKnownCredentialsMcpClientParams {
/**
* OAuth客户端ID,预期通过用户输入收集
*/
clientId: string;
/**
* OAuth客户端密钥,预期通过用户输入收集
*/
clientSecret: string;
/**
* OAuth重定向URL - 用户同意后,此路由将获得授权码和状态。
*/
oauthRedirectUrl: string;
/**
* 您希望请求访问的OAuth范围
*/
oauthScopes?: string;
/**
* 预期通过用户输入收集的MCP服务端点
*/
mcpEndpoint: string;
/**
* 传递给MCP SDK创建的客户端的名称
* @see https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#writing-mcp-clients
*/
mcpClientName: string;
/**
* 传递给MCP SDK创建的客户端的版本号
* @see https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#writing-mcp-clients
*/
mcpClientVersion: string;
/**
* 当传入一个URL时,将重定向到给定URL的函数
*/
redirect: (url: string) => void;
/**
* 用于认证数据的持久存储
* @see https://github.com/clerk/mcp-tools?tab=readme-ov-file#stores
*/
store: McpClientStore;
}
返回类型:
interface McpClientReturnType {
/**
* 与连接的MCP服务端点关联的会话标识符。
*/
sessionId: string;
/**
* 调用此函数将初始化与MCP服务的连接。
*/
connect: () => void;
/**
* 较低级别的原始操作,通常不需要使用
* @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/streamableHttp.ts#L119
*/
transport: StreamableHTTPClientTransport;
/**
* 较低级别的原始操作,通常不需要使用
* @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/index.ts#L81
*/
client: Client;
/**
* 较低级别的原始操作,通常不需要使用
* @see https://github.com/modelcontextprotocol/typescript-sdk/blob/main/src/client/auth.ts#L13
*/
authProvider: OAuthClientProvider;
}
createDynamicallyRegisteredMcpClient
描述: 给定仅一个MCP服务端点URL,创建一个新的MCP客户端。通过OAuth 2.0 动态客户端注册协议按需在授权服务器上注册一个OAuth客户端。
参数:
interface CreateDynamicallyRegisteredMcpClientParams {
/**
* 预期通过用户输入收集的MCP服务端点
*/
mcpEndpoint: string;
/**
* OAuth重定向URL - 用户同意后,此路由将获得授权码和状态。
*/
oauthRedirectUrl: string;
/**
* 将与授权服务器一起创建的OAuth客户端名称
*/
oauthClientName?: string;
/**
* 将与授权服务器一起创建的OAuth客户端URI
*/
oauthClientUri?: string;
/**
* 您希望请求访问的OAuth范围
*/
oauthScopes?: string;
/**
* OAuth客户端是否为公共或机密
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.1
*/
oauthPublicClient?: boolean;
/**
* 传递给MCP SDK创建的客户端的名称
* @see https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#writing-mcp-clients
*/
mcpClientName: string;
/**
* 传递给MCP SDK创建的客户端的版本号
* @see https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#writing-mcp-clients
*/
mcpClientVersion: string;
/**
* 当传入一个URL时,将重定向到给定URL的函数
*/
redirect: (url: string) => void;
/**
* 用于认证数据的持久存储
* @see https://github.com/clerk/mcp-tools?tab=readme-ov-file#stores
*/
store: McpClientStore;
}
返回类型:
interface McpClientReturnType {
/**
* 与连接的MCP服务端点关联的会话标识符。
*/
sessionId: string;
/**
* 调用此函数将初始化与MCP服务的连接。
*/
connect: