{"id":32675617,"url":"https://github.com/famma-ai/mcp-auth","last_synced_at":"2026-03-05T07:37:03.486Z","repository":{"id":321534495,"uuid":"1085401005","full_name":"famma-ai/mcp-auth","owner":"famma-ai","description":"MCP Auth via Reverse Proxy ","archived":false,"fork":false,"pushed_at":"2025-10-30T06:28:27.000Z","size":197,"stargazers_count":12,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-30T07:09:01.145Z","etag":null,"topics":["auth","cloudflare","mcp","modelcontextprotocol","oauth","reverse-proxy","supabase"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/famma-ai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-29T01:47:33.000Z","updated_at":"2025-10-30T06:44:58.000Z","dependencies_parsed_at":"2025-10-30T07:23:25.174Z","dependency_job_id":null,"html_url":"https://github.com/famma-ai/mcp-auth","commit_stats":null,"previous_names":["famma-ai/mcp-auth"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/famma-ai/mcp-auth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/famma-ai%2Fmcp-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/famma-ai%2Fmcp-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/famma-ai%2Fmcp-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/famma-ai%2Fmcp-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/famma-ai","download_url":"https://codeload.github.com/famma-ai/mcp-auth/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/famma-ai%2Fmcp-auth/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":282607307,"owners_count":26697280,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-04T02:00:05.887Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["auth","cloudflare","mcp","modelcontextprotocol","oauth","reverse-proxy","supabase"],"created_at":"2025-11-01T06:02:06.277Z","updated_at":"2025-11-04T09:01:51.105Z","avatar_url":"https://github.com/famma-ai.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\".github/images/white-preference.png\"\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\".github/images/dark-preference.png\"\u003e\n    \u003cimg alt=\"Famma AI - MCP Auth Logo\" src=\".github/images/white-preference.png\" width=\"100%\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://famma.ai\" target=\"_blank\"\u003e\n          \u003cimg alt=\"Static Badge\" src=\"https://img.shields.io/badge/Website-F04438\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://twitter.com/intent/follow?screen_name=Famma_AI\" target=\"_blank\"\u003e\n          \u003cimg src=\"https://img.shields.io/twitter/follow/Famma_AI?logo=X\u0026color=%20%23f5f5f5\"\n              alt=\"follow on X(Twitter)\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.linkedin.com/company/109541898/\" target=\"_blank\"\u003e\n          \u003cimg src=\"https://custom-icon-badges.demolab.com/badge/LinkedIn-0A66C2?logo=linkedin-white\u0026logoColor=fff\"\n              alt=\"follow on LinkedIn\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@famma/mcp-auth\" target=\"_blank\"\u003e\n           \u003cimg src=\"https://img.shields.io/npm/v/%40famma%2Fmcp-auth\"\n  alt=\"NPM Version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/famma-ai/mcp-auth/blob/main/LICENSE\" target=\"_blank\"\u003e\n          \u003cimg src=\"https://img.shields.io/github/license/famma-ai/mcp-auth\"\n              alt=\"License\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# Famma AI - MCP Auth\n\nSDK for building OAuth-protected Remote MCP servers on Cloudflare Workers with pluggable auth adapters (Supabase already implemented).\n\n## Who is this for?\n\nTL;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.\nUse this if:\n- You have an MCP agent/server and need authenticated access per user.\n- Your IdP lacks a suitable OAuth 2.1 flow for your use case today.\n- You want a Cloudflare Workers deployment with pluggable auth adapters (Supabase included).\n\n## Interface\n- Exported primitives: `createOAuthProviderWithMCP`, `createAuthProxy`\n- Adapters: `SupabaseAuthAdapter`\n- Types: `AppConfig`, `AuthAdapter`, `CoreBindings`, `TokenExchangeResult`\n  - `AppConfig.loginPath?` optional login route (default `\"/auth/login\"`)\n\n## Install\n\n```bash\nnpm install @famma/mcp-auth\n```\n\n## Quickstart\n\nYour MCP agent should be of type `agents/mcp` (extend or be compatible with `McpAgent`).\n\n```ts\n// src/worker.ts\nimport { createOAuthProviderWithMCP, SupabaseAuthAdapter, type AppConfig } from \"@famma/mcp-auth\";\nimport { McpAgent } from \"agents/mcp\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nclass MyMCP extends McpAgent {\n  server = new McpServer({ name: \"Demo\", version: \"1.0.0\" });\n  async init() {\n    this.server.tool(\"whoami\", async () =\u003e ({\n      content: [{ type: \"text\", text: String(this.props?.userEmail ?? \"Unknown user\") }],\n    }));\n  }\n}\n\nlet provider: ReturnType\u003ctypeof createOAuthProviderWithMCP\u003e | undefined;\n\nexport default {\n  async fetch(request: Request, env: any, ctx: ExecutionContext) {\n    if (!provider) {\n      const appConfig: AppConfig = {\n        logoUrl: env.LOGO_URL ?? \"https://example.com/logo.png\",\n        companyName: env.COMPANY_NAME ?? \"Example Co\",\n        proxyTargetUrl: env.PROXY_TARGET_URL,\n        // Optional: customize the login route mounted by the proxy (default \"/auth/login\")\n        loginPath: env.LOGIN_PATH ?? \"/auth/login\",\n      };\n\n      const authAdapter = new SupabaseAuthAdapter({\n        supabaseUrl: env.SUPABASE_URL,\n        supabaseAnonKey: env.SUPABASE_ANON_KEY,\n      });\n\n      provider = createOAuthProviderWithMCP({\n        mcpAgentClass: MyMCP,\n        authAdapter,\n        appConfig,\n      });\n    }\n    return provider.fetch(request, env, ctx);\n  },\n};\n```\n\n## Setup and Deployment\n\n### 1. Create KV namespace\n\nFirst, create a KV namespace for token storage:\n\n```bash\nnpx wrangler kv namespace create OAUTH_KV\n```\n\nCopy the `id` value from the output.\n\n### 2. Configure wrangler.jsonc\n\nCreate or update your `wrangler.jsonc` with the KV ID from the previous step. Make sure to keep the `binding` name as `OAUTH_KV`:\n\n```json\n{\n  \"name\": \"mcp-worker\",\n  \"main\": \"src/worker.ts\",\n  \"compatibility_date\": \"2025-03-10\",\n  \"compatibility_flags\": [\"nodejs_compat\"],\n  \"kv_namespaces\": [\n    { \"binding\": \"OAUTH_KV\", \"id\": \"\u003cyour-kv-id\u003e\" }\n  ],\n  \"vars\": {\n    \"COMPANY_NAME\": \"Example Co\",\n    \"LOGO_URL\": \"https://example.com/logo.png\",\n    \"PROXY_TARGET_URL\": \"https://your-login-host.example.com\"\n  }\n}\n```\n\n### 3. Create .dev.vars for local development\n\nFor local development, create a `.dev.vars` file in your project root:\n\n```bash\n# .dev.vars (for local development only - DO NOT commit to git)\nSUPABASE_URL=https://your-project.supabase.co\nSUPABASE_ANON_KEY=your-anon-key-here\nPROXY_TARGET_URL=http://localhost:3000\n```\n\n**Note**: `.dev.vars` is already included in `.gitignore` to prevent accidental commits of sensitive credentials.\n\n### 4. Set secrets for production\n\nFor production deployment, configure all environment variables as secrets with Wrangler:\n\n```bash\n# Required secrets\nnpx wrangler secret put SUPABASE_URL\nnpx wrangler secret put SUPABASE_ANON_KEY\nnpx wrangler secret put PROXY_TARGET_URL\n```\n\n### 5. Run locally or deploy\n\n```bash\n# Local development (uses .dev.vars)\nnpx wrangler dev\n\n# Deploy to production (uses wrangler secrets)\nnpx wrangler deploy\n```\n\nUse `npx @modelcontextprotocol/inspector` or `npx @mcpjam/inspector@latest` to connect and test your MCP server.\n\n## Example Project\n\nSee `examples/supabase/` for a complete working Worker with:\n- Example MCP agent\n- Your Supabase Auth provider\n- Runtime adapter construction from `env`\n- Dev vars template and Wrangler config\n\n## Building a Custom Auth Provider (non-Supabase)\n\nImplement the `AuthAdapter` interface and pass your adapter to `createOAuthProviderWithMCP`.\n\nKey responsibilities:\n- `getUser(c)`: return `{ id, email }` when authenticated, or `null`.\n- `getSession(c)`: return `{ accessToken, refreshToken, ... }` or `null`.\n- `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).\n- `tokenExchangeCallback({ grantType, props })` (optional): perform refresh flow and return updated tokens.\n\nMinimal skeleton:\n\n```ts\nimport type { Context } from 'hono';\nimport type {\n  AuthAdapter,\n  AuthUser,\n  AuthSession,\n  CoreBindings,\n  TokenExchangeResult,\n} from '@famma/mcp-auth';\n\nexport interface MyBindings extends CoreBindings {\n  // Add any additional env bindings if your provider needs them\n}\n\nexport class HeaderAuthAdapter implements AuthAdapter\u003cMyBindings\u003e {\n  async getUser(c: Context\u003c{ Bindings: MyBindings }\u003e): Promise\u003cAuthUser | null\u003e {\n    // Example: derive user from headers/cookies/session\n    const userId = c.req.header('x-user-id');\n    const userEmail = c.req.header('x-user-email');\n    if (!userId) return null;\n    return { id: userId, email: userEmail ?? null };\n  }\n\n  async getSession(c: Context\u003c{ Bindings: MyBindings }\u003e): Promise\u003cAuthSession | null\u003e {\n    // Example: access token from header/cookie; refresh token optional\n    const accessToken = c.req.header('x-access-token');\n    const refreshToken = c.req.header('x-refresh-token') ?? '';\n    if (!accessToken) return null;\n    return { accessToken, refreshToken };\n  }\n\n  async getAuthorizationProps(\n    _c: Context\u003c{ Bindings: MyBindings }\u003e,\n    user: AuthUser,\n    session: AuthSession,\n  ): Promise\u003cRecord\u003cstring, any\u003e\u003e {\n    return {\n      userEmail: user.email ?? '',\n      userId: user.id,\n      accessToken: session.accessToken,\n      refreshToken: session.refreshToken,\n      // Add provider-specific props needed for future refresh\n      providerBaseUrl: 'https://api.example.com',\n      clientId: 'your-client-id',\n    };\n  }\n\n  // Optional: implement refresh flow\n  async tokenExchangeCallback({ grantType, props }: { grantType: string; props: Record\u003cstring, any\u003e }): Promise\u003cTokenExchangeResult | void\u003e {\n    if (grantType !== 'refresh_token') return;\n    const rt = props?.refreshToken as string | undefined;\n    if (!rt) return;\n    // Perform your provider's refresh request here\n    // const resp = await fetch('https://api.example.com/oauth/token', { ... });\n    // const json = await resp.json();\n    const newAccess = 'NEW_ACCESS_TOKEN';\n    const newRefresh = rt; // or a rotated token\n    return {\n      accessTokenProps: { ...props, accessToken: newAccess },\n      newProps: { ...props, accessToken: newAccess, refreshToken: newRefresh },\n      // accessTokenTTL: json.expires_in,\n    };\n  }\n}\n```\n\nWire it into a Worker:\n\n```ts\nimport { createOAuthProviderWithMCP, type AppConfig } from '@famma/mcp-auth';\nimport { McpAgent } from 'agents/mcp';\nimport { HeaderAuthAdapter } from './header-auth-adapter';\n\nclass MyMCP extends McpAgent { /* ...tools... */ }\n\nexport default {\n  async fetch(request: Request, env: any, ctx: ExecutionContext) {\n    const appConfig: AppConfig = {\n      logoUrl: env.LOGO_URL,\n      companyName: env.COMPANY_NAME,\n      proxyTargetUrl: env.PROXY_TARGET_URL,\n      // Optional: customize login route (default \"/auth/login\")\n      loginPath: env.LOGIN_PATH ?? \"/auth/login\",\n    };\n    const authAdapter = new HeaderAuthAdapter();\n    return createOAuthProviderWithMCP({\n      mcpAgentClass: MyMCP,\n      authAdapter,\n      appConfig,\n    }).fetch(request, env, ctx);\n  }\n}\n```\n\nA full runnable sample is in `examples/custom-adapter/`.\n\n## API\n\n```ts\nimport {\n  createOAuthProviderWithMCP,\n  createAuthProxy,\n  SupabaseAuthAdapter,\n  type SupabaseAdapterConfig,\n  type SupabaseBindings,\n  type AppConfig,\n  type AuthAdapter,\n  type CoreBindings,\n  type TokenExchangeResult,\n} from \"@famma/mcp-auth\";\n```\n\n- `createOAuthProviderWithMCP({ mcpAgentClass, authAdapter, appConfig, tokenExchangeCallback? })`\n  - Returns an `OAuthProvider` Worker-compatible handler. Uses `authAdapter.tokenExchangeCallback` by default.\n- `createAuthProxy(authAdapter, appConfig)`\n  - Returns a Hono app implementing `/authorize`, `/approve`, `loginPath` (default `\"/auth/login\"`), and a reverse proxy.\n- `SupabaseAuthAdapter(config: SupabaseAdapterConfig)`\n  - Requires: `supabaseUrl`, `supabaseAnonKey`.\n\n### AuthAdapter contract\n\n```ts\ninterface AuthAdapter\u003cTBindings = any\u003e {\n  getUser(c): Promise\u003cAuthUser | null\u003e;\n  getSession(c): Promise\u003cAuthSession | null\u003e;\n  getAuthorizationProps(c, user, session): Promise\u003cRecord\u003cstring, any\u003e\u003e;\n  tokenExchangeCallback?: (args: { grantType: string; props: Record\u003cstring, any\u003e }) =\u003e Promise\u003cTokenExchangeResult | void\u003e;\n}\n```\n\nThe Supabase adapter implements `tokenExchangeCallback` to rotate `refresh_token` via Supabase.\n\n## Notes\n- Cloudflare Workers do not use `process.env`; read runtime config from `env` in `fetch`.\n- The OAuth provider requires `OAUTH_KV` configured in Wrangler.\n\n## Compatibility and requirements\n\n- Cloudflare Workers: compatibility_date `2025-03-10` or newer\n- Wrangler: v4.42+ (with `nodejs_compat` flag enabled)\n- KV: `OAUTH_KV` namespace required for token storage\n- Node.js: 18+ for local development/build tooling\n- TypeScript: 5.9+\n\nNote: Environment variables are optional in examples; you may hardcode values or use `vars`/secrets in Wrangler for production.\n\n## Contributing\n\nRequirements: Node 18+, npm, Wrangler.\n\nDevelopment:\n\n```bash\nnpm install\nnpm run build\n\n# Example worker (dev)\ncd examples/supabase\nnpx wrangler dev\n\n# Formatting / lint\nnpm run format\nnpm run lint:fix\n```\n\nPlease open issues or PRs on GitHub.\n\n## Links\n\nRepo: https://github.com/famma-ai/mcp-auth\n\n- MCP: https://modelcontextprotocol.io\n- Cloudflare Workers OAuth provider: https://developers.cloudflare.com/workers/\n- npm: https://www.npmjs.com/package/@famma/mcp-auth\n\n## Credits\n\nBuilt on top of [Josh Warwick's comprehensive guide on building Remote MCP servers](https://blog.remote-mcp.com/p/from-hackathon-to-revenue-how-i-built), this SDK extends his work into a reusable, pluggable adapter architecture.\n\n## License\n\nMIT © 2025 Famma. See `LICENSE` for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffamma-ai%2Fmcp-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffamma-ai%2Fmcp-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffamma-ai%2Fmcp-auth/lists"}