🚀 A production-ready Model Context Protocol (MCP) server with OAuth 2.1 authentication using FastMCP's built-in OIDC Proxy and Auth0 as the identity provider.
- 🔒 FastMCP OIDC Proxy: Built-in OAuth authentication via FastMCP's OIDCProxy
- 🌐 Auth0 Integration: Pre-configured Auth0 provider for seamless authentication
- 🔄 Dynamic Client Registration: Proxies DCR for Auth0 to work with Claude.ai
- 🛠️ Demo Tools: Calculator and notes management tools for testing
- 📡 HTTP/SSE Transport: Built-in server with automatic routing and auth
FastMCP's OIDC Proxy acts as a bridge between Claude.ai and Auth0:
┌─────────────┐
│ Claude.ai │
│ Client │
└──────┬──────┘
│
│ 1. DCR Request
↓
┌──────────────────────────────────┐
│ FastMCP Server (OIDC Proxy) │
│ - Proxies DCR │
│ - Handles OAuth flow │
│ - Validates tokens │
│ - Protects MCP tools │
└──────────┬───────────────────────┘
│
│ 2. OAuth with Auth0
↓
┌──────────────────────────────────┐
│ Auth0 (IdP) │
│ - User authentication │
│ - Token issuance │
│ - OIDC discovery │
└──────────────────────────────────┘
- 🐍 Python 3.10 or higher
- 🔑 Auth0 account (free tier works)
- ⚙️ Auth0 application configured with redirect URI
git clone <your-repo-url>
cd mcp-auth-oidcOpip install -r requirements.txtOr with a virtual environment:
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt- Log in to your Auth0 Dashboard
- Navigate to Applications → Create Application
- Choose Regular Web Application
- Note your Domain, Client ID, and Client Secret
In your Auth0 application settings, add the callback URL:
For local development:
http://localhost:8000/auth/callback
For production:
https://your-domain.com/auth/callback
- Navigate to APIs → Create API
- Set an identifier (e.g.,
https://dev-zilqiezmsk6ylig2.us.auth0.com/api/v2/) - Add the following scopes:
- 📖
read:notes- Read notes - ✍️
write:notes- Create, update, delete notes - 🧮
use:calculator- Use calculator tools
- 📖
Create a .env file in the project root:
# Auth0 Configuration
AUTH0_DOMAIN=your-domain.us.auth0.com
AUTH0_CLIENT_ID=your_client_id
AUTH0_CLIENT_SECRET=your_client_secret
AUTH0_AUDIENCE=https://your-domain.us.auth0.com/api/v2/
# Server Configuration
RESOURCE_ID=http://localhost:8000
SERVER_HOST=0.0.0.0
SERVER_PORT=8000✨ Demo credentials (pre-configured):
AUTH0_DOMAIN=dev-zilqiezmsk6ylig2.us.auth0.com
AUTH0_CLIENT_ID=GsJfBMVGn5cDgWQwTiq91SIQBYxQccJA
AUTH0_CLIENT_SECRET=Qst5RVD9Vt79F5NgM_s6ymZSvMYemKrFMykWrDOtextPC2nBeK593yBvpJBafIDl
AUTH0_AUDIENCE=https://dev-zilqiezmsk6ylig2.us.auth0.com/api/v2/
RESOURCE_ID=http://localhost:8000
SERVER_HOST=0.0.0.0
SERVER_PORT=8000Start the server:
python run.pyYou should see:
============================================================
MCP Auth Demo Server (FastMCP + OIDC Proxy)
============================================================
Base URL: http://localhost:8000
Auth0 Domain: dev-zilqiezmsk6ylig2.us.auth0.com
Server: http://0.0.0.0:8000
Auth Callback: http://localhost:8000/auth/callback
============================================================
Starting server with FastMCP's built-in HTTP/SSE transport...
The server will start on http://localhost:8000 with the following endpoints:
- 📡 MCP/SSE:
/mcp/sseor/sse(authenticated) - 🔄 Auth Callback:
/auth/callback(OAuth redirect) - ✅ Consent:
/auth/consent(user authorization)
All calculator tools require the use:calculator scope:
- add_numbers(a, b) ➕ Add two numbers
- subtract_numbers(a, b) ➖ Subtract b from a
- multiply_numbers(a, b) ✖️ Multiply two numbers
- divide_numbers(a, b) ➗ Divide a by b
Notes tools require either read:notes or write:notes scope:
- create_note(title, content) ✍️ Create a new note (requires
write:notes) - read_note(note_id) 📖 Read a specific note (requires
read:notes) - list_notes() 📋 List all notes (requires
read:notes) - update_note(note_id, title, content) 📝 Update a note (requires
write:notes) - delete_note(note_id) 🗑️ Delete a note (requires
write:notes)
- Open Claude.ai
- Go to Settings → Integrations → Model Context Protocol
- Click "Add Remote MCP Server"
- Enter your server URL:
http://localhost:8000 - Claude.ai will:
- Perform Dynamic Client Registration (DCR) via the proxy
- Redirect you to Auth0 for authentication
- Request consent for the required scopes
- Connect to your MCP server with a valid access token
If Claude.ai needs to access your local server over the internet:
- Install ngrok
- Start ngrok:
ngrok http 8000
- Update your
.envfile with the ngrok URL:RESOURCE_ID=https://your-ngrok-id.ngrok.io
- Update Auth0 redirect URI to:
https://your-ngrok-id.ngrok.io/auth/callback - Restart the server
- Use the ngrok URL in Claude.ai:
https://your-ngrok-id.ngrok.io
For the Claude Desktop application, add to your MCP configuration:
Location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Configuration:
{
"mcpServers": {
"auth-demo": {
"url": "http://localhost:8000",
"transport": "sse"
}
}
}The MCP Inspector is an interactive developer tool for testing and debugging your OAuth-enabled MCP server.
-
Start your server:
python run.py
-
Launch MCP Inspector (in a new terminal):
npx @modelcontextprotocol/inspector
This will open the Inspector at
http://localhost:6274 -
Configure connection in the Inspector UI:
- Transport: Select "HTTP with SSE"
- Server URL:
http://localhost:8000/mcp - OAuth Scopes:
read:notes write:notes use:calculator
-
Update Auth0 redirect URI:
- Add
http://localhost:6274/oauth/callbackto your Auth0 application's Allowed Callback URLs
- Add
-
Connect and test:
- Click "Connect" to initiate OAuth flow
- Authenticate with Auth0
- Test calculator and notes tools interactively
- ✅ Test OAuth authentication flow step-by-step
- ✅ Call MCP tools with custom parameters
- ✅ View detailed request/response data
- ✅ Debug authentication and authorization issues
- ✅ Verify scope-based access control
- ✅ Test error handling and edge cases
For comprehensive testing instructions, including:
- Step-by-step OAuth flow testing
- Example tool calls with expected results
- Common issues and solutions
- Advanced testing scenarios
See the complete MCP Inspector Testing Guide.
- Client Registration 📝 Claude.ai sends a DCR request to FastMCP
- Proxy DCR 🔄 FastMCP proxies the registration (Auth0 doesn't support DCR natively)
- Authorization 🔑 User is redirected to Auth0 for authentication
- Consent ✅ User authorizes the scopes requested by Claude.ai
- Token Exchange 🎫 FastMCP exchanges the authorization code for tokens
- Token Validation ✔️ FastMCP validates tokens using Auth0's OIDC configuration
- Tool Access 🛠️ Authenticated requests can access protected MCP tools
- 🚀 No Manual Registration: Clients register dynamically
- ✅ Automatic Token Validation: JWT verification via Auth0's JWKS
- 🔒 Scope-Based Authorization: Tools protected by OAuth scopes
- 👤 Consent Flow: Users see and approve what clients can access
- 🌐 Works with Any OIDC Provider: Not just Auth0
mcp-auth-oidcO/
├── src/
│ ├── __init__.py # Package initialization
│ ├── auth_config.py # Auth0Provider configuration
│ ├── server.py # FastMCP server with demo tools
│ ├── app.py # FastMCP app export
│ └── main.py # Main entry point
├── .env # Environment variables (not in git)
├── .gitignore # Git ignore rules
├── pyproject.toml # Python project configuration
├── requirements.txt # Python dependencies
├── run.py # Convenience script
├── README.md # This file
├── QUICKSTART.md # Quick start guide
└── TESTING.md # Testing guide
The server is built with three simple files:
auth_config.py🔧 Configures Auth0Providerserver.py🛠️ Defines MCP tools with@mcp.tool()decoratormain.py🚀 Starts the FastMCP HTTP server
That's it! FastMCP handles all the routing, authentication, and SSE automatically.
To add new tools:
- Open
src/server.py - Add a new function with the
@mcp.tool()decorator:
@mcp.tool()
def my_new_tool(param: str) -> str:
"""
Description of what the tool does.
Args:
param: Description
Returns:
Description of return value
Requires:
Scope: my:scope
"""
return f"Result: {param}"- Add the scope to
src/auth_config.py:
SUPPORTED_SCOPES = [
"read:notes",
"write:notes",
"use:calculator",
"my:scope", # Add your new scope
]- Configure the scope in Auth0
See TESTING.md for comprehensive testing instructions.
Start the server and verify it's running:
# Start server
python run.py
# In another terminal, check health (if you add a health endpoint)
curl http://localhost:8000/- ✅ Check Python version:
python --version(need 3.10+) - ✅ Verify all environment variables are set in
.env - ✅ Ensure dependencies are installed:
pip install -r requirements.txt
- ✅ Verify Auth0 credentials in
.envare correct - ✅ Check that redirect URI in Auth0 matches
{BASE_URL}/auth/callback - ✅ Ensure scopes are configured in Auth0 API
- ✅ Verify the server is running and accessible
- ✅ Check that the base URL is correct
- ✅ For local testing, ensure Claude.ai can reach localhost (try ngrok)
- ✅ Review server logs for authentication errors
- 🔐 HTTPS Only: Always use HTTPS in production
- 🔑 Environment Variables: Never commit
.envto version control - 🗝️ Secure Secrets: Use a secrets manager (AWS Secrets Manager, Azure Key Vault, etc.)
- 💾 Token Storage: Configure encrypted storage backend (Redis with encryption)
- 🎫 JWT Signing Key: Set explicit
jwt_signing_keyin production - ⏱️ Rate Limiting: Add rate limiting middleware
- 📝 Logging: Implement comprehensive audit logs
- 🌐 CORS: Configure specific origins (not
*)
from fastmcp.server.auth.providers.auth0 import Auth0Provider
from key_value.aio.stores.redis import RedisStore
from key_value.aio.wrappers.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
import os
auth = Auth0Provider(
config_url=f"https://{os.environ['AUTH0_DOMAIN']}/.well-known/openid-configuration",
client_id=os.environ['AUTH0_CLIENT_ID'],
client_secret=os.environ['AUTH0_CLIENT_SECRET'],
audience=os.environ['AUTH0_AUDIENCE'],
base_url=os.environ['BASE_URL'],
required_scopes=SUPPORTED_SCOPES,
jwt_signing_key=os.environ['JWT_SIGNING_KEY'],
client_storage=FernetEncryptionWrapper(
key_value=RedisStore(host="redis.example.com", port=6379),
fernet=Fernet(os.environ['STORAGE_ENCRYPTION_KEY'])
)
)- 🚀 FastMCP Documentation
- 🔐 FastMCP OIDC Proxy Guide
- 📖 Model Context Protocol Specification
- 🔒 OAuth 2.1 Draft
- 🌐 Auth0 Documentation
MIT License - feel free to use this as a template for your own MCP servers.
For issues and questions:
- 🚀 FastMCP: GitHub Issues | Discord
- 🌐 Auth0: Community Forum
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.