Deploy with OIDC Authentication
Complete guide to deploying Seed MCP Server with OIDC authentication and connecting it to Claude Code and Claude Desktop.
Overview
This guide covers:
Prerequisites
Required Services
OIDC Provider - Any OIDC-compliant identity provider:
- Authentik (recommended for self-hosted)
- Okta
- Auth0
- Keycloak
- Google OAuth
- Azure AD / Entra ID
Redis Server - For session and client storage:
bash# Docker docker run -d --name redis -p 6379:6379 redis:alpine # Or use existing Redis instance1
2
3
4
OIDC Provider Setup
Create an Application/Client in your OIDC provider
Configure the client:
- Grant Types: Authorization Code, Refresh Token
- Redirect URIs:
- For Claude Desktop:
http://localhost:PORT/callback(any port, Claude handles this) - For testing:
http://localhost:8080/callback
- For Claude Desktop:
- PKCE: Required
- Client Type: Public (no client secret)
Note these values:
OIDC_ISSUER- Issuer URL (e.g.,https://auth.example.com/application/o/seed/)OIDC_AUDIENCE- Client ID (e.g.,seed-client-abc123)OAUTH_TOKEN_URL- Token endpoint (e.g.,https://auth.example.com/application/o/token/)OAUTH_AUTHORIZATION_URL- Authorization endpoint (e.g.,https://auth.example.com/application/o/authorize/)
TIP
Most OIDC providers support auto-discovery via {issuer}/.well-known/openid-configuration. Seed will automatically discover endpoints if you only provide OIDC_ISSUER.
Deploy Seed Server
Step 1: Create Configuration
Create a .env file with your OIDC configuration:
# .env
# Server
PORT=3000
BASE_URL=http://localhost:3000
# Authentication
AUTH_REQUIRED=true
# OIDC Provider
OIDC_ISSUER=https://auth.example.com/application/o/seed/
OIDC_AUDIENCE=seed-client-abc123
# OAuth Endpoints
OAUTH_TOKEN_URL=https://auth.example.com/application/o/token/
OAUTH_AUTHORIZATION_URL=https://auth.example.com/application/o/authorize/
# Redis
REDIS_URL=redis://localhost:6379
# Optional: CORS (if needed)
# CORS_EXTRA_ORIGINS=https://custom-claude-instance.com2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
WARNING
Replace the example values with your actual OIDC provider details!
Step 2: Start the Server
Choose your preferred deployment method:
Option A: Docker Compose (Recommended)
Create docker-compose.yml:
version: '3.8'
services:
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped
seed:
image: seed-mcp-server:latest
ports:
- "3000:3000"
environment:
PORT: 3000
BASE_URL: http://localhost:3000
AUTH_REQUIRED: "true"
OIDC_ISSUER: ${OIDC_ISSUER}
OIDC_AUDIENCE: ${OIDC_AUDIENCE}
OAUTH_TOKEN_URL: ${OAUTH_TOKEN_URL}
OAUTH_AUTHORIZATION_URL: ${OAUTH_AUTHORIZATION_URL}
REDIS_URL: redis://redis:6379
depends_on:
- redis
restart: unless-stopped
volumes:
redis_data:2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Start services:
docker-compose up -dOption B: Docker
# Start Redis
docker run -d \
--name redis \
-p 6379:6379 \
redis:alpine
# Start Seed
docker run -d \
--name seed \
-p 3000:3000 \
--env-file .env \
seed-mcp-server:latest2
3
4
5
6
7
8
9
10
11
12
Option C: From Source
# Install dependencies
npm install
# Build
npm run build
# Start server
npm start2
3
4
5
6
7
8
Step 3: Verify Server is Running
# Health check
curl http://localhost:3000/health
# Expected response:
# {"status":"ok","timestamp":"2025-12-09T..."}
# OAuth discovery
curl http://localhost:3000/.well-known/oauth-authorization-server
# Should return OAuth server metadata2
3
4
5
6
7
8
9
10
Claude Code Setup
Configure Claude Code (CLI) to connect to your authenticated Seed MCP Server.
Step 1: Locate Configuration File
Claude Code configuration is stored at:
~/.config/claude/config.jsonIf it doesn't exist, create it:
mkdir -p ~/.config/claude
touch ~/.config/claude/config.json2
Step 2: Add MCP Server Configuration
You can configure the MCP server using either the CLI command or manual configuration.
Option A: Using CLI Command (Recommended)
Use the claude mcp add command to automatically configure the server with OAuth:
claude mcp add --scope user --transport http seed http://localhost:3000/mcp \
--auth-type oauth2 \
--auth-url http://localhost:3000/oauth/authorize \
--token-url http://localhost:3000/oauth/token \
--client-id claude-code \
--scopes openid \
--pkce2
3
4
5
6
7
This automatically adds the configuration to ~/.config/claude/config.json.
Option B: Manual Configuration
Alternatively, manually edit ~/.config/claude/config.json and add your Seed server:
{
"mcpServers": {
"seed": {
"url": "http://localhost:3000/mcp",
"transport": "http",
"authentication": {
"type": "oauth2",
"authorizationUrl": "http://localhost:3000/oauth/authorize",
"tokenUrl": "http://localhost:3000/oauth/token",
"clientId": "claude-code",
"scopes": ["openid"],
"pkce": true
}
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Configuration Breakdown
- url: MCP endpoint -
http://localhost:3000/mcp - transport: Always
httpfor HTTP-based MCP servers - authentication.type: Use
oauth2for OIDC/OAuth authentication - authorizationUrl: Seed's authorization proxy endpoint
- tokenUrl: Seed's token proxy endpoint
- clientId: Any identifier for this Claude Code client (e.g.,
claude-code,my-cli-client) - scopes: Minimum
openidscope required - pkce: Always
true(required by Seed)
Step 3: Test Connection
Start Claude Code and it will automatically trigger the OAuth flow:
claudeWhat happens:
- Claude Code detects the OAuth configuration
- Opens your default browser to the authorization URL
- Redirects you to your OIDC provider login
- After login and consent, redirects back with authorization code
- Claude Code exchanges the code for access token
- Connection established!
Expected output:
✓ Connected to seed MCP server
✓ Authenticated as user@example.com
Available tools: seed_ping2
3
Troubleshooting Claude Code
"Failed to connect to MCP server"
Check server is running:
curl http://localhost:3000/healthCheck configuration URL is correct:
# Test MCP endpoint (should require auth)
curl http://localhost:3000/mcp
# Expected: 401 Unauthorized with WWW-Authenticate header2
3
"OAuth authorization failed"
Verify OAuth endpoints:
# Check authorization endpoint
curl "http://localhost:3000/oauth/authorize?response_type=code&client_id=test"
# Should redirect to your OIDC provider
# Check token endpoint accepts requests
curl -X POST http://localhost:3000/oauth/token
# Should return OAuth error (expected without valid params)2
3
4
5
6
7
Check OIDC provider settings:
- Redirect URI is configured correctly
- Client allows public access (no client secret)
- PKCE is enabled
"Token expired"
This is normal! Claude Code should automatically refresh the token using the refresh token. If refresh fails:
- Check your OIDC provider supports refresh tokens
- Verify
refresh_tokengrant type is enabled - Check refresh token hasn't been revoked
Manual re-authentication:
# Remove cached credentials
rm -rf ~/.config/claude/credentials
# Restart Claude Code
claude2
3
4
5
Claude Desktop Setup
Configure Claude Desktop to connect to your authenticated Seed MCP Server.
Step 1: Open Claude Desktop Settings
- Open Claude Desktop application
- Click Settings (gear icon)
- Navigate to MCP Servers or Developer section
Step 2: Add MCP Server Configuration
In Claude Desktop's MCP server configuration, add:
{
"mcpServers": {
"seed": {
"url": "http://localhost:3000/mcp",
"authentication": {
"type": "oauth2",
"authorization_endpoint": "http://localhost:3000/oauth/authorize",
"token_endpoint": "http://localhost:3000/oauth/token",
"client_registration_endpoint": "http://localhost:3000/oauth/register",
"scopes": ["openid"]
}
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
Dynamic Client Registration
Claude Desktop uses Dynamic Client Registration (RFC 7591) to automatically register itself as an OAuth client. This means:
- No manual client creation needed
- Each Claude Desktop instance gets its own client ID
- Clients are stored in Redis with 30-day TTL
Step 3: Connect and Authenticate
After adding the configuration:
- Click "Connect" or restart Claude Desktop
- Browser opens - Claude Desktop opens your browser to the authorization URL
- Login - Enter credentials at your OIDC provider
- Grant consent - Approve the requested permissions
- Redirect - After authorization, browser redirects back to Claude Desktop
- Connected! - Claude Desktop is now connected and authenticated
Visual confirmation:
- ✅ Green dot next to "seed" MCP server
- Status shows "Connected"
- User info displays your authenticated identity
Step 4: Verify Tools are Available
In Claude Desktop chat:
Can you ping the seed server?Claude should respond using the seed_ping tool, confirming:
- ✅ Connection to Seed server works
- ✅ Authentication is successful
- ✅ Tools are accessible
Troubleshooting Claude Desktop
"Failed to connect"
Check server URL is accessible:
# From your desktop machine
curl http://localhost:3000/health2
If using Docker on a different machine, replace localhost with the server's IP or hostname.
"Dynamic client registration failed"
Verify DCR endpoint:
curl -X POST http://localhost:3000/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "Test Client",
"redirect_uris": ["http://localhost:8080/callback"]
}'
# Should return client_id and metadata2
3
4
5
6
7
8
Check Redis is running:
# If using Docker Compose
docker-compose ps
# Should show both redis and seed services running2
3
4
Check rate limiting: DCR has rate limiting (default: 10 registrations per hour per IP). If you've been testing extensively:
# Wait an hour, or restart Redis to clear rate limits
docker restart redis2
"Authorization failed" or "Token exchange failed"
Check OIDC provider configuration:
- Redirect URI pattern allows dynamic ports (e.g.,
http://localhost:*orhttp://127.0.0.1:*) - Client is configured for public access (PKCE, no client secret)
- All required scopes are available (minimum:
openid)
Check Seed logs:
# Docker Compose
docker-compose logs seed
# Docker
docker logs seed
# From source
# Logs appear in console2
3
4
5
6
7
8
Look for errors related to JWKS fetching, token validation, or OAuth proxy.
"Token expired" - Constant re-authentication
This indicates refresh token flow is failing.
Verify refresh tokens are enabled:
- Check your OIDC provider allows refresh tokens
- Verify token endpoint supports
refresh_tokengrant type - Check token lifetime isn't too short
Check Seed configuration:
# Ensure these are set correctly
echo $OAUTH_TOKEN_URL
echo $OIDC_ISSUER2
3
Verify Setup
1. Test MCP Connection
With curl:
First, get an access token (see First Steps for complete OAuth flow).
Then test MCP endpoint:
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1.0"}
}
}'2
3
4
5
6
7
8
9
10
11
12
13
Expected response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"serverInfo": {
"name": "seed",
"version": "0.1.3"
},
"capabilities": {
"tools": {}
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
2. Test Tool Execution
In Claude Code:
Can you ping the seed server and tell me the status?In Claude Desktop: Use the same prompt in chat.
Expected: Claude uses the seed_ping tool and reports server status.
3. Check User Context
Tools receive authenticated user information from the JWT. Check logs to verify:
# Docker Compose
docker-compose logs seed | grep "User context"
# Should show user info from JWT2
3
4
4. Verify Token Refresh
Wait for the access token to expire (typically 1 hour), then use Claude again. The OAuth client should automatically refresh the token without requiring re-authentication.
Security Considerations
Production Deployment
For production, ensure:
Use HTTPS - OAuth requires secure connections:
bashBASE_URL=https://seed.example.com1Secure Redis - Enable authentication:
bashREDIS_URL=redis://:password@redis:63791Configure CORS - Restrict origins:
bashCORS_EXTRA_ORIGINS=https://claude.ai1Set strong OIDC configuration:
- Use production OIDC provider
- Enable MFA for users
- Configure appropriate token lifetimes
- Implement proper redirect URI validation
Monitor logs - Watch for authentication failures:
bashdocker-compose logs -f seed1
Network Security
Firewall rules:
# Allow only necessary ports
# 3000: Seed MCP server
# 6379: Redis (internal only, not exposed)2
3
Reverse proxy (nginx example):
server {
listen 443 ssl;
server_name seed.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
Token Management
Access token lifetime:
- Default: 1 hour (set by OIDC provider)
- Automatically refreshed by OAuth clients
- No action needed from users
Refresh token lifetime:
- Default: 30 days (set by OIDC provider)
- After expiration, user must re-authenticate
- Can be configured in your OIDC provider
Client TTL:
# Dynamically registered clients expire after 30 days
# Configurable via:
DCR_CLIENT_TTL=2592000 # seconds2
3
Next Steps
Now that your Seed MCP Server is deployed and connected:
- Create Custom Tools - Extend functionality
- Monitor Usage - Understand session lifecycle
- Scale Deployment - Add more instances
- Configure OIDC Provider - Fine-tune settings
Related Documentation
- Configuration Reference - All environment variables
- First Steps - Complete OAuth flow walkthrough
- Architecture: Authentication - How JWT validation works
- Architecture: OAuth - OAuth 2.1 implementation details
- API Reference: OAuth - OAuth endpoint documentation