Skip to main content

Setup

1. Install Keycloak

Choose your deployment method: Docker:
docker run -p 8080:8080 \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:latest start-dev
Or use existing Keycloak instance

2. Create Realm

  1. Open Keycloak Admin Console (http://localhost:8080)
  2. Click Add Realm or select existing realm
  3. Set Realm Name (e.g., “my-realm”)

3. Create Client

  1. Go to ClientsCreate
  2. Set Client ID: my-mcp-server
  3. Set Client Protocol: openid-connect
  4. Click Save
Configure Client:
  • Access Type: confidential (for server-side) or public (for SPA)
  • Valid Redirect URIs: http://localhost:3000/callback, https://yourdomain.com/callback
  • Web Origins: http://localhost:3000, https://yourdomain.com
  • Save and note the Client Secret from Credentials tab

4. Create Users and Roles

Create Realm Roles:
  1. Go to RolesAdd Role
  2. Create roles: admin, editor, viewer
Create Users:
  1. Go to UsersAdd User
  2. Set username, email, enable user
  3. In Credentials tab, set password
  4. In Role Mappings tab, assign roles

Configuration

Basic Configuration

import { MCPServer, oauthKeycloakProvider } from 'mcp-use/server'

const server = new MCPServer({
  name: 'my-server',
  version: '1.0.0',
  oauth: oauthKeycloakProvider({
    serverUrl: process.env.MCP_USE_OAUTH_KEYCLOAK_SERVER_URL!, // 'https://keycloak.example.com'
    realm: process.env.MCP_USE_OAUTH_KEYCLOAK_REALM!,          // 'my-realm'
    clientId: process.env.MCP_USE_OAUTH_KEYCLOAK_CLIENT_ID!,   // 'my-mcp-server'
  })
})

await server.listen(3000)

Environment Variables

# .env
MCP_USE_OAUTH_KEYCLOAK_SERVER_URL=http://localhost:8080
MCP_USE_OAUTH_KEYCLOAK_REALM=my-realm
MCP_USE_OAUTH_KEYCLOAK_CLIENT_ID=my-mcp-server

Full Configuration Options

const server = new MCPServer({
  oauth: oauthKeycloakProvider({
    // Required
    serverUrl: 'https://keycloak.example.com',
    realm: 'my-realm',
    clientId: 'my-mcp-server',
    
    // Optional
    clientSecret: process.env.KEYCLOAK_CLIENT_SECRET,
    
    // OAuth mode: 'proxy' (default) or 'direct'
    mode: 'proxy',
    
    // JWT verification
    verifyJwt: process.env.NODE_ENV === 'production',
    
    // Custom scopes
    scopes: ['openid', 'profile', 'email', 'roles'],
    
    // Custom user info extraction
    getUserInfo: (payload) => ({
      userId: payload.sub,
      email: payload.email,
      name: payload.name,
      username: payload.preferred_username,
      roles: payload.realm_access?.roles || [],
      clientRoles: payload.resource_access?.[clientId]?.roles || [],
    })
  })
})

Resources

Next Steps