MCP CLI now supports OAuth 2.0 authentication for HTTP-based MCP servers like Notion.
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/
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
}
}
}
}| 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 |
-
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" } } } }
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
MCP CLI uses secure, OS-native token storage to protect your OAuth credentials. Tokens are never stored in plain text files.
-
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
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-hereFor Encrypted File Storage:
export MCP_CLI_ENCRYPTION_KEY=your-secure-passwordThe system automatically:
- Detects expired tokens (with 5-minute buffer)
- Refreshes using refresh token if available
- Falls back to full OAuth flow if refresh fails
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}-
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
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
If the browser doesn't open automatically, copy the URL from the terminal and paste it into your browser.
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"
}
}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
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
- ✅ 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)
{
"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 requestsThe 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)