Multi-auth allows a single MCP server to support multiple authentication methods simultaneously, with automatic fallback based on priority.
Scenario: Interactive users use OAuth flow in Cursor/VS Code, while CI/CD pipelines use static Bearer tokens.
Profile Configuration:
{
"interceptors": {
"auth": [
{
"type": "oauth",
"priority": 0,
"oauth_config": {
"issuer": "https://gitlab.example.com",
"client_id": "${env:MCP4_OAUTH_CLIENT_ID}",
"client_secret": "${env:MCP4_OAUTH_CLIENT_SECRET}",
"redirect_uri": "${env:MCP4_OAUTH_REDIRECT_URI}"
}
},
{
"type": "bearer",
"priority": 1,
"value_from_env": "MCP4_API_TOKEN"
}
]
}
}How it works:
-
Interactive users (Cursor/VS Code):
- Server detects OAuth session
- Uses OAuth token from session
- If no session → displays "Connect" button
-
CI/CD pipelines:
- Send
Authorization: Bearer <CI_TOKEN>header - Server uses Bearer token
- No browser flow required
- Send
Scenario: Support legacy systems using custom headers (e.g., X-API-Key), while also supporting modern clients using standard Bearer tokens.
Profile Configuration:
{
"interceptors": {
"auth": [
{
"type": "custom-header",
"priority": 0,
"header_name": "X-API-Key",
"value_from_env": "API_KEY"
},
{
"type": "bearer",
"priority": 1,
"value_from_env": "MCP4_API_TOKEN"
}
]
}
}How it works:
- Legacy client: Sends
X-API-Key: abc123→ server uses custom header token - Modern client: Sends
Authorization: Bearer xyz789→ server uses Bearer token - Different clients can use different auth methods
1. HTTP Transport receives request
↓
2. extractAuthToken() checks for authentication in order:
a. OAuth session (mcp-session-id header)
b. Authorization: Bearer header
c. X-API-Token header (custom)
↓
3. If token found → authenticate request
If no token → return 401 Unauthorized
- Lower priority = Higher precedence
- Priority
0is tried first, then1, then2, etc. - Default priority is
0if not specified
Example:
{
"auth": [
{"type": "oauth", "priority": 0}, // Tried first
{"type": "session-cookie", "priority": 1}, // Tried second
{"type": "bearer", "priority": 2} // Tried third
]
}HTTP Transport checks for tokens in this order:
-
OAuth Session
- Highest priority for active user sessions
- If session exists and has token → use it
-
Authorization Header (Bearer token)
- Format:
Authorization: Bearer <token> - For CI/CD, API clients, scripts
- Format:
-
Custom Headers (e.g.,
X-API-Token)- For legacy systems or special integrations
Important: Token detection happens in HTTP transport layer, before profile auth configs are consulted. Profile configs define which tokens are valid and how to use them, but detection is built into the transport.
session-cookie is not token-detected. It uses profile-managed upstream login credentials from session_cookie_config and maintains the cookie jar inside the HTTP client runtime.
{
"interceptors": {
"auth": [
{
"type": "oauth | bearer | query | custom-header | session-cookie",
"priority": 0,
// ... type-specific fields
}
]
}
}| Field | Type | Required | Description |
|---|---|---|---|
type |
string |
✅ | Auth method: oauth, bearer, query, custom-header, session-cookie |
priority |
integer |
❌ | Priority (lower = higher). Default: 0 |
value_from_env |
string |
✅* | Environment variable name (for bearer, query, custom-header) |
header_name |
string |
✅** | Custom header name (for custom-header) |
query_param |
string |
✅*** | Query parameter name (for query) |
oauth_config |
object |
✅**** | OAuth configuration (for oauth) |
session_cookie_config |
object |
✅***** | Session cookie login configuration (for session-cookie) |
*Required for: bearer, query, custom-header
**Required for: custom-header
***Required for: query
****Required for: oauth
*****Required for: session-cookie
session_cookie_config follows the profile auth schema from docs/PROFILE-GUIDE.md, including login_endpoint, credential env vars, and cookie_names.
{
"interceptors": {
"auth": {
"type": "bearer",
"value_from_env": "MCP4_API_TOKEN"
}
}
}This is equivalent to:
{
"interceptors": {
"auth": [
{
"type": "bearer",
"priority": 0,
"value_from_env": "MCP4_API_TOKEN"
}
]
}
}- OAuth sessions take precedence over static tokens
- Prevents accidental use of wrong credentials
- User-specific OAuth tokens > service account tokens
- Never commit tokens to version control
- Use
.envfiles locally (gitignored) - Use Kubernetes Secrets in production
Example .env:
# Required for OAuth browser flow
MCP4_TRANSPORT=http
# OAuth (for interactive users)
MCP4_OAUTH_CLIENT_ID=your-client-id
MCP4_OAUTH_CLIENT_SECRET=your-secret
# Bearer (for CI/CD)
MCP4_API_TOKEN=glpat-xxxxxxxxxxxx
# GitLab instance URL
MCP4_API_BASE_URL=https://gitlab.example.com/api/v4CLI alternative:
npx mcp4openapi \
--transport http \
--oauth-client-id your-client-id \
--oauth-client-secret your-secret \
--api-token glpat-xxxxxxxxxxxx \
--api-base-url https://gitlab.example.com/api/v4- OAuth tokens: Automatically refreshed by OAuth provider
- Bearer tokens: Rotate manually or use short-lived CI tokens
# 1. Start server with multi-auth profile
export MCP4_TRANSPORT=http
export MCP4_OAUTH_AUTHORIZATION_URL=https://gitlab.example.com/oauth/authorize
export MCP4_OAUTH_TOKEN_URL=https://gitlab.example.com/oauth/token
export MCP4_OAUTH_CLIENT_ID=xxx
export MCP4_OAUTH_CLIENT_SECRET=yyy
export MCP4_OAUTH_REDIRECT_URI=https://<your-mcp-server-host>/oauth/callback
export MCP4_API_BASE_URL=https://gitlab.example.com/api/v4
npm start
# 2. Configure Cursor
{
"mcpServers": {
"gitlab": {
"url": "http://<your-mcp-server-host>/mcp"
}
}
}
# 3. Click "Connect" button → OAuth flowCLI alternative:
npx mcp4openapi \
--transport http \
--oauth-authorization-url https://gitlab.example.com/oauth/authorize \
--oauth-token-url https://gitlab.example.com/oauth/token \
--oauth-client-id xxx \
--oauth-client-secret yyy \
--oauth-redirect-uri https://<your-mcp-server-host>/oauth/callback \
--api-base-url https://gitlab.example.com/api/v4# Without OAuth session, use Bearer token
curl -H "Authorization: Bearer $MCP4_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
http://localhost:3003/mcp# Create session with OAuth
curl -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
http://localhost:3003/mcp
# Extract session ID from response
# Then try with both OAuth session AND Bearer token
# OAuth should take precedence
curl -H "Mcp-Session-Id: <session-id>" \
-H "Authorization: Bearer $MCP4_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
http://localhost:3003/mcp
# Should use OAuth token from session, not Bearer tokenCause: OAuth endpoints not initialized (missing OAuth config or environment variables)
Solution:
# Check server logs
grep -i "oauth" server.log
# Should see:
# "OAuth authentication enabled for HTTP transport"
# "OAuth provider initialized"
# "OAuth routes registered"
# If missing, verify:
echo $MCP4_OAUTH_CLIENT_ID
echo $MCP4_OAUTH_CLIENT_SECRETCause: Bearer token not provided or invalid
Solution:
# Verify token in environment
echo $MCP4_API_TOKEN
# Test token manually
curl -H "Authorization: Bearer $MCP4_API_TOKEN" \
https://www.gitlab.com/api/v4/personal_access_tokens/selfCause: OAuth metadata endpoint not available
Solution:
# Verify OAuth metadata
curl http://localhost:3003/.well-known/oauth-authorization-server
# Should return JSON with OAuth endpoints
# If 404, OAuth is not initializedSee: profiles/gitlab/multi-auth-profile.json
# K8s deployment with multi-auth
apiVersion: v1
kind: Secret
metadata:
name: mcp-gitlab-auth
data:
oauth-client-id: <base64>
oauth-client-secret: <base64>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-gitlab
spec:
template:
spec:
containers:
- name: mcp-server
env:
# OAuth for users
- name: MCP4_OAUTH_CLIENT_ID
valueFrom:
secretKeyRef:
name: mcp-gitlab-auth
key: oauth-client-id
- name: MCP4_OAUTH_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: mcp-gitlab-auth
key: oauth-client-secret
# Bearer token sent in Authorization header from client-
Use OAuth for Interactive Users
- Better UX (browser-based auth)
- Automatic token refresh
- User-specific permissions
-
Use Bearer for Automation
- CI/CD pipelines
- Scheduled jobs
- Service-to-service calls
-
Set Priorities Correctly
- OAuth (priority 0) for users
- Bearer (priority 1) for automation
- Custom fallbacks (priority 2+)
-
Secure Environment Variables
- Use Kubernetes Secrets
- Never log tokens
- Rotate regularly
-
Test Both Paths
- Verify OAuth flow works
- Verify Bearer token works
- Test fallback behavior