返回市场
MCP认证服务器

MCP认证服务器

作者:famma-ai26 星标更新:2025-10-31

项目介绍

<p align="center"> <picture> <source media="(prefers-color-scheme: light)" srcset=".github/images/white-preference.png"> <source media="(prefers-color-scheme: dark)" srcset=".github/images/dark-preference.png"> <img alt="Famma AI - MCP Auth Logo" src=".github/images/white-preference.png" width="100%"> </picture> </p> <p align="center"> <a href="https://famma.ai" target="_blank"> <img alt="静态徽章" src="https://img.shields.io/badge/网站-F04438"></a> <a href="https://twitter.com/intent/follow?screen_name=Famma_AI" target="_blank"> <img src="https://img.shields.io/twitter/follow/Famma_AI?logo=X&color=%20%23f5f5f5" alt="在X(Twitter)上关注"></a> <a href="https://www.linkedin.com/company/109541898/" target="_blank"> <img src="https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white&logoColor=fff" alt="在LinkedIn上关注"></a> <a href="https://www.npmjs.com/package/@famma/mcp-auth" target="_blank"> <img src="https://img.shields.io/npm/v/%40famma%2Fmcp-auth" alt="NPM版本"></a> <a href="https://github.com/famma-ai/mcp-auth/blob/main/LICENSE" target="_blank"> <img src="https://img.shields.io/github/license/famma-ai/mcp-auth" alt="许可证"></a> </p>

Famma AI - MCP Auth

用于在Cloudflare Workers上构建受OAuth保护的远程MCP服务器的SDK,支持可插拔的身份验证适配器(Supabase已实现)。

这是为谁准备的?

简而言之:如果你正在构建一个需要用户身份验证的MCP服务器/代理,但你的身份提供商尚未提供OAuth 2.1流程(例如,截至2025年10月的Supabase),此SDK可以帮助你在反向代理基础上运行OAuth流程,并将其部署为Cloudflare Worker。 使用场景:

  • 你有一个MCP代理/服务器,并且需要每个用户的认证访问。
  • 你的IdP目前缺乏适用于你用例的OAuth 2.1流程。
  • 你希望拥有一个带有可插拔身份验证适配器的Cloudflare Workers部署(包括Supabase)。

接口

  • 导出的基本类型:createOAuthProviderWithMCP, createAuthProxy
  • 适配器:SupabaseAuthAdapter
  • 类型:AppConfig, AuthAdapter, CoreBindings, TokenExchangeResult
    • AppConfig.loginPath? 可选登录路由(默认为"/auth/login"

安装

npm install @famma/mcp-auth

快速开始

你的MCP代理应为类型agents/mcp(扩展或兼容McpAgent)。

// src/worker.ts
import { createOAuthProviderWithMCP, SupabaseAuthAdapter, type AppConfig } from "@famma/mcp-auth";
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

class MyMCP extends McpAgent {
  server = new McpServer({ name: "Demo", version: "1.0.0" });
  async init() {
    this.server.tool("whoami", async () => ({
      content: [{ type: "text", text: String(this.props?.userEmail ?? "未知用户") }],
    }));
  }
}

let provider: ReturnType<typeof createOAuthProviderWithMCP> | undefined;

export default {
  async fetch(request: Request, env: any, ctx: ExecutionContext) {
    if (!provider) {
      const appConfig: AppConfig = {
        logoUrl: env.LOGO_URL ?? "https://example.com/logo.png",
        companyName: env.COMPANY_NAME ?? "示例公司",
        proxyTargetUrl: env.PROXY_TARGET_URL,
        // 可选:自定义代理挂载的登录路由(默认为"/auth/login")
        loginPath: env.LOGIN_PATH ?? "/auth/login",
      };

      const authAdapter = new SupabaseAuthAdapter({
        supabaseUrl: env.SUPABASE_URL,
        supabaseAnonKey: env.SUPABASE_ANON_KEY,
      });

      provider = createOAuthProviderWithMCP({
        mcpAgentClass: MyMCP,
        authAdapter,
        appConfig,
      });
    }
    return provider.fetch(request, env, ctx);
  },
};

配置与部署

1. 创建KV命名空间

首先,创建一个KV命名空间用于存储令牌:

npx wrangler kv namespace create OAUTH_KV

复制输出中的id值。

2. 配置wrangler.jsonc

创建或更新你的wrangler.jsonc,并添加上一步中的KV ID。确保绑定名称为OAUTH_KV

{
  "name": "mcp-worker",
  "main": "src/worker.ts",
  "compatibility_date": "2025-03-10",
  "compatibility_flags": ["nodejs_compat"],
  "kv_namespaces": [
    { "binding": "OAUTH_KV", "id": "<your-kv-id>" }
  ],
  "vars": {
    "COMPANY_NAME": "示例公司",
    "LOGO_URL": "https://example.com/logo.png",
    "PROXY_TARGET_URL": "https://your-login-host.example.com"
  }
}

3. 创建.dev.vars用于本地开发

为了本地开发,在项目根目录中创建一个.dev.vars文件:

# .dev.vars(仅用于本地开发 - 不要提交到git)
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key-here
PROXY_TARGET_URL=http://localhost:3000

注意.dev.vars已经包含在.gitignore中,以防止意外提交敏感凭证。

4. 设置生产环境的秘密

对于生产部署,使用Wrangler配置所有环境变量为秘密:

# 必需的秘密
npx wrangler secret put SUPABASE_URL
npx wrangler secret put SUPABASE_ANON_KEY
npx wrangler secret put PROXY_TARGET_URL

5. 本地运行或部署

# 本地开发(使用.dev.vars)
npx wrangler dev

# 部署到生产(使用wrangler秘密)
npx wrangler deploy

使用npx @modelcontextprotocol/inspectornpx @mcpjam/inspector@latest连接并测试你的MCP服务器。

示例项目

查看examples/supabase/以获取一个完整的可运行Worker示例,其中包括:

  • 示例MCP代理
  • 你的Supabase Auth提供商
  • env动态构造运行时适配器
  • 开发变量模板和Wrangler配置

构建自定义身份验证提供者(非Supabase)

实现AuthAdapter接口,并将你的适配器传递给createOAuthProviderWithMCP

关键职责:

  • getUser(c):当认证成功时返回{ id, email },否则返回null
  • getSession(c):返回{ accessToken, refreshToken, ... }null
  • getAuthorizationProps(c, user, session):返回与OAuth令牌一起持久化的属性。包括未来刷新所需的任何内容(例如,API基础URL,客户端ID/密钥,租户)。
  • tokenExchangeCallback({ grantType, props })(可选):执行刷新流程并返回更新的令牌。

最小骨架:

import type { Context } from 'hono';
import type {
  AuthAdapter,
  AuthUser,
  AuthSession,
  CoreBindings,
  TokenExchangeResult,
} from '@famma/mcp-auth';

export interface MyBindings extends CoreBindings {
  // 如果你的提供商需要额外的环境绑定,请在此处添加
}

export class HeaderAuthAdapter implements AuthAdapter<MyBindings> {
  async getUser(c: Context<{ Bindings: MyBindings }>): Promise<AuthUser | null> {
    // 示例:从headers/cookies/session中推导用户
    const userId = c.req.header('x-user-id');
    const userEmail = c.req.header('x-user-email');
    if (!userId) return null;
    return { id: userId, email: userEmail ?? null };
  }

  async getSession(c: Context<{ Bindings: MyBindings }>): Promise<AuthSession | null> {
    // 示例:从header/cookie中获取访问令牌;刷新令牌可选
    const accessToken = c.req.header('x-access-token');
    const refreshToken = c.req.header('x-refresh-token') ?? '';
    if (!accessToken) return null;
    return { accessToken, refreshToken };
  }

  async getAuthorizationProps(
    _c: Context<{ Bindings: MyBindings }>,
    user: AuthUser,
    session: AuthSession,
  ): Promise<Record<string, any>> {
    return {
      userEmail: user.email ?? '',
      userId: user.id,
      accessToken: session.accessToken,
      refreshToken: session.refreshToken,
      // 添加未来刷新所需的提供商特定属性
      providerBaseUrl: 'https://api.example.com',
      clientId: 'your-client-id',
    };
  }

  // 可选:实现刷新流程
  async tokenExchangeCallback({ grantType, props }: { grantType: string; props: Record<string, any> }): Promise<TokenExchangeResult | void> {
    if (grantType !== 'refresh_token') return;
    const rt = props?.refreshToken as string | undefined;
    if (!rt) return;
    // 在此处执行你的提供商的刷新请求
    // const resp = await fetch('https://api.example.com/oauth/token', { ... });
    // const json = await resp.json();
    const newAccess = 'NEW_ACCESS_TOKEN';
    const newRefresh = rt; // 或旋转的新令牌
    return {
      accessTokenProps: { ...props, accessToken: newAccess },
      newProps: { ...props, accessToken: newAccess, refreshToken: newRefresh },
      // accessTokenTTL: json.expires_in,
    };
  }
}

将其集成到Worker中:

import { createOAuthProviderWithMCP, type AppConfig } from '@famma/mcp-auth';
import { McpAgent } from 'agents/mcp';
import { HeaderAuthAdapter } from './header-auth-adapter';

class MyMCP extends McpAgent { /* ...工具... */ }

export default {
  async fetch(request: Request, env: any, ctx: ExecutionContext) {
    const appConfig: AppConfig = {
      logoUrl: env.LOGO_URL,
      companyName: env.COMPANY_NAME,
      proxyTargetUrl: env.PROXY_TARGET_URL,
      // 可选:自定义登录路由(默认为"/auth/login")
      loginPath: env.LOGIN_PATH ?? "/auth/login",
    };
    const authAdapter = new HeaderAuthAdapter();
    return createOAuthProviderWithMCP({
      mcpAgentClass: MyMCP,
      authAdapter,
      appConfig,
    }).fetch(request, env, ctx);
  }
}

完整的可运行示例在examples/custom-adapter/中。

API

import {
  createOAuthProviderWithMCP,
  createAuthProxy,
  SupabaseAuthAdapter,
  type SupabaseAdapterConfig,
  type SupabaseBindings,
  type AppConfig,
  type AuthAdapter,
  type CoreBindings,
  type TokenExchangeResult,
} from "@famma/mcp-auth";
  • createOAuthProviderWithMCP({ mcpAgentClass, authAdapter, appConfig, tokenExchangeCallback? })
    • 返回一个与Worker兼容的OAuthProvider处理器。默认使用authAdapter.tokenExchangeCallback
  • createAuthProxy(authAdapter, appConfig)
    • 返回一个实现/authorize, /approve, loginPath(默认为"/auth/login")和反向代理的Hono应用。
  • SupabaseAuthAdapter(config: SupabaseAdapterConfig)
    • 需要:supabaseUrl, supabaseAnonKey

AuthAdapter合同

interface AuthAdapter<TBindings = any> {
  getUser(c): Promise<AuthUser | null>;
  getSession(c): Promise<AuthSession | null>;
  getAuthorizationProps(c, user, session): Promise<Record<string, any>>;
  tokenExchangeCallback?: (args: { grantType: string; props: Record<string, any> }) => Promise<TokenExchangeResult | void>;
}

Supabase适配器实现了tokenExchangeCallback以通过Supabase轮换refresh_token

注意事项

  • Cloudflare Workers不使用process.env;在fetch中从env读取运行时配置。
  • OAuth提供者需要在Wrangler中配置OAUTH_KV

兼容性和需求

  • Cloudflare Workers:兼容日期2025-03-10或更新
  • Wrangler:v4.42+(启用nodejs_compat标志)
  • KV:需要OAUTH_KV命名空间用于令牌存储
  • Node.js:18+用于本地开发/构建工具
  • TypeScript:5.9+

注意:示例中的环境变量是可选的;你可以硬编码值,或者在Wrangler中使用vars/秘密进行生产部署。

贡献

需求:Node 18+, npm, Wrangler。

开发:

npm install
npm run build

# 示例Worker(开发)
cd examples/supabase
npx wrangler dev

# 格式化/检查
npm run format
npm run lint:fix

请在GitHub上打开问题或PR。

链接

仓库:https://github.com/famma-ai/mcp-auth

致谢

基于Josh Warwick关于构建远程MCP服务器的全面指南,此SDK扩展了他的工作,形成了一种可重用、可插拔的适配器架构。

许可证

MIT © 2.025 Famma. 查看LICENSE以获取详细信息。