Skip to main content

Pluggable OAuth 2.0 + credentials elicitation library for FastMCP servers

Project description

mcp-authkit

CI Coverage Docs PyPI version Python License: MIT

Pluggable authentication library for FastMCP servers built on FastAPI / Starlette.

Supports two independent auth legs:

  1. Primary leg — every MCP session is gated behind a standard OIDC provider (Keycloak, Okta, Entra ID, Duende, Auth0, …) using JWT bearer tokens. The MCP client (e.g. VS Code Copilot) handles the PKCE flow automatically.
  2. Secondary leg — individual MCP tools can additionally require a third-party OAuth token or a PAT / API key, collected on demand via MCP elicitation.

Installation

pip install mcp-authkit

# Optional Redis storage backend
pip install "mcp-authkit[redis]"

Package layout

mcpauthkit/
├── __init__.py                 # Public exports: OAuthProvider, CredentialsProvider, …
├── auth_middleware.py          # JwtAuthMiddleware (BaseHTTPMiddleware)
├── auth_routes.py              # oauth_meta_router() — well-known + DCR façade
├── jwt_validator.py            # OIDC JWKS-based JWT validation (provider-agnostic)
├── providers/
│   ├── oauth_provider.py       # OAuthProvider — third-party OAuth 2.0 leg
│   ├── credentials_provider.py # CredentialsProvider — PAT / API-key form
│   └── templates/              # Jinja2 HTML templates (no external CDN)
└── store/
    ├── base.py                 # Abstract store interfaces
    ├── memory.py               # In-process store (dev / testing)
    ├── file_store.py           # Fernet-encrypted file store
    ├── redis_store.py          # Async Redis store (requires redis extra)
    ├── encryption.py           # Fernet key derivation helpers
    └── factory.py              # create_stores() — env-driven backend selection

The repository also contains server.py (a complete example server using GitHub OAuth and Confluence credentials) and a docker-compose.yml / keycloak-realm.json for running a local Keycloak instance. See the quickstart repo for a guided walkthrough.


Primary auth leg — OIDC JWT validation

Every request to the MCP server must carry a valid Authorization: Bearer <token> issued by the configured OIDC provider. The middleware performs JWKS discovery automatically and caches keys for 10 minutes.

from mcpauthkit.auth_middleware import JwtAuthMiddleware
from mcpauthkit.auth_routes import oauth_meta_router

ISSUER_URL    = "https://sso.example.com/realms/my-realm"
SERVER_URL    = "https://my-mcp-server.example.com"
CLIENT_ID     = "my-mcp-public-client"   # pre-registered public client

# Publish RFC 8414 / MCP-spec well-known endpoints + DCR façade
app.include_router(oauth_meta_router(
    server_base_url=SERVER_URL,
    issuer_url=ISSUER_URL,
    client_id=CLIENT_ID,
))

# Validate JWT on every request; populate current_user ContextVar
app.add_middleware(
    JwtAuthMiddleware,
    issuer_url=ISSUER_URL,
    current_user=current_user,
    server_base_url=SERVER_URL,
    open_paths=(
        "/.well-known", "/health", "/register",
        github_oauth.callback_path,
        *confluence_creds.open_paths,
    ),
)

oauth_meta_router

Returns a FastAPI APIRouter with:

Route Purpose
GET /.well-known/oauth-protected-resource RFC 9728 resource metadata
GET /.well-known/oauth-protected-resource/{path} Wildcard variant (some clients append the resource path)
GET /.well-known/oauth-authorization-server RFC 8414 authorization server metadata (proxied from the real OIDC provider)
POST /register Dynamic Client Registration façade — always returns the pre-registered public client ID

JwtAuthMiddleware

BaseHTTPMiddleware subclass. Parameters passed via app.add_middleware(...):

Parameter Type Description
issuer_url str OIDC issuer base URL (e.g. Keycloak realm URL)
current_user ContextVar Populated with verified claims on each authenticated request
server_base_url str Used in WWW-Authenticate realm / resource-metadata URIs
open_paths tuple[str, ...] Path prefixes that bypass authentication

Returns 401 with a standards-compliant WWW-Authenticate: Bearer … header when authentication fails, triggering the PKCE flow in the MCP client automatically.

jwt_validator

Provider-agnostic OIDC JWT validation. Supports RS256/384/512, PS256/384/512, ES256/384/512, EdDSA. Discovers jwks_uri automatically via {issuer_url}/.well-known/openid-configuration. OIDC config and JWKS are cached for 10 minutes.


Secondary auth leg — tool-level credential acquisition

Individual tools can be gated behind additional credentials collected on demand via MCP elicitation.

OAuthProvider — third-party OAuth 2.0

Gates a tool behind a full Authorization Code + PKCE flow with a third-party provider. The MCP client opens the provider's login page; the tool call suspends until the callback fires.

from mcpauthkit import OAuthProvider

github_oauth = OAuthProvider.from_standard_oauth2(
    name="github",
    authorization_url="https://github.com/login/oauth/authorize",
    token_url="https://github.com/login/oauth/access_token",
    client_id=os.environ["GITHUB_CLIENT_ID"],
    client_secret=os.environ["GITHUB_CLIENT_SECRET"],
    scope="read:user repo",
    redirect_uri=f"{SERVER_URL}/github/callback",
    user_context=current_user,
)
github_oauth.register(app)   # registers GET /github/callback on the FastAPI app

@mcp.tool(description="List open PRs")
@github_oauth.require_token()
async def list_prs(ctx: Context, repo: str) -> str:
    token = github_oauth.get_token()    # guaranteed non-None inside the decorator
    ...

Key methods:

Method Description
from_standard_oauth2(...) Factory for any standard OAuth 2.0 provider
register(app) Register the callback route on the FastAPI app
require_token(*, fail_fast=False) Decorator — elicits token if not cached, or raises immediately
get_token() Return the cached access token for the current user (or None)
invalidate_token(sub) Evict a user's cached token
.callback_path The redirect URI path (add to open_paths)

CredentialsProvider — PAT / API key form

Serves a self-hosted HTML form where the user enters credentials (PATs, API keys, etc.). Values are stored server-side, keyed by the primary OIDC sub. The form optionally renders a Markdown how-to guide (client-side via marked.js).

from mcpauthkit import CredentialsProvider

confluence_creds = CredentialsProvider(
    name="confluence",
    variables={
        "pat": {
            "label": "Personal Access Token",
            "type": "password",
            "placeholder": "Your Confluence PAT",
        },
    },
    user_context=current_user,
    server_base_url=SERVER_URL,
    doc="docs/confluence_token_how.md",   # optional — rendered above the form
)
confluence_creds.register(app)

@mcp.tool(description="List Confluence pages")
@confluence_creds.require_credentials()
async def list_pages(ctx: Context, space: str) -> str:
    creds = confluence_creds.get_credentials()   # {"pat": "..."}
    ...

Key methods / properties:

Member Description
register(app) Register entry + submit routes on the FastAPI app
require_credentials(*, fail_fast=False) Decorator — elicits credentials if not cached
get_credentials() Return the cached credentials dict for the current user
invalidate_credentials(sub) Evict a user's cached credentials
.open_paths Tuple of paths to add to JwtAuthMiddleware open_paths

MCP mount

The FastMCP sub-app must be mounted at / after all routes are registered. Its internal Starlette router exposes the MCP endpoint at /mcp:

# All routes (include_router, register, @app.get) must come before this line
app.mount("/", app=mcp.streamable_http_app())

The MCP client connects to http://<host>:<port>/mcp.


HTML templates

All browser-facing pages use Jinja2 templates in mcpauthkit/providers/templates/. Every page extends base.html which provides:

  • A blue "MCP Authentication 🔒" top bar
  • A centered card layout
  • Minimal inline CSS (no external CDN dependencies except marked.js on the credentials entry page)

Getting started

A full working example (Docker Compose with Redis + Keycloak, a GitHub OAuth tool, and a Confluence credentials tool) lives in the companion repo:

mcp-authkit-quickstart


Storage backends

Mode Class Notes
memory (default) MemoryTokenStore / MemoryPendingStore In-process. Tokens lost on restart. Suitable for development.
file FileTokenStore / FilePendingStore Fernet-encrypted JSON files. Good for single-instance deployments.
redis RedisTokenStore / RedisPendingStore Async Redis. Requires pip install "mcp-authkit[redis]". Use for multi-replica deployments.

Select a backend via the TOKEN_STORAGE_MODE environment variable (memory / file / redis), or call create_stores() directly.


Dependencies

Package Purpose
fastapi HTTP framework
mcp>=1.6 MCP server SDK (FastMCP)
starlette ASGI primitives, BaseHTTPMiddleware
python-jose[cryptography] JWT decoding and JWKS validation
httpx Async HTTP client (token exchange, OIDC discovery)
jinja2 HTML template rendering
cryptography Fernet encryption for file and Redis stores

Contributing

# Install dev dependencies
uv sync --group dev

# Lint + type-check
uv run ruff check mcpauthkit/ tests/
uv run mypy mcpauthkit/

# Tests with coverage
uv run pytest --cov=mcpauthkit --cov-report=term-missing -q

See CHANGELOG.md for release history.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_authkit-0.1.1.tar.gz (36.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_authkit-0.1.1-py3-none-any.whl (43.1 kB view details)

Uploaded Python 3

File details

Details for the file mcp_authkit-0.1.1.tar.gz.

File metadata

  • Download URL: mcp_authkit-0.1.1.tar.gz
  • Upload date:
  • Size: 36.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mcp_authkit-0.1.1.tar.gz
Algorithm Hash digest
SHA256 579888b560e3fe4376324b50211f13a92b64d95cdfbc6d119b2196cc020873c4
MD5 e01e10bb76cee8cf87f1859f7c13bb21
BLAKE2b-256 88acbae3a07c6a16b3952d07f82d34bb3d56443553daa50f316d4c413de64b6f

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_authkit-0.1.1.tar.gz:

Publisher: release.yml on masterela/mcp-authkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mcp_authkit-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: mcp_authkit-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 43.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mcp_authkit-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2e5fb5c8ec3d4dc29b1f0228a87bca707c96364fb968f08e26d419ab6d7219e1
MD5 7b6748d8909ad98e42e82bb0f59e00bd
BLAKE2b-256 c273046d12fea460b5c8b1e6f39c13499529616258a9fae1329532e21432b24c

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_authkit-0.1.1-py3-none-any.whl:

Publisher: release.yml on masterela/mcp-authkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page