Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/punkpeye/fastmcp/llms.txt

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

FastMCP provides flexible authentication options to secure your MCP server, from simple API keys to full OAuth 2.1 flows with pre-configured providers.

OAuth with Pre-configured Providers

The simplest way to add OAuth is using the auth option with a pre-configured provider:
import { FastMCP, getAuthSession, GoogleProvider, requireAuth } from "fastmcp";

const server = new FastMCP({
  auth: new GoogleProvider({
    baseUrl: "https://your-server.com",
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
  }),
  name: "My Server",
  version: "1.0.0",
});

server.addTool({
  canAccess: requireAuth,
  description: "Get user profile",
  execute: async (_args, { session }) => {
    const { accessToken } = getAuthSession(session);
    const response = await fetch(
      "https://www.googleapis.com/oauth2/v2/userinfo",
      {
        headers: { Authorization: `Bearer ${accessToken}` },
      },
    );
    return JSON.stringify(await response.json());
  },
  name: "get-profile",
});

Available Providers

import { GoogleProvider } from "fastmcp";

const server = new FastMCP({
  auth: new GoogleProvider({
    baseUrl: "https://your-server.com",
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    scopes: ["openid", "profile", "email"],
  }),
  name: "My Server",
  version: "1.0.0",
});

Tool Authorization

Control which tools are available to authenticated users using the canAccess property with built-in helper functions:

Require Authentication

import { requireAuth } from "fastmcp";

server.addTool({
  canAccess: requireAuth, // Only authenticated users
  name: "user-tool",
  execute: async () => "Authenticated!",
});

Require Specific Scopes

import { requireScopes } from "fastmcp";

server.addTool({
  canAccess: requireScopes("read:user", "write:data"),
  name: "scoped-tool",
  execute: async () => "Access granted!",
});

Require Specific Role

import { requireRole } from "fastmcp";

server.addTool({
  canAccess: requireRole("admin"),
  name: "admin-tool",
  execute: async () => "Welcome, admin!",
});

Combine Requirements

import { requireAll, requireAuth, requireRole } from "fastmcp";

server.addTool({
  canAccess: requireAll(requireAuth, requireRole("admin")),
  name: "admin-only",
  execute: async () => "Full access!",
});

Access Session Data

Use getAuthSession for type-safe access to the OAuth session in your tool execute functions:
import { getAuthSession, GoogleSession } from "fastmcp";

server.addTool({
  canAccess: requireAuth,
  name: "get-profile",
  execute: async (_args, { session }) => {
    // Type-safe destructuring (throws if not authenticated)
    const { accessToken } = getAuthSession(session);

    // Or with provider-specific typing:
    // const { accessToken } = getAuthSession<GoogleSession>(session);

    const response = await fetch("https://api.example.com/user", {
      headers: { Authorization: `Bearer ${accessToken}` },
    });
    return JSON.stringify(await response.json());
  },
});
You can also access session.accessToken directly, but you must handle the case where session is undefined. The getAuthSession helper throws a clear error if the session is not authenticated, making it safer when used with canAccess: requireAuth.

Custom Authentication

For non-OAuth scenarios like API keys or custom tokens, use the authenticate option:
const server = new FastMCP({
  name: "My Server",
  version: "1.0.0",
  authenticate: (request) => {
    const apiKey = request.headers["x-api-key"];

    if (apiKey !== "123") {
      throw new Response(null, {
        status: 401,
        statusText: "Unauthorized",
      });
    }

    return { id: 1, role: "user" };
  },
});

server.addTool({
  name: "sayHello",
  execute: async (args, { session }) => {
    return `Hello, ${session.id}!`;
  },
});

OAuth Discovery Endpoints

FastMCP supports OAuth discovery endpoints for direct integration with OAuth providers, supporting both MCP Specification 2025-03-26 and MCP Specification 2025-06-18:
import { FastMCP, DiscoveryDocumentCache } from "fastmcp";
import { buildGetJwks } from "get-jwks";
import fastJwt from "fast-jwt";

const discoveryCache = new DiscoveryDocumentCache({
  ttl: 3600000, // Cache for 1 hour
});

const server = new FastMCP({
  name: "My Server",
  version: "1.0.0",
  oauth: {
    enabled: true,
    authorizationServer: {
      issuer: "https://auth.example.com",
      authorizationEndpoint: "https://auth.example.com/oauth/authorize",
      tokenEndpoint: "https://auth.example.com/oauth/token",
      jwksUri: "https://auth.example.com/.well-known/jwks.json",
      responseTypesSupported: ["code"],
    },
    protectedResource: {
      resource: "mcp://my-server",
      authorizationServers: ["https://auth.example.com"],
    },
  },
  authenticate: async (request) => {
    const authHeader = request.headers.authorization;

    if (!authHeader?.startsWith("Bearer ")) {
      throw new Response(null, {
        status: 401,
        statusText: "Missing or invalid authorization header",
      });
    }

    const token = authHeader.slice(7);

    try {
      const config = (await discoveryCache.get(
        "https://auth.example.com/.well-known/openid-configuration"
      )) as {
        jwks_uri: string;
        issuer: string;
      };

      const getJwks = buildGetJwks({
        jwksUrl: config.jwks_uri,
        cache: true,
        rateLimit: true,
      });

      const verify = fastJwt.createVerifier({
        key: async (token) => {
          const { header } = fastJwt.decode(token, { complete: true });
          const jwk = await getJwks.getJwk({
            kid: header.kid,
            alg: header.alg,
          });
          return jwk;
        },
        algorithms: ["RS256", "ES256"],
        issuer: config.issuer,
        audience: "mcp://my-server",
      });

      const payload = await verify(token);

      return {
        userId: payload.sub,
        scope: payload.scope,
        email: payload.email,
      };
    } catch (error) {
      throw new Response(null, {
        status: 401,
        statusText: "Invalid OAuth token",
      });
    }
  },
});
This configuration automatically exposes OAuth discovery endpoints:
  • /.well-known/oauth-authorization-server - Authorization server metadata (RFC 8414)
  • /.well-known/oauth-protected-resource - Protected resource metadata (RFC 9728)
  • /.well-known/oauth-protected-resource<endpoint> - Protected resource metadata at sub-path

Helper Functions Reference

FunctionDescriptionExample
requireAuthRequires any authenticated usercanAccess: requireAuth
requireScopes(...scopes)Requires specific OAuth scopescanAccess: requireScopes("read:user")
requireRole(...roles)Requires specific rolecanAccess: requireRole("admin")
requireAll(...checks)Combines checks with AND logiccanAccess: requireAll(requireAuth, requireRole("admin"))
requireAny(...checks)Combines checks with OR logiccanAccess: requireAny(requireRole("admin"), requireRole("moderator"))
getAuthSession(session)Type-safe session extractionconst { accessToken } = getAuthSession(session)

Next Steps

OAuth Proxy

Learn about the built-in OAuth Proxy with DCR, PKCE, and token swap

Custom Routes

Add authenticated custom HTTP routes to your server