OAuth Support for MCP Servers
October 15, 2025 ยท View on GitHub
MCP CLI now supports OAuth 2.0 authentication for HTTP-based MCP servers like Notion.
Overview
The OAuth implementation provides:
- Authorization Code Flow with PKCE support
- Automatic token management (storage, refresh, expiration)
- Browser-based authentication with local callback server
- Secure token storage in
~/.mcp_cli/tokens/
Configuration
Add OAuth configuration to your server in server_config.json:
{
"mcpServers": {
"notion": {
"url": "https://mcp.notion.com/mcp",
"oauth": {
"authorization_url": "https://api.notion.com/v1/oauth/authorize",
"token_url": "https://api.notion.com/v1/oauth/token",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"scopes": [],
"redirect_uri": "http://localhost:8080/callback",
"use_pkce": false
}
}
}
}
Configuration Fields
| Field | Required | Description |
|---|---|---|
authorization_url | Yes | OAuth authorization endpoint |
token_url | Yes | OAuth token exchange endpoint |
client_id | Yes | OAuth client ID from the provider |
client_secret | No | OAuth client secret (not required for public clients) |
scopes | No | List of OAuth scopes to request |
redirect_uri | No | Callback URL (default: http://localhost:8080/callback) |
use_pkce | No | Enable PKCE for additional security (default: true) |
extra_auth_params | No | Additional parameters for authorization request |
Setting Up Notion OAuth
-
Create a Notion Integration:
- Go to https://www.notion.so/my-integrations
- Click "New integration"
- Set the integration type to "Public"
- Note your Client ID and Client Secret
-
Configure Redirect URI:
- In your Notion integration settings
- Add redirect URI:
http://localhost:8080/callback
-
Update Configuration:
{ "mcpServers": { "notion": { "url": "https://mcp.notion.com/mcp", "oauth": { "authorization_url": "https://api.notion.com/v1/oauth/authorize", "token_url": "https://api.notion.com/v1/oauth/token", "client_id": "YOUR_NOTION_CLIENT_ID", "client_secret": "YOUR_NOTION_CLIENT_SECRET", "redirect_uri": "http://localhost:8080/callback" } } } }
Usage
When you connect to a server with OAuth configured:
-
First Time: The CLI will open your browser for authorization
mcp-cli --server notion ๐ Authentication required for notion ============================================================ Opening browser for authorization... -
Authorize in Browser: Grant permissions to the integration
-
Automatic Token Management: Tokens are stored securely and refreshed automatically
-
Subsequent Uses: Already authenticated - no browser needed
Token Management
Secure Token Storage
MCP CLI uses secure, OS-native token storage to protect your OAuth credentials. Tokens are never stored in plain text files.
Available Storage Backends
-
macOS Keychain (default on macOS)
- Leverages macOS Keychain for secure credential storage
- Integrates with system security features
- Requires
keyringlibrary
-
Windows Credential Manager (default on Windows)
- Uses Windows DPAPI for encrypted storage
- Integrates with Windows security infrastructure
- Requires
keyringlibrary
-
Linux Secret Service (default on Linux)
- Supports GNOME Keyring, KWallet, and other Secret Service providers
- Uses D-Bus Secret Service API
- Requires
keyringlibrary and a keyring daemon
-
HashiCorp Vault (enterprise option)
- Remote, centralized secret storage
- Multi-tenant support with namespaces
- Audit logging and access policies
- Configure via environment variables or config file
-
Encrypted File Storage (fallback)
- AES-256 encrypted files using PBKDF2 key derivation
- Protected by user password
- Used when OS keyring is unavailable
Configuration
By default, MCP CLI auto-detects the best storage backend for your platform. You can configure this in server_config.json:
{
"tokenStorage": {
"backend": "auto",
"password": "${MCP_CLI_ENCRYPTION_KEY}"
}
}
Backend Options:
auto- Auto-detect best available backend (default)keychain- macOS Keychainwindows- Windows Credential Managersecretservice- Linux Secret Servicevault- HashiCorp Vaultencrypted- Encrypted file storage
For HashiCorp Vault:
{
"tokenStorage": {
"backend": "vault",
"vaultUrl": "https://vault.example.com:8200",
"vaultToken": "${VAULT_TOKEN}",
"vaultMountPoint": "secret",
"vaultPathPrefix": "mcp-cli/oauth",
"vaultNamespace": "my-namespace"
}
}
Or use environment variables:
export VAULT_ADDR=https://vault.example.com:8200
export VAULT_TOKEN=s.your-token-here
For Encrypted File Storage:
export MCP_CLI_ENCRYPTION_KEY=your-secure-password
Token Refresh
The system automatically:
- Detects expired tokens (with 5-minute buffer)
- Refreshes using refresh token if available
- Falls back to full OAuth flow if refresh fails
Manual Token Reset
To re-authenticate a server, you'll need to delete the token from your secure storage backend:
Using the CLI (recommended):
# Future: mcp-cli auth reset {server_name}
Manual removal:
- macOS Keychain: Use Keychain Access app and search for "mcp-cli-oauth"
- Windows: Use Credential Manager and remove "mcp-cli-oauth" entries
- Linux: Use your keyring manager (seahorse, kwalletmanager, etc.)
- Vault: Delete from
{vault_path_prefix}/{server_name} - Encrypted File: Remove
~/.mcp_cli/tokens/{server_name}.enc
After removal, next connection will trigger re-authentication:
mcp-cli --server {server_name}
Security Considerations
-
Secure Token Storage:
- Tokens are stored using OS-native secure storage (Keychain, Credential Manager, Secret Service)
- Never stored in plain text files
- Protected by OS-level encryption and access controls
- Vault option provides enterprise-grade secret management with audit logging
-
Client Secrets: Store client secrets securely using environment variables:
{ "oauth": { "client_id": "${NOTION_CLIENT_ID}", "client_secret": "${NOTION_CLIENT_SECRET}" } } -
PKCE: Enabled by default for additional security. Disable only if the provider doesn't support it.
-
Localhost Callback: The callback server runs on localhost only and shuts down after receiving the callback
-
Encrypted File Fallback: When OS keyring is unavailable:
- Uses AES-256 encryption with PBKDF2 key derivation
- 480,000 iterations for key derivation (OWASP recommended)
- Requires secure password (set via environment variable or prompt)
-
HashiCorp Vault Integration:
- Supports both KV v1 and v2 secret engines
- Namespace support for enterprise multi-tenancy
- Token-based authentication (avoid storing Vault tokens in config)
- Use short-lived tokens and token renewal policies
Troubleshooting
Storage Backend Issues
Keyring not available:
# Install keyring support
pip install keyring
# macOS: No additional setup needed
# Windows: No additional setup needed
# Linux: Install a keyring daemon (gnome-keyring, kwallet, or secretstorage)
Vault connection issues:
- Verify
VAULT_ADDRandVAULT_TOKENenvironment variables - Check Vault token has permissions to read/write at the configured path
- Ensure network connectivity to Vault server
- Verify TLS certificates if using HTTPS
Encrypted file password issues:
- Set
MCP_CLI_ENCRYPTION_KEYenvironment variable to avoid prompts - Password is used to derive encryption key (cannot recover if lost)
- Delete
.saltand.encfiles in~/.mcp_cli/tokens/to start fresh
Browser Doesn't Open
If the browser doesn't open automatically, copy the URL from the terminal and paste it into your browser.
Port Already in Use
If port 8080 is in use, change the redirect_uri in your config and update it in your OAuth provider settings:
{
"oauth": {
"redirect_uri": "http://localhost:9090/callback"
}
}
Token Refresh Fails
If token refresh continuously fails:
- Delete the token from secure storage (see "Manual Token Reset" above)
- Verify your OAuth configuration is correct
- Check that your client credentials are valid
Authorization Errors
Common issues:
- Redirect URI mismatch: Ensure the redirect URI in config matches your OAuth provider settings
- Invalid client credentials: Verify client ID and secret are correct
- Missing scopes: Add required scopes to the configuration
Supported OAuth Flows
- โ Authorization Code Flow (with and without PKCE)
- โ Token Refresh Flow
- โ Client Credentials Flow (not needed for browser-based auth)
- โ Implicit Flow (deprecated, use Authorization Code + PKCE)
Example: Complete Notion Setup
{
"mcpServers": {
"notion": {
"url": "https://mcp.notion.com/mcp",
"oauth": {
"authorization_url": "https://api.notion.com/v1/oauth/authorize",
"token_url": "https://api.notion.com/v1/oauth/token",
"client_id": "abcd1234-5678-90ef-ghij-klmnopqrstuv",
"client_secret": "secret_abc123def456ghi789jkl012mno345pqr678stu",
"scopes": [],
"redirect_uri": "http://localhost:8080/callback",
"use_pkce": false
}
}
}
}
Running the CLI:
# First time - opens browser
mcp-cli --server notion
# After authentication
You: list my pages
# Token automatically included in all requests
Architecture
The OAuth implementation consists of:
oauth_config.py: Configuration models (OAuthConfig, OAuthTokens)oauth_flow.py: Authorization flow with local callback servertoken_manager.py: Token management with secure storage backendsoauth_handler.py: High-level OAuth orchestrationsecure_token_store.py: Abstract interface for token storagetoken_store_factory.py: Factory for creating storage backendsstores/: Platform-specific storage implementationskeychain_store.py- macOS Keychainwindows_store.py- Windows Credential Managerlinux_store.py- Linux Secret Servicevault_store.py- HashiCorp Vaultencrypted_file_store.py- Encrypted file fallback
Integration happens in ToolManager which:
- Detects servers with OAuth configuration
- Ensures valid tokens exist before connecting
- Injects Authorization headers into HTTP requests
- Handles token refresh transparently
Token storage backend is selected automatically based on platform and configuration, with the following priority:
- Vault (if
VAULT_ADDRandVAULT_TOKENare set) - Platform-specific keyring (Keychain/Credential Manager/Secret Service)
- Encrypted file storage (fallback)