Skip to content

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:

  1. Prerequisites
  2. Deploy Seed Server
  3. Configure Claude Code
  4. Configure Claude Desktop
  5. Verify Setup

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 instance

OIDC Provider Setup

  1. Create an Application/Client in your OIDC provider

  2. 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
    • PKCE: Required
    • Client Type: Public (no client secret)
  3. 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:

bash
# .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.com

WARNING

Replace the example values with your actual OIDC provider details!

Step 2: Start the Server

Choose your preferred deployment method:

Create docker-compose.yml:

yaml
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:

Start services:

bash
docker-compose up -d

Option B: Docker

bash
# 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:latest

Option C: From Source

bash
# Install dependencies
npm install

# Build
npm run build

# Start server
npm start

Step 3: Verify Server is Running

bash
# 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 metadata

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:

bash
~/.config/claude/config.json

If it doesn't exist, create it:

bash
mkdir -p ~/.config/claude
touch ~/.config/claude/config.json

Step 2: Add MCP Server Configuration

You can configure the MCP server using either the CLI command or manual configuration.

Use the claude mcp add command to automatically configure the server with OAuth:

bash
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 \
  --pkce

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:

json
{
  "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
      }
    }
  }
}

Configuration Breakdown

  • url: MCP endpoint - http://localhost:3000/mcp
  • transport: Always http for HTTP-based MCP servers
  • authentication.type: Use oauth2 for 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 openid scope required
  • pkce: Always true (required by Seed)

Step 3: Test Connection

Start Claude Code and it will automatically trigger the OAuth flow:

bash
claude

What happens:

  1. Claude Code detects the OAuth configuration
  2. Opens your default browser to the authorization URL
  3. Redirects you to your OIDC provider login
  4. After login and consent, redirects back with authorization code
  5. Claude Code exchanges the code for access token
  6. Connection established!

Expected output:

✓ Connected to seed MCP server
✓ Authenticated as user@example.com
Available tools: seed_ping

Troubleshooting Claude Code

"Failed to connect to MCP server"

Check server is running:

bash
curl http://localhost:3000/health

Check configuration URL is correct:

bash
# Test MCP endpoint (should require auth)
curl http://localhost:3000/mcp
# Expected: 401 Unauthorized with WWW-Authenticate header

"OAuth authorization failed"

Verify OAuth endpoints:

bash
# 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)

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:

  1. Check your OIDC provider supports refresh tokens
  2. Verify refresh_token grant type is enabled
  3. Check refresh token hasn't been revoked

Manual re-authentication:

bash
# Remove cached credentials
rm -rf ~/.config/claude/credentials

# Restart Claude Code
claude

Claude Desktop Setup

Configure Claude Desktop to connect to your authenticated Seed MCP Server.

Step 1: Open Claude Desktop Settings

  1. Open Claude Desktop application
  2. Click Settings (gear icon)
  3. Navigate to MCP Servers or Developer section

Step 2: Add MCP Server Configuration

In Claude Desktop's MCP server configuration, add:

json
{
  "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"]
      }
    }
  }
}

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:

  1. Click "Connect" or restart Claude Desktop
  2. Browser opens - Claude Desktop opens your browser to the authorization URL
  3. Login - Enter credentials at your OIDC provider
  4. Grant consent - Approve the requested permissions
  5. Redirect - After authorization, browser redirects back to Claude Desktop
  6. 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:

bash
# From your desktop machine
curl http://localhost:3000/health

If using Docker on a different machine, replace localhost with the server's IP or hostname.

"Dynamic client registration failed"

Verify DCR endpoint:

bash
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 metadata

Check Redis is running:

bash
# If using Docker Compose
docker-compose ps

# Should show both redis and seed services running

Check rate limiting: DCR has rate limiting (default: 10 registrations per hour per IP). If you've been testing extensively:

bash
# Wait an hour, or restart Redis to clear rate limits
docker restart redis

"Authorization failed" or "Token exchange failed"

Check OIDC provider configuration:

  • Redirect URI pattern allows dynamic ports (e.g., http://localhost:* or http://127.0.0.1:*)
  • Client is configured for public access (PKCE, no client secret)
  • All required scopes are available (minimum: openid)

Check Seed logs:

bash
# Docker Compose
docker-compose logs seed

# Docker
docker logs seed

# From source
# Logs appear in console

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_token grant type
  • Check token lifetime isn't too short

Check Seed configuration:

bash
# Ensure these are set correctly
echo $OAUTH_TOKEN_URL
echo $OIDC_ISSUER

Verify Setup

1. Test MCP Connection

With curl:

First, get an access token (see First Steps for complete OAuth flow).

Then test MCP endpoint:

bash
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"}
    }
  }'

Expected response:

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "serverInfo": {
      "name": "seed",
      "version": "0.1.3"
    },
    "capabilities": {
      "tools": {}
    }
  }
}

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:

bash
# Docker Compose
docker-compose logs seed | grep "User context"

# Should show user info from JWT

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:

  1. Use HTTPS - OAuth requires secure connections:

    bash
    BASE_URL=https://seed.example.com
  2. Secure Redis - Enable authentication:

    bash
    REDIS_URL=redis://:password@redis:6379
  3. Configure CORS - Restrict origins:

    bash
    CORS_EXTRA_ORIGINS=https://claude.ai
  4. Set strong OIDC configuration:

    • Use production OIDC provider
    • Enable MFA for users
    • Configure appropriate token lifetimes
    • Implement proper redirect URI validation
  5. Monitor logs - Watch for authentication failures:

    bash
    docker-compose logs -f seed

Network Security

Firewall rules:

bash
# Allow only necessary ports
# 3000: Seed MCP server
# 6379: Redis (internal only, not exposed)

Reverse proxy (nginx example):

nginx
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;
    }
}

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:

bash
# Dynamically registered clients expire after 30 days
# Configurable via:
DCR_CLIENT_TTL=2592000  # seconds

Next Steps

Now that your Seed MCP Server is deployed and connected:

  1. Create Custom Tools - Extend functionality
  2. Monitor Usage - Understand session lifecycle
  3. Scale Deployment - Add more instances
  4. Configure OIDC Provider - Fine-tune settings

Released under the MIT License.