FastAuthMCP
Identity infrastructure for AI agents.
When an AI agent calls your MCP tools — who is the human behind the request? What are they allowed to do? How do you forward their credentials to downstream APIs?
FastAuthMCP answers these questions. It's an identity runtime that authenticates humans, propagates their identity into tool functions, and forwards user-scoped tokens to enterprise APIs. One config file. Zero changes to your tools.
The Problem
The Model Context Protocol defines how AI agents interact with tools. It says nothing about identity.
Every team building production MCP servers re-invents authentication, authorization, and token propagation from scratch. There is no standard. No shared infrastructure. No reference implementation.
Your MCP server works on your laptop. Then your security team asks:
- Who is calling these tools?
- What are they allowed to do?
- Where's the audit trail?
- How do downstream APIs get a user-scoped token?
You start building OAuth. Then token refresh. Then per-user propagation. Then authorization. Weeks pass.
The Solution
FastAuthMCP sits between MCP clients and your tools:
┌─────────────────────────────────────────────┐
│ MCP Client (Claude, Gemini, Cursor, etc.) │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ FastAuthMCP │
│ │
│ Authenticate → Authorize → Propagate │
│ │
│ identity() @require_roles("admin") │
│ access_token() sessions, observability │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Your Tools → Downstream APIs (Stripe, │
│ GitHub, SAP, internal services...) │
└─────────────────────────────────────────────┘
- Authenticates the human — OAuth2, PKCE, or token exchange
- Propagates identity —
identity()returns who is calling, in every tool - Forwards user-scoped tokens —
access_token()for downstream API calls - Enforces authorization —
@require_roles("admin")per tool - Works identically — local (stdio + browser) and cloud (HTTP + token exchange)
Quick Start
1. Install
pip install fastauthmcp
2. Change one import
# Before
from fastmcp import FastMCP
# After
from fastauthmcp import FastMCP
Without a config file, this behaves identically to vanilla FastMCP. No overhead.
3. Add identity
Create fastauthmcp.yaml:
auth:
provider: oidc
issuer: https://your-idp.example.com
client_id: your-app
scopes: [openid, profile, email]
Use identity in your tools:
from fastauthmcp import FastMCP, identity, access_token
mcp = FastMCP("my-server", config="fastauthmcp.yaml")
@mcp.tool()
def whoami() -> dict:
user = identity()
return {"email": user.email, "roles": sorted(user.roles)}
@mcp.tool()
def get_orders() -> list:
token = access_token() # User-scoped, auto-refreshed
return httpx.get(
"https://api.internal.com/orders",
headers={"Authorization": f"Bearer {token}"},
).json()
That's it. Every tool call is now authenticated. The token is always valid — auto-refreshed by the middleware before your code runs.
How It Works
Local: Browser login (Claude Desktop, Cursor)
auth:
provider: oidc
issuer: https://your-idp.example.com
client_id: my-app
grant_type: authorization_code # default
First tool call → browser opens → user logs in → token stored in Keychain → all subsequent calls authenticated. No user interaction after the first login.
Cloud: Token exchange (Gemini, remote agents)
auth:
provider: oidc
issuer: https://your-idp.example.com
client_id: cloud-mcp-server
client_secret: ${FASTAUTHMCP_AUTH_CLIENT_SECRET}
grant_type: token_exchange
upstream_token_header: x-user-token
token_exchange_audience: https://api.internal.com
The calling platform passes a user token → FastAuthMCP exchanges it at the IDP → your tool gets a downstream-scoped token via access_token(). No browser. Per-user scoping.
Machine-to-Machine (headless services)
auth:
provider: oidc
issuer: https://your-idp.example.com
client_id: my-service
client_secret: ${FASTAUTHMCP_AUTH_CLIENT_SECRET}
grant_type: client_credentials
Identity Primitives
from fastauthmcp import identity, access_token, require_roles
@mcp.tool()
def whoami() -> dict:
user = identity() # Who is calling this tool?
return {"email": user.email, "subject": user.subject}
@mcp.tool()
def call_api() -> dict:
token = access_token() # Forward to downstream APIs
return httpx.get(url, headers={"Authorization": f"Bearer {token}"}).json()
@mcp.tool()
@require_roles("admin") # Per-tool authorization
def admin_action() -> str:
return "access granted"
Supported Platforms
MCP Clients
| Platform | Transport | Guide |
|---|---|---|
| Claude Desktop | stdio | Guide |
| Google Gemini | SSE/HTTP | Guide |
| Cursor IDE | SSE | Guide |
| Custom agents | Any | Guide |
Identity Providers
| Provider | Auth Code | Client Credentials | Token Exchange | Guide |
|---|---|---|---|---|
| Zitadel | ✓ | ✓ | ✓ (RFC 8693) | Guide |
| Keycloak | ✓ | ✓ | ✓ | Guide |
| Auth0 | ✓ | ✓ | via Actions | Guide |
| Azure Entra ID | ✓ | ✓ | ✓ (OBO) | Guide |
| Okta | ✓ | ✓ | ✓ | Guide |
| Google Cloud | ✓ | ✓ | ✓ (STS) | Guide |
| Any OIDC provider | ✓ | ✓ | ✓ | — |
Compatibility Lab
FastAuthMCP includes an automated interoperability suite — 34 scenarios validating every provider × flow × security check:
Authentication ──────────────
✓ ZITADEL Auth Code + PKCE → identity propagation
✓ Keycloak Client Credentials → service identity
✓ Auth0, Azure, Okta — same
Authorization ─────────────
✓ Admin role → tool allowed
✓ Viewer role → tool rejected
Security ────────────────
✓ Expired token → rejected
✓ Wrong issuer → rejected
✓ Wrong audience → rejected
Result: 34 passed, 0 failed (300ms)
./lab.sh # Run all scenarios
make lab-ui # Interactive TUI with LLM + MCP tools
Why FastAuthMCP?
| FastAuthMCP | DIY | API Gateway | |
|---|---|---|---|
| Setup | 5 minutes | Weeks | Hours + infra |
| MCP-aware | ✓ | ✗ | ✗ |
| Identity in tools | identity() |
Manual | Not possible |
| Token forwarding | access_token() |
Build it | Depends |
| Per-tool auth | Decorators | Build it | Route-level |
| Lock-in | None (remove import) | — | Vendor |
| Infrastructure | None (in-process) | None | Separate service |
Architecture
FastAuthMCP wraps FastMCP via composition. Your tools, prompts, and resources stay unchanged. Remove FastAuthMCP and your app still works.
Request → Observability → Session → Authentication → Authorization → Your Tool
Each layer activates only when its section is present in fastauthmcp.yaml. Omit a section to disable it entirely.
Configuration
All configuration lives in fastauthmcp.yaml. Every section is optional.
auth:
provider: oidc
issuer: https://idp.example.com
client_id: your-client-id
grant_type: authorization_code
scopes: [openid, profile, email]
callback_port: 9876
observability:
enabled: true
exporter: otlp
log_format: json
sessions:
ttl: 3600
Full reference: docs/GUIDE.md
CLI
fastauthmcp login # Browser login, store token
fastauthmcp whoami # Show current identity
fastauthmcp doctor # Check config + IDP connectivity
fastauthmcp run # Start the server
Testing
Test without a live IDP:
from fastauthmcp.testing import FastAuthMCPTestClient
client = FastAuthMCPTestClient(app=mcp, email="admin@co.com", roles=["admin"])
result = await client.call_tool("whoami")
assert result["email"] == "admin@co.com"
Documentation
| Resource | Description |
|---|---|
| Full Guide | Complete integration & development reference |
| Integration Guides | Per-platform and per-IDP setup |
| Examples | Working code samples |
| Compatibility Lab | Automated interoperability suite |
Installation Extras
pip install fastauthmcp # Core (auth, identity, CLI)
pip install fastauthmcp[observability] # + OpenTelemetry + Prometheus
pip install fastauthmcp[soap] # + SOAP/XML downstream support
pip install fastauthmcp[keyring] # + Platform-native token storage
pip install fastauthmcp[dev] # + Testing tools
Contributing
See CONTRIBUTING.md for guidelines.
git clone https://github.com/enzomar/fastauthmcp.git
cd fastauthmcp
pip install -e ".[dev]"
pytest # 359 tests
python -m fastauthmcp.lab run # 34 compatibility scenarios
make lab-ui # Interactive TUI
Support
If FastAuthMCP is useful to you:
License
Apache 2.0 — see LICENSE.
Metadata
Release files for fastauthmcp 0.1.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fastauthmcp-0.1.5.tar.gz | 1.3 MB | With provenance | Details for fastauthmcp-0.1.5.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fastauthmcp-0.1.5-py3-none-any.whl | Python 3 | none | any | Details for fastauthmcp-0.1.5-py3-none-any.whl |
Total release size: 1.4 MB
Release files / fastauthmcp-0.1.5.tar.gz
| Download URL | fastauthmcp-0.1.5.tar.gz |
|---|---|
| Size | 1.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
79bf8572f6debdf6aaeb4d191695dea6391959036a0c656a666cd3322944c1f6
|
|
BLAKE2b-256 checksum How to use checksums |
580f181c69da0a09c8facd181ff437b0b086bf4683453fa78c37cb9f7c010a20
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 18, 2026.
Transparency logRelease files / fastauthmcp-0.1.5-py3-none-any.whl
| Download URL | fastauthmcp-0.1.5-py3-none-any.whl |
|---|---|
| Size | 127.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0b7cafb8542901ad68c012195a4d1fe42b9f02ff9043336fef15238510baba78
|
|
BLAKE2b-256 checksum How to use checksums |
f5ef0a2c4ee890de20c90df50a950a78b83bbbe574fef83b1d0724e60dbfd56b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 18, 2026.
Transparency logSecurity for fastauthmcp 0.1.5
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for fastauthmcp-0.1.5.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 18, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.