A Docker-based lab environment for testing and developing with the Okta MCP (Model Context Protocol) Server. This setup provides multiple deployment options including an HTTP Gateway for easy integration.
Other use cases and information are available in my blog post: Lab for test the Okta MCP Server with (or without) Docker
π’ Okta MCP Server: This lab is based on the Okta MCP Server - currently in beta. The official server enables AI assistants to interact with your Okta organization through the Model Context Protocol.
You can refer to the blog post Introducing the Okta MCP Server that contains useful information and instructions on how configure the Okta tenant.
β οΈ Important: This is a development/lab setup intended for testing and experimentation. It is NOT suitable for production use as it uses a plaintext keyring for credential storage. For production deployments, use proper secret management solutions.
- Overview
- What is MCP?
- Why Use a Gateway?
- Quick Start
- Docker Installation Details
- Configuration
- Available Commands
- Client Configuration Examples
- Okta Authentication Setup
- Testing and Examples
- Native Installation Alternative
- Security Considerations
- Troubleshooting
- Additional Resources
This Docker-based lab provides a complete environment for testing the Okta MCP Server with flexible integration options:
- Okta MCP Server: Core server for Okta API operations
- HTTP Gateway: Supergateway for HTTP transport and remote access
- Gemini CLI: Google's AI-powered CLI for testing
- Client Configurations: Ready-to-use configs for VS Code, Claude Desktop, and Gemini CLI
| Component | Purpose | Access Method |
|---|---|---|
| MCP Server | Core Okta operations | stdio (internal only) |
| HTTP Gateway | Network access | HTTP on port 8000 |
| Gemini CLI | Testing interface | Via gateway |
- Isolation: No system-wide changes required
- Consistency: Same environment across all platforms
- Easy Cleanup: Simple removal with no traces
- Version Control: Infrastructure as code
- Quick Setup: Running in minutes
The Model Context Protocol (MCP) is an open protocol introduced by Anthropic that standardizes how Large Language Models communicate with external tools, resources, and remote services. It enables AI assistants to:
- Access external data sources and APIs in real-time
- Execute actions on behalf of users through natural language
- Maintain context across multiple interactions
- Integrate seamlessly with existing tools and workflows
flowchart LR
subgraph AI["AI applications"]
Chat["Chat interface <br><small>(Claude Desktop, LibreChat)</small>"]
IDE["IDEs and code editors <br><small>(Claude Code, Goose)</small>"]
Other["Other AI applications <br><small>(5ire, Superinterface)</small>"]
end
subgraph Data["Data sources and tools"]
Files["Data and file systems <br><small>(PostgreSQL, SQLite, GDrive)</small>"]
DevTools["Development tools <br><small>(Git, Sentry, etc.)</small>"]
ProdTools["Productivity tools <br><small>(Slack, Google Maps, etc.)</small>"]
end
Chat L_Chat_MCP_0@<--> MCP["MCP <br><small>Standardized protocol</small>"]
IDE L_IDE_MCP_0@<--> MCP
MCP L_MCP_Files_0@<--> Files & DevTools & ProdTools
Other L_Other_MCP_0@<--> MCP
style Chat stroke-width:2px
style IDE stroke-width:2px
style Other stroke-width:2px
style Files stroke-width:2px
style DevTools stroke-width:2px
style ProdTools stroke-width:2px
style MCP fill:#e6f3ff,stroke:#6699cc,stroke-width:2px
style AI fill:#FFE0B2,color:#000000
style Data fill:#C8E6C9
L_Chat_MCP_0@{ animation: slow }
L_IDE_MCP_0@{ animation: slow }
L_MCP_Files_0@{ animation: slow }
L_MCP_DevTools_0@{ animation: slow }
L_MCP_ProdTools_0@{ animation: slow }
L_Other_MCP_0@{ animation: slow }
The Okta MCP Server integrates with LLMs and AI agents, allowing you to perform various Okta management operations using natural language. For instance, you could simply ask Claude Desktop, VS Code Copilot, or Gemini to perform Okta management operations:
- "Create a new user and add them to the Engineering group"
- "Show me all failed login attempts from the last 24 hours"
- "List all applications that haven't been used in the past month"
- "Deactivate users who haven't logged in for 90 days"
The server provides comprehensive tool support including:
- User Management: Create, update, deactivate, and manage user profiles
- Group Operations: Manage groups and memberships
- Application Management: Configure and manage SSO applications
- Policy Administration: Handle security policies and rules
- Audit and Logging: Access system logs and authentication data
---
config:
layout: dagre
---
flowchart LR
agent["AI Agent"] L_agent_server_0@-- MCP Protocol --> server["Okta MCP Server"]
server L_server_apis_0@-- API Calls --> apis["Okta APIs"]
agent:::Sky
server:::Rose
apis:::Peach
classDef Sky stroke-width:1px, stroke-dasharray:none, stroke:#374D7C, fill:#E2EBFF, color:#374D7C
classDef Rose stroke-width:1px, stroke-dasharray:none, stroke:#FF5978, fill:#FFDFE5, color:#8E2236
classDef Peach stroke-width:1px, stroke-dasharray:none, stroke:#FBB35A, fill:#FFEFDB, color:#8F632D
L_agent_server_0@{ animation: slow }
L_server_apis_0@{ animation: slow }
The MCP protocol uses various ways for communication, but the first release of the Okta MCP Server supports only stdio (standard input/output), which works perfectly for direct, local client-server communication. However, stdio has limitations:
- Single Connection: Only one client can connect at a time
- Local Only: No network access capability
- Docker Compose Incompatibility: Cannot directly interact with stdio in detached containers
- No Session Management: No built-in way to maintain state across connections
The Supergateway (HTTP Gateway) solves these limitations by creating a bridge that enables:
- Remote Access: Connect from anywhere via HTTP
- Multiple Clients: Support concurrent connections
- Docker Compose Support: Perfect for containerized environments
flowchart LR
agent["AI Agent"] L_agent_gateway_0@-- HTTP --> gateway["Gateway"]
gateway L_gateway_server_0@-- stdio --> server["Okta MCP Server"]
server L_server_apis_0@-- API Calls --> apis["Okta APIs"]
agent:::Sky
gateway:::Aqua
gateway:::Ash
server:::Rose
apis:::Peach
classDef Sky stroke-width:1px, stroke-dasharray:none, stroke:#374D7C, fill:#E2EBFF, color:#374D7C
classDef Rose stroke-width:1px, stroke-dasharray:none, stroke:#FF5978, fill:#FFDFE5, color:#8E2236
classDef Peach stroke-width:1px, stroke-dasharray:none, stroke:#FBB35A, fill:#FFEFDB, color:#8F632D
classDef Aqua stroke-width:1px, stroke-dasharray:none, stroke:#46EDC8, fill:#DEFFF8, color:#378E7A
classDef Ash stroke-width:1px, stroke-dasharray:none, stroke:#999999, fill:#EEEEEE, color:#000000
L_agent_gateway_0@{ animation: slow }
L_gateway_server_0@{ animation: slow }
L_server_apis_0@{ animation: slow }
- Docker Compose Compatibility
- Containers in Docker Compose run in detached mode
- Cannot directly pipe stdio between services
- Gateway provides HTTP endpoints that services can communicate with
- Remote Access
- Access your MCP server from anywhere via HTTP
- Perfect for team collaboration
- Easy integration with cloud services
- Multiple Clients
- Support concurrent connections from different clients
- Each session maintains its own state
- No conflicts between users
- Standard HTTP Protocol
- Works with any HTTP client (curl, Postman, browsers)
- Easy to debug and test
- Compatible with web applications
| Use Case | Recommended Approach |
|---|---|
| Local VS Code extension | Direct stdio (Docker run without Compose) |
| Claude Desktop on same machine | Direct stdio (Docker run without Compose) |
| Docker Compose setup | HTTP Gateway (required) |
| Remote Access | HTTP Gateway + SSH tunnel |
| Web application integration | HTTP Gateway |
| API testing with Postman | HTTP Gateway |
| Multiple concurrent users | HTTP Gateway |
| Single user, local development | Either option works |
- Docker Engine 20.10+ or Docker Desktop
- Git
- Port 8000 available (or change it in the config)
- Gemini API Key
- You can generate a new API Key at https://aistudio.google.com/apikey
- There is a free tier of 100 requests/day with Gemini 2.5 Pro. Enough for testing.
- An Okta tenant
- You can refer to the blog post Introducing the Okta MCP Server that contains useful information and instructions on how configure the Okta tenant.
# Clone with submodules
git clone --recurse-submodules https://github.com/fabiograsso/okta-lab-mcp.git
cd okta-lab-mcp
# Initial setup
make setup# Create .env from template
cp example.env .env
# Edit with your Okta credentials
nano .envRequired variables:
OKTA_ORG_URL=https://my-tenant.okta.com
OKTA_CLIENT_ID=my-client-id
OKTA_KEY_ID=my-key-id
OKTA_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nmy-pem-private-key...\n-----END PRIVATE KEY-----
OKTA_SCOPES=okta.users.read okta.groups.read# Build Docker images
make build
# Start all services
make start
# Verify health
make health# Test the gateway
curl http://localhost:8000
# View logs
make logs-gateway
make logs-mcp
make logs-allokta-lab-mcp/
βββ Docker Files
β βββ Dockerfile # Main Okta MCP server image
β βββ Dockerfile-gateway # MCP + Gateway image
β βββ docker-compose.yml # Service orchestration
β
βββ Configuration
β βββ env-example # Template for .env
β
βββ Management Scripts
β βββ Makefile # Simplified commands
β βββ docker-utils.sh # Docker utilities
β
βββ Source Code
β βββ okta-mcp-server/ # Okta MCP server source (git submodule)
β
βββ Runtime Data
β βββ data/
β βββ okta-mcp/
β β βββ logs/ # Container logs
β βββ gemini/ # Gemini workspace
β
βββ Sample Configurations
βββ sample/
βββ .vscode/
β βββ mcp.json # VS Code config
βββ .gemini/
β βββ settings.json # Gemini config
βββ Claude Desktop/
βββ claude_desktop_config.json
- Purpose: Core MCP server functionality
- Base Image: python:3.13-slim
- Communication: stdio only (internal)
- Access: Through
dockercommand only
- Purpose: MCP Server + HTTP Gateway
- Base Image: okta-mcp-server + Node.js
- Port: 8000 (configurable via env
GATEWAY_PORT) - Protocol: HTTP
- Access: http://localhost:8000
- Purpose: Testing and demonstration
- Image: Google's official Gemini CLI
- Access: Connects to the gateway via HTTP
You can run both the MCP Server + Gateway and Gemini-cli:
# Start all services (recommended for lab)
docker compose up -d
# Services running:
# - MCP Server + HTTP Gateway (port 8000)
# - Gemini CLI# For direct stdio access (VS Code, Claude Desktop, Gemini run locally)
docker run -i --rm \
-e OKTA_ORG_URL="https://my-tenant.okta.com" \
-e OKTA_CLIENT_ID="your_client_id" \
-e OKTA_KEY_ID="your_key_id" \
-e OKTA_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" \
-e OKTA_SCOPES="okta.users.read okta.groups.read" \
okta-mcp-server
# No .env file needed - credentials passed directly# Start just the gateway
docker compose up -d okta-mcp-server-gateway
# Access at http://localhost:8000| Host Path | Container Path | Purpose |
|---|---|---|
| ./data/okta-mcp/logs | /app/logs | MCP Server logs |
| ./data/gemini | /home/node/.gemini/ | Gemini workspace |
# Required
OKTA_ORG_URL=https://my-tenant.okta.com
OKTA_CLIENT_ID=my-client-id
OKTA_KEY_ID=my-key-id
OKTA_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nmy-pem-private-key...\n-----END PRIVATE KEY-----
OKTA_SCOPES=okta.users.read okta.groups.read okta.apps.read
# Optional
OKTA_LOG_LEVEL=INFO
GATEWAY_PORT=8000
GEMINI_API_KEY=your-gemini-api-keyEnvironment variables are passed directly with -e flags:
docker run -i --rm \
-e "OKTA_ORG_URL=https://my-tenant.okta.com" \
-e "OKTA_CLIENT_ID=my-client-id" \
-e "OKTA_KEY_ID=my-key-id" \
-e "OKTA_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" \
-e "OKTA_SCOPES=okta.users.read okta.groups.read" \
-e "OKTA_LOG_LEVEL=DEBUG" \
okta-mcp-serverThe private key must be formatted as a single line with \n for newlines:
# Original format
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
more_key_content_here...
-----END PRIVATE KEY-----
# Formatted for environment variable
-----BEGIN PRIVATE KEY-----\nmy-pem-private-keyQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...\nmore_key_content_here...\n-----END PRIVATE KEY-----Convert using:
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' private_key.pemThe sample/ folder contains ready-to-use configurations:
In each file you'll find two servers:
- Using Docker for run the Okta MCP.
- Using HTTP for connect to the Gateway
You can then experiment with both and enable/disable each one as needed.
Ready-to-use configuration for VS Code.
Example:
{
"mcp": {
"servers": {
"okta-docker": {
"type": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e", "OKTA_ORG_URL=https://xxxx.okta.com",
"-e", "OKTA_CLIENT_ID=xxxx",
"-e", "OKTA_SCOPES=okta.users.read okta.groups.read okta.logs.read okta.apps.read okta.userTypes.read",
"-e", "OKTA_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nxxxxxxxx\n-----END PRIVATE KEY-----",
"-e", "OKTA_KEY_ID=xxxxx",
"-e", "OKTA_LOG_LEVEL=DEBUG",
"okta-mcp-server"
]
},
"okta-gateway": {
"type": "http",
"url": "http://localhost:8000/mcp"
}
}
}
}The sample code implement both ways to connect to the MCP Server: via HTTP gateway or via stdio. You can choose what to use, or try both. I suggest in any case to keep only one active at time.
Ready-to-use configuration for Claude Desktop.
Ready-to-use configuration for Gemini-cli (if you run it locally instead of inside the docker compose).
The MCP Server supports two authentication methods:
- Create a new App Integration in your Okta org
- Select OIDC - OpenID Connect and Native Application
- Under Grant type, ensure Device Authorization is checked
- Go to Okta API Scopes tab and grant permissions (e.g.,
okta.users.read,okta.groups.manage) - Save and copy the Client ID
- Create App: Select API Services and save
- Configure Client Authentication:
- Disable Require Demonstrating Proof of Possession (DPoP)
- Select Public key / Private key authentication
- Add Public Key: Generate in Okta or upload your own
- Grant API Scopes: Add required permissions
- Assign Admin Roles: Assign Super Administrator role
My lab is meant to use the Private Key JWT (Browserless) method, in order to run without requiring user interaction for registration. For detailed instructions, refer to Okta's blog post.
You have different ways to start the gemini-cli:
- Docker Desktop: Open the
gemini-clicontainer, go to the Exec tab and rungemini - **Exec gemini in the docker compose:
docker compose exec gemini-cli gemini # or make gemini
- Standalone Docker:
docker run -it --rm \ --network okta-gemini-network \ -v ./data/gemini:/home/node/.gemini:rw \ -e GEMINI_API_KEY="${GEMINI_API_KEY}" \ us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.8.1 - Local Installation:
# Using npx (no installation required) npx https://github.com/google-gemini/gemini-cli # Or install with NPM npm install -g @google/gemini-cli # Or with brew brew install gemini-cli # Then just run gemini
Once you started gemini-cli, you can run /mcp to see available MCP servers and supported commands. Then test queries like:
How many users are in my Okta tenant?What are the latest logins of user Fabio Grasso?Add user Fabio Grasso to the group Office 365 LicenseCheck the logs for login errors and provide me the list of users with more than 5 failed login attemptsCheck the logs, count all logins per user and give me the ranking of the 10 top users by login count
make help # Display all available commands
make setup # Initial setup
make build # Build Docker images
make start # Start all services
make stop # Stop all services
make status # Check service status
make health # Health check
make logs # View logsmake start-gateway # Start only gateway
make restart # Restart all services
make logs-gateway # Gateway logs only
make logs-all # All service logs
make shell # Open shell in containermake gateway-test # Test HTTP gateway
make run-example # Show Docker run examplemake clean # Clean Docker resources
make clean-all # Complete cleanup
make update # Update and rebuild
make backup-logs # Backup logsThis setup is designed for testing and development only:
- Plaintext Keyring: Uses
keyrings.alt.file.PlaintextKeyring - HTTP Transport: No TLS/SSL encryption
- No Authentication: Gateway has no access control
- Environment Variables: Sensitive data in plain text
- Log Files: May contain sensitive information
When running on a remote server, create an SSH tunnel for secure access (instead of opening the HTTP port):
# On your local machine
ssh -L 8000:localhost:8000 username@server-ip
# Access the gateway at http://localhost:8000Never use this setup in production. Instead:
- Implement proper secret management
- Use HTTPS with valid certificates
- Add authentication to the gateway
- Implement rate limiting
- Use dedicated service accounts
- Enable audit logging
- Encrypt data at rest and in transit
-
Authentication Failures
- Verify
OKTA_ORG_URL,OKTA_CLIENT_ID,OKTA_PRIVATE_KEY,OKTA_KEY_IDand scopes - Check application has required API scopes granted
- Ensure admin roles are assigned
- Verify
-
MCP Client Connection Issues
- Restart your MCP client after installation
- Verify server path in configuration
- Check that
uvis installed and accessible
-
Docker Issues
- Use
make logs-allto view container logs - Verify port 8000 is available
- Check Docker compose services with
make status
- Use
-
Gateway Access Problems
- Test with
curl http://localhost:8000 - Check firewall rules for port 8000
- Verify HTTP gateway container is running
- Test with
Enable detailed logging:
# For Docker deployment
export OKTA_LOG_LEVEL=DEBUG
# Or add to .env file
OKTA_LOG_LEVEL=DEBUGYou will find in my blog post detailed instructions on how to install this stack (Okta MCP Server + Gateway + Gemini-cli) in a Linux (Ubuntu) server or in macOS.
Advantages of Native Installation:
β
Direct stdio access for local clients
β
Better performance (no container overhead)
β
System service integration with systemd
β
Auto-start on boot
β
Native file system access
Disadvantages of Native Installation
β System-wide changes required
β Harder to clean up completely
β Platform-specific (the script is Ubuntu only)
β Manual updates required
Quick Testing? β Use Docker installation with HTTP Gateway
Local Development? β Use standalone Docker with stdio
Persistent Test Server? β Use native installation
No Docker Available? β Use native installation
Remote Access Needed? β Set up SSH tunnel
Multiple Users? β Use HTTP Gateway
Need help? Check the troubleshooting section or view logs with make logs-all
Questions or feedback? Feel free to open an issue on the okta-lab-mcp repository.