Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mcp-use.com/llms.txt

Use this file to discover all available pages before exploring further.

Use Supabase’s OAuth 2.1 server to sign users into your MCP server. Supabase handles the OAuth flow and issues the tokens; your server verifies them and you host the consent page.

Prerequisites

Before your MCP server will work, configure these in the Supabase Dashboard:
1

Enable the OAuth 2.1 server

Authentication → Sign In / Providers → OAuth Server → toggle on. Also toggle Allow Dynamic OAuth Apps so MCP clients can self-register.
2

Set the consent screen URL

Point it at the route your MCP server will host, e.g. http://localhost:3000/auth/consent. Supabase redirects the browser here with ?authorization_id=<uuid> after /authorize.
3

Enable a sign-in method

Users must be signed in before they can consent. Pick one under Authentication → Sign In / Providers:
  • Anonymous sign-ins — one-click guest sessions, ideal for demos
  • Email + password, magic links, or OAuth providers (Google, GitHub, etc.) — for real apps
4

Copy your keys

From Project Settings:
  • Project ID (General)
  • Publishable keysb_publishable_... (API Keys). Used by your consent route and by tools that call Supabase — not by the provider itself.
Supabase redirects the browser to the consent screen URL you configured above. Your route signs the user in, loads the authorization details with the Supabase JS SDK, and submits the approve/deny decision back to Supabase — mcp-use is not involved in this step. Don’t want to build it from scratch? Start from the mcp-oauth-supabase-template — it has sign-in, consent, and approve/deny already wired up. Or follow Supabase’s OAuth Server — Getting Started guide to roll your own.

Environment variables

# Required — read by oauthSupabaseProvider()
MCP_USE_OAUTH_SUPABASE_PROJECT_ID=your-project-id

# Used by your consent UI and by tools calling Supabase REST/APIs.
# Not read by oauthSupabaseProvider() itself.
MCP_USE_OAUTH_SUPABASE_PUBLISHABLE_KEY=sb_publishable_...

Configure the MCP server

// server.ts
import { MCPServer, oauthSupabaseProvider } from "mcp-use/server";

const server = new MCPServer({
  name: "my-server",
  version: "1.0.0",
  oauth: oauthSupabaseProvider(), // reads MCP_USE_OAUTH_SUPABASE_PROJECT_ID
});

await server.listen(3000);

Configuration options

oauthSupabaseProvider({
  // Required (or set MCP_USE_OAUTH_SUPABASE_PROJECT_ID)
  projectId: "your-project-id",

  // Skip JWT verification — development only
  verifyJwt: process.env.NODE_ENV === "production",

  // Override advertised scopes
  scopesSupported: ["openid", "profile", "email"],
});
New Supabase projects sign tokens with ES256 and expose a JWKS endpoint — the provider fetches it automatically, no secret needed. Legacy HS256 projects can still pass a jwtSecret (or set MCP_USE_OAUTH_SUPABASE_JWT_SECRET), but new projects do not need it.

Accessing user info in tools

server.tool(
  {
    name: "get-user-info",
    description: "Get authenticated user info",
  },
  async (_args, ctx) => ({
    userId: ctx.auth.user.userId,
    email: ctx.auth.user.email,
    name: ctx.auth.user.name,
    scopes: ctx.auth.scopes,
  }),
);
You can also call Supabase from within a tool using the user’s access token. Create a Supabase client scoped to the request so RLS policies see the caller as the authenticated user:
import { createClient } from "@supabase/supabase-js";

server.tool(
  {
    name: "list-notes",
    description: "Fetch the user's notes",
  },
  async (_args, ctx) => {
    const supabase = createClient(
      `https://${process.env.MCP_USE_OAUTH_SUPABASE_PROJECT_ID}.supabase.co`,
      process.env.MCP_USE_OAUTH_SUPABASE_PUBLISHABLE_KEY!,
      {
        auth: { persistSession: false, autoRefreshToken: false, detectSessionInUrl: false },
        global: { headers: { Authorization: `Bearer ${ctx.auth.accessToken}` } },
      },
    );

    const { data, error } = await supabase.from("notes").select();
    if (error) return { content: [{ type: "text", text: error.message }], isError: true };

    return { content: [{ type: "text", text: JSON.stringify(data) }] };
  },
);

Resources

Next Steps