OAuth for your MCP server. Ship it today, not next quarter.

Drop-in OAuth 2.1 authorization server and SDK for MCP servers. Implements Dynamic Client Registration (RFC 7591) out of the box.

import express from "express";
import { mcpAuth } from "getmcpauth";

const app = express();

app.use(
  "/mcp",
  mcpAuth({ registrationSecret: process.env.MCPAUTH_SECRET })
);

// Unauthenticated or invalid requests never reach this handler.
app.post("/mcp", handleMcpRequest);
56,000+
MCP servers indexed on LobeHub
8.5%
of MCP servers use OAuth
~40%
of public MCP servers have no auth at all
53%
use bare static API keys or PATs

MCP auth is broken today

The MCP spec expects clients to register themselves dynamically via Dynamic Client Registration (RFC 7591) — a client shows up, no human has pre-configured it in a dashboard, and it registers itself on the spot. Almost no OAuth provider supports that. General-purpose identity platforms are built around a fixed, pre-registered set of apps, not clients that appear at runtime.

So builders end up with three bad options: ship the MCP server with no auth at all, hand-roll a static API key check that has no scopes, no expiry, and no revocation, or bolt on a full enterprise CIAM platform meant for SSO and SCIM provisioning when all they actually needed was OAuth for an MCP client.

How it works

  1. 1

    npm install getmcpauth

    Add the SDK to your MCP server project. Peer dependencies are @modelcontextprotocol/sdk and express.

  2. 2

    Wrap your server with mcpAuth()

    One middleware call protects your MCP route. It wraps the official SDK's requireBearerAuth middleware, so bad or missing tokens get a spec-correct 401 before they ever reach your handlers.

  3. 3

    Clients auto-discover the rest

    GET /.well-known/oauth-authorization-server lists every endpoint mcpauth exposes — registration, authorize, token, revoke, introspect — so an MCP client only needs your base URL.

POST/api/oauth/registerDynamic Client Registration (RFC 7591)
GET/oauth/authorizeBrowser-facing OAuth 2.1 authorize + consent screen
POST/api/oauth/tokenAuthorization-code and refresh-token grants
POST/api/oauth/revokeToken revocation (RFC 7009)
POST/api/oauth/introspectToken introspection (RFC 7662)
POST/api/oauth/token/exchangeServer-to-server token minting
GET/.well-known/oauth-authorization-serverDiscovery document (RFC 8414)

Before and after

Most MCP servers today either skip auth entirely or check a static key with a middleware like this:

// Hand-rolled API key check
app.use("/mcp", (req, res, next) => {
  const key = req.headers["x-api-key"];

  if (!key || key !== process.env.MCP_API_KEY) {
    return res.status(401).json({ error: "unauthorized" });
  }

  // No scopes, no per-user identity, no token
  // expiry, no revocation, no discovery for
  // clients that expect real OAuth.
  next();
});

With mcpauth, the same protection — plus real per-user identity, scopes, token expiry, and revocation — is three lines:

// mcpauth
app.use(
  "/mcp",
  mcpAuth({ registrationSecret: process.env.MCPAUTH_SECRET })
);

Test it live, before you install anything

This isn't a mock. It's a real MCP server, in production, protected by mcpauth's own published SDK — the same package you'd install. Point the official MCP Inspector at it:

npx @modelcontextprotocol/inspector \
  https://getmcpauth.dev/api/demo/mcp --transport http

Inspector hits the server, gets a 401 with a WWW-Authenticate header pointing at mcpauth, registers itself automatically (Dynamic Client Registration — no client ID to configure), and walks you through the real GitHub-login consent screen in your browser. Approve it, and Inspector can call the two tools this demo server exposes — a live, end-to-end proof of the whole flow.

Who it's for

New MCP server

Building an MCP server from scratch. Install the SDK, wrap it with mcpAuth(), and users sign in through GitHub and a consent screen — no auth code to write.

Existing product

Adding MCP to a product that already has its own users. Your backend calls /api/oauth/token/exchange directly to mint a token for an identity it already authenticated, skipping the consent screen entirely. Read the token-minting docs →

Self-hosting

Prefer to run the authorization server yourself rather than use the hosted dashboard. The SDK and endpoints are the same either way.

Already have users? Skip the consent screen entirely

If you're bolting an MCP server onto a product that already has its own login, don't make your users authenticate twice. Your backend, which already knows who's signed in, mints the token directly:

  1. Your user logs into your product, as usual — not mcpauth.
  2. Your backend calls mintToken() (or /api/oauth/token/exchange directly), server-to-server, vouching for that user.
  3. The resulting token is handed straight to the MCP client — no GitHub login, no browser redirect, no separate consent screen.
import { mintToken } from "getmcpauth";

// After your own login/session check —
// mcpauth never authenticates this user itself.
const { accessToken } = await mintToken({
  registrationSecret: process.env.MCPAUTH_SECRET,
  clientId,
  subject: currentUser.id,
  scopes: ["read:files"],
});

// Hand accessToken to the MCP client. No
// GitHub login, no consent screen — your
// backend already vouched for this user.

Full server-to-server token minting docs →

Pricing

Free

Free

For building and testing an MCP server.

  • 1 project
  • Up to 1,000 monthly active tokens
  • Full OAuth 2.1 + Dynamic Client Registration
  • GitHub-authenticated dashboard
  • Community support

Pro

$29/mo

For MCP servers in real production use.

  • Unlimited projects
  • 10,000 monthly active tokens included
  • $5 per additional 1,000 monthly active tokens
  • Server-to-server token minting
  • Priority support

See full pricing details →

Add OAuth to your MCP server this afternoon