Famma AI MCP Auth
Enables developers to build OAuth-protected MCP servers on Cloudflare Workers with pluggable authentication adapters, allowing user-specific access control and secure token exchange.
README
<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="Static Badge" src="https://img.shields.io/badge/Website-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="follow on 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="follow on 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 Version"></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="License"></a> </p>
Famma AI - MCP Auth
SDK for building OAuth-protected Remote MCP servers on Cloudflare Workers with pluggable auth adapters (Supabase already implemented).
Who is this for?
TL;DR: If you are building an MCP server/agent that needs user authentication but your identity provider does not yet offer an OAuth 2.1 flow (e.g., Supabase as of October 2025), this SDK helps you run your MCP behind a reverse-proxy-based OAuth flow and deploy it as a Cloudflare Worker. Use this if:
- You have an MCP agent/server and need authenticated access per user.
- Your IdP lacks a suitable OAuth 2.1 flow for your use case today.
- You want a Cloudflare Workers deployment with pluggable auth adapters (Supabase included).
Interface
- Exported primitives:
createOAuthProviderWithMCP,createAuthProxy - Adapters:
SupabaseAuthAdapter - Types:
AppConfig,AuthAdapter,CoreBindings,TokenExchangeResultAppConfig.loginPath?optional login route (default"/auth/login")
Install
npm install @famma/mcp-auth
Quickstart
Your MCP agent should be of type agents/mcp (extend or be compatible with 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 ?? "Unknown user") }],
}));
}
}
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 ?? "Example Co",
proxyTargetUrl: env.PROXY_TARGET_URL,
// Optional: customize the login route mounted by the proxy (default "/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);
},
};
Setup and Deployment
1. Create KV namespace
First, create a KV namespace for token storage:
npx wrangler kv namespace create OAUTH_KV
Copy the id value from the output.
2. Configure wrangler.jsonc
Create or update your wrangler.jsonc with the KV ID from the previous step. Make sure to keep the binding name as 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": "Example Co",
"LOGO_URL": "https://example.com/logo.png",
"PROXY_TARGET_URL": "https://your-login-host.example.com"
}
}
3. Create .dev.vars for local development
For local development, create a .dev.vars file in your project root:
# .dev.vars (for local development only - DO NOT commit to git)
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key-here
PROXY_TARGET_URL=http://localhost:3000
Note: .dev.vars is already included in .gitignore to prevent accidental commits of sensitive credentials.
4. Set secrets for production
For production deployment, configure all environment variables as secrets with Wrangler:
# Required secrets
npx wrangler secret put SUPABASE_URL
npx wrangler secret put SUPABASE_ANON_KEY
npx wrangler secret put PROXY_TARGET_URL
5. Run locally or deploy
# Local development (uses .dev.vars)
npx wrangler dev
# Deploy to production (uses wrangler secrets)
npx wrangler deploy
Use npx @modelcontextprotocol/inspector or npx @mcpjam/inspector@latest to connect and test your MCP server.
Example Project
See examples/supabase/ for a complete working Worker with:
- Example MCP agent
- Your Supabase Auth provider
- Runtime adapter construction from
env - Dev vars template and Wrangler config
Building a Custom Auth Provider (non-Supabase)
Implement the AuthAdapter interface and pass your adapter to createOAuthProviderWithMCP.
Key responsibilities:
getUser(c): return{ id, email }when authenticated, ornull.getSession(c): return{ accessToken, refreshToken, ... }ornull.getAuthorizationProps(c, user, session): return properties to persist with the OAuth token. Include anything required for future refreshes (e.g., API base URL, client id/secret, tenant).tokenExchangeCallback({ grantType, props })(optional): perform refresh flow and return updated tokens.
Minimal skeleton:
import type { Context } from 'hono';
import type {
AuthAdapter,
AuthUser,
AuthSession,
CoreBindings,
TokenExchangeResult,
} from '@famma/mcp-auth';
export interface MyBindings extends CoreBindings {
// Add any additional env bindings if your provider needs them
}
export class HeaderAuthAdapter implements AuthAdapter<MyBindings> {
async getUser(c: Context<{ Bindings: MyBindings }>): Promise<AuthUser | null> {
// Example: derive user from 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> {
// Example: access token from header/cookie; refresh token optional
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,
// Add provider-specific props needed for future refresh
providerBaseUrl: 'https://api.example.com',
clientId: 'your-client-id',
};
}
// Optional: implement refresh flow
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;
// Perform your provider's refresh request here
// const resp = await fetch('https://api.example.com/oauth/token', { ... });
// const json = await resp.json();
const newAccess = 'NEW_ACCESS_TOKEN';
const newRefresh = rt; // or a rotated token
return {
accessTokenProps: { ...props, accessToken: newAccess },
newProps: { ...props, accessToken: newAccess, refreshToken: newRefresh },
// accessTokenTTL: json.expires_in,
};
}
}
Wire it into a Worker:
import { createOAuthProviderWithMCP, type AppConfig } from '@famma/mcp-auth';
import { McpAgent } from 'agents/mcp';
import { HeaderAuthAdapter } from './header-auth-adapter';
class MyMCP extends McpAgent { /* ...tools... */ }
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,
// Optional: customize login route (default "/auth/login")
loginPath: env.LOGIN_PATH ?? "/auth/login",
};
const authAdapter = new HeaderAuthAdapter();
return createOAuthProviderWithMCP({
mcpAgentClass: MyMCP,
authAdapter,
appConfig,
}).fetch(request, env, ctx);
}
}
A full runnable sample is in 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? })- Returns an
OAuthProviderWorker-compatible handler. UsesauthAdapter.tokenExchangeCallbackby default.
- Returns an
createAuthProxy(authAdapter, appConfig)- Returns a Hono app implementing
/authorize,/approve,loginPath(default"/auth/login"), and a reverse proxy.
- Returns a Hono app implementing
SupabaseAuthAdapter(config: SupabaseAdapterConfig)- Requires:
supabaseUrl,supabaseAnonKey.
- Requires:
AuthAdapter contract
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>;
}
The Supabase adapter implements tokenExchangeCallback to rotate refresh_token via Supabase.
Notes
- Cloudflare Workers do not use
process.env; read runtime config fromenvinfetch. - The OAuth provider requires
OAUTH_KVconfigured in Wrangler.
Compatibility and requirements
- Cloudflare Workers: compatibility_date
2025-03-10or newer - Wrangler: v4.42+ (with
nodejs_compatflag enabled) - KV:
OAUTH_KVnamespace required for token storage - Node.js: 18+ for local development/build tooling
- TypeScript: 5.9+
Note: Environment variables are optional in examples; you may hardcode values or use vars/secrets in Wrangler for production.
Contributing
Requirements: Node 18+, npm, Wrangler.
Development:
npm install
npm run build
# Example worker (dev)
cd examples/supabase
npx wrangler dev
# Formatting / lint
npm run format
npm run lint:fix
Please open issues or PRs on GitHub.
Links
Repo: https://github.com/famma-ai/mcp-auth
- MCP: https://modelcontextprotocol.io
- Cloudflare Workers OAuth provider: https://developers.cloudflare.com/workers/
- npm: https://www.npmjs.com/package/@famma/mcp-auth
Credits
Built on top of Josh Warwick's comprehensive guide on building Remote MCP servers, this SDK extends his work into a reusable, pluggable adapter architecture.
License
MIT © 2025 Famma. See LICENSE for details.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。