Test-Governance MCP Server
Enables AI assistants to securely access Okta Identity Governance APIs for managing users, groups, apps, access requests, and certifications with configurable deployment modes and safety controls.
README
Test - Governance MCP Server for OIG
Disclaimer: This project is a test/proof of concept developed using publicly available Okta APIs. This MCP server implementation is not affiliated with Okta, its official MCP servers, or its developer tools. This was developed on a personal device without any use of Okta (Okta, Inc.) resources or information outside of what is available to the public.
A Model Context Protocol (MCP) server that provides AI assistants with secure, structured access to Okta Identity Governance (OIG) APIs.
Built with TypeScript on Node.js, this server exposes 28 tools (19 admin + 9 end-user) that let LLMs query users, groups, apps, entitlements, access requests, certification campaigns, and more — with built-in safety controls for write operations.
Features
- 28 tools — 19 admin tools for Okta core + OIG governance APIs, plus 9 end-user tools for delegated mode
- Four deployment modes —
read-only,governance-admin,full-adminfor admin use;delegatedfor end-user access via their own Okta login - End-user authentication — OAuth2 Authorization Code + PKCE flow lets users sign in with their own Okta credentials; the AI agent acts within their permissions
- Confirmation protocol — write/destructive actions in admin modes require a two-step confirmation with HMAC-signed tokens
- Intelligent rate limiting — tracks Okta's per-endpoint rate limits and backs off proactively
- Response caching — LRU cache reduces redundant API calls (disabled in delegated mode to prevent cross-user data leaks)
- Full audit trail — every tool invocation logged to stderr; mutations logged to SQLite
- Two transports — stdio (for Claude Desktop, CLI tools) or HTTP (for web integrations and delegated mode)
- Auto-detection — probes your Okta org at startup to determine if OIG is enabled (admin modes)
Prerequisites
- Node.js >= 20
- An Okta org with API access
- Okta Identity Governance license (for governance tools; core user/group/app tools work without it)
Installation
git clone https://github.com/kaivalyapowale/test-governance-mcp.git
cd test-governance-mcp
npm install
npm run build
Configuration
All configuration is via environment variables. Copy .env.example to .env and fill in your values:
cp .env.example .env
Required
| Variable | Description |
|---|---|
OKTA_ORG_URL |
Your Okta org URL (e.g., https://your-org.okta.com) |
Authentication (choose one)
Option 1: SSWS API Token (simpler, good for development)
| Variable | Description |
|---|---|
OKTA_API_TOKEN |
Okta API token (created in Admin > Security > API > Tokens) |
Option 2: OAuth2 Client Credentials (recommended for production)
| Variable | Description |
|---|---|
OKTA_CLIENT_ID |
OAuth2 service app client ID |
OKTA_PRIVATE_KEY_PATH |
Path to RS256 private key PEM file |
OKTA_SCOPES |
Comma-separated OAuth2 scopes (see Scopes below) |
OAuth2 Scopes
The default scopes (okta.users.read, okta.groups.read, okta.apps.read, okta.logs.read) only cover core Okta APIs. If your org has OIG enabled, you will need additional governance scopes for the OIG tools to work. The exact scope names depend on your Okta org configuration — check your Okta Admin console under Security > API > Authorization Servers for available governance scopes (e.g., okta.governance.accessRequests.read, okta.governance.campaigns.read, etc.).
Alternatively, using an SSWS API token with sufficient admin permissions avoids scope configuration entirely, as SSWS tokens inherit the permissions of the admin user who created them.
Optional
| Variable | Default | Description |
|---|---|---|
MCP_DEPLOYMENT_MODE |
read-only |
read-only, governance-admin, full-admin, or delegated |
MCP_CLIENT_SECRET |
— | Required for governance-admin and full-admin modes. Used to sign confirmation tokens. |
MCP_STATE_DB_PATH |
./data/state.db |
SQLite database path for audit log and token tracking |
MCP_TRANSPORT |
stdio |
stdio or http |
MCP_HTTP_PORT |
3000 |
HTTP server port (when transport is http) |
MCP_HTTP_HOST |
127.0.0.1 |
HTTP server bind address |
MCP_LOG_LEVEL |
info |
debug, info, warn, or error |
Delegated Mode Configuration
Delegated mode requires additional configuration for the OAuth2 Authorization Code flow with PKCE. This mode requires HTTP transport.
| Variable | Description |
|---|---|
OKTA_DELEGATED_CLIENT_ID |
Client ID of an Okta app configured for Authorization Code + PKCE (SPA or Native type) |
OKTA_DELEGATED_REDIRECT_URI |
Redirect URI registered in the Okta app (e.g., http://localhost:3000/auth/callback) |
OKTA_DELEGATED_SCOPES |
Comma-separated scopes (defaults to openid,profile,email,okta.users.read,okta.groups.read,okta.apps.read) |
MCP_SESSION_SECRET |
Secret key for signing session cookies (use a random 32+ character string) |
Usage
Stdio transport (Claude Desktop, CLI)
npm start
Add to your Claude Desktop config (claude_desktop_config.json):
{
"mcpServers": {
"okta-governance": {
"command": "node",
"args": ["/absolute/path/to/oig-mcp-server/dist/index.js"],
"env": {
"OKTA_ORG_URL": "https://your-org.okta.com",
"OKTA_API_TOKEN": "your-token",
"MCP_DEPLOYMENT_MODE": "read-only"
}
}
}
}
HTTP transport (admin modes)
MCP_TRANSPORT=http MCP_HTTP_PORT=3000 npm start
Endpoints:
POST /mcp— MCP JSON-RPC endpointGET /health— Health check (returns tool count and deployment mode)
Delegated mode (end-user access)
Delegated mode lets end users authenticate with their own Okta credentials. The server uses their access token for all API calls, so responses are naturally scoped to their permissions — no admin privileges required.
MCP_DEPLOYMENT_MODE=delegated \
MCP_TRANSPORT=http \
OKTA_ORG_URL=https://your-org.okta.com \
OKTA_DELEGATED_CLIENT_ID=your_spa_client_id \
OKTA_DELEGATED_REDIRECT_URI=http://localhost:3000/auth/callback \
MCP_SESSION_SECRET=$(openssl rand -hex 32) \
npm start
Auth flow (browser — cookie-based):
- User visits
GET /auth/login→ redirected to Okta sign-in page - After login, Okta redirects to
GET /auth/callback→ session cookie is set - All subsequent
POST /mcprequests use the user's own access token GET /auth/status— check if authenticatedPOST /auth/logout— end session
Auth flow (CLI / non-browser — Bearer token):
For non-browser MCP clients (Claude Code CLI, MCP Inspector, custom scripts), you can pass a user's Okta access token directly via the Authorization header instead of using cookies:
curl -X POST http://localhost:3000/mcp \
-H "Authorization: Bearer <user's Okta access token>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"okta_my_profile","arguments":{}},"id":1}'
The server validates the token against Okta (GET /api/v1/users/me), extracts the user identity, and runs the tool as that user. This is stateless — no session is stored. Bearer tokens take priority over cookies if both are present.
Okta app setup: Create a SPA or Native application in Okta Admin (Applications > Create App Integration). Set the Sign-in redirect URI to match OKTA_DELEGATED_REDIRECT_URI. Enable Authorization Code grant type with PKCE. No client secret is needed.
Deployment Modes
The server supports four deployment modes. The first three are admin modes that use a service-level API token or OAuth2 client credentials. The fourth is delegated mode where each end user authenticates individually.
Admin Modes
| Mode | Access | Use Case |
|---|---|---|
read-only |
Query users, groups, apps, entitlements, grants, campaigns, etc. | Safe exploration and reporting |
governance-admin |
Read + create access requests, launch campaigns, submit decisions, assign group/app membership | Day-to-day governance operations |
full-admin |
All actions including revoke grants, remove memberships, user lifecycle (suspend/deactivate) | Full administrative control |
Write and destructive actions use a two-step confirmation protocol: the first call returns a preview and a signed token; the second call with that token executes the action. Tokens are single-use and expire after 5 minutes.
Delegated Mode
| Mode | Access | Use Case |
|---|---|---|
delegated |
End-user tools only: view own profile/groups/apps, browse catalog, request access, review certifications | AI-assisted end-user experience (e.g., chatbot, help desk) |
In delegated mode:
- The user logs in with their own Okta credentials via OAuth2 Authorization Code + PKCE
- All API calls use the user's own access token — no admin privileges
- The user's
userIdis automatically injected into all tool calls (they can't query other users) - No confirmation tokens are needed — the user IS the actor
- Admin tools are completely hidden; only end-user tools are available
Integrating with AI Agents (Delegated Mode)
Delegated mode is designed for building AI-powered end-user experiences — chatbots, help desk agents, or self-service portals where users interact with an AI assistant to manage their own access.
Architecture
┌─────────────────────────────────────────────────────────┐
│ End User's Browser / App │
│ │
│ ┌──────────────┐ ┌──────────────────────────────┐ │
│ │ Chat UI / │────▶│ AI Agent Backend │ │
│ │ Frontend │◀────│ (Claude, GPT, custom LLM) │ │
│ └──────────────┘ └──────────┬───────────────────┘ │
│ │ MCP JSON-RPC │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ This MCP Server │ │
│ │ (delegated mode, HTTP) │ │
│ │ │ │
│ │ /auth/login → Okta SSO │ │
│ │ /auth/callback ← token │ │
│ │ /mcp ← tools (user context) │ │
│ └──────────┬───────────────────┘ │
│ │ Bearer {user token} │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ Okta APIs │ │
│ │ (scoped to user's perms) │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
How it works
- User opens your app — the frontend checks
GET /auth/status. If not authenticated, it redirects the user toGET /auth/login. - User signs in via Okta — standard Okta login page (SSO, MFA, etc.). After authentication, Okta redirects back to
/auth/callback, which sets a session cookie. - AI agent calls tools — the agent backend sends MCP JSON-RPC requests to
POST /mcp. The session cookie is included automatically. Every tool call runs under the user's own identity. - User asks questions naturally — e.g., "What apps do I have access to?", "Request access to Salesforce", "Do I have any pending reviews?". The AI agent translates these into the appropriate tool calls.
What the AI agent can do for the user
| User intent | Tool used | What happens |
|---|---|---|
| "What's my access?" | iga_my_access |
Returns user's groups, apps, and active grants in one call |
| "Show me my groups" | okta_my_groups |
Lists groups the user belongs to |
| "What can I request access to?" | iga_catalog_browse |
Browses the access catalog scoped to the user |
| "Request access to Salesforce Admin role" | iga_request_access |
Submits an access request with the user as requester |
| "What's the status of my requests?" | iga_my_requests |
Lists the user's access requests with status |
| "Do I have any reviews to complete?" | iga_my_pending_reviews |
Lists pending certification items assigned to the user |
| "Approve the review for Jane's access" | iga_submit_review_decision |
Submits APPROVE/REVOKE on a review item |
Key behaviors in delegated mode
- No userId parameter — the user's identity is injected automatically from their session. They cannot query other users' data.
- No confirmation tokens — unlike admin modes, delegated tools execute directly because the authenticated user IS the actor making the request.
- Okta enforces permissions — if the user doesn't have permission for an action, Okta returns 403. The server doesn't need its own permission layer.
- Session expiry — tokens refresh automatically. If the refresh token expires, the user is prompted to log in again.
- Admin tools are hidden — the AI agent only sees the 9 delegated tools. It cannot access admin-level tools like
okta_users,iga_campaigns, origa_risk_assessment.
Example: connecting Claude as the AI agent
If you're using Claude (or another LLM) as the agent backend with an MCP client library:
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
// The MCP client connects to your delegated-mode server
// The session cookie from the user's browser must be forwarded
const transport = new StreamableHTTPClientTransport(
new URL('http://localhost:3000/mcp'),
{
// Forward the user's session cookie from the browser request
headers: { Cookie: `mcp_session=${userSessionCookie}` },
},
);
const client = new Client({ name: 'my-agent', version: '1.0.0' });
await client.connect(transport);
// Now call tools — they execute as the authenticated user
const myAccess = await client.callTool('iga_my_access', {});
Okta app setup for delegated mode
- Go to Okta Admin Console → Applications → Create App Integration
- Select OIDC - OpenID Connect → Single-Page Application (or Native)
- Set Sign-in redirect URI to your
OKTA_DELEGATED_REDIRECT_URI(e.g.,http://localhost:3000/auth/callback) - Set Sign-out redirect URI if needed
- Under General Settings, ensure Authorization Code grant type is enabled (with PKCE — this is default for SPA apps)
- No client secret is needed (PKCE replaces it)
- Under Assignments, assign the users or groups who should have access
- Copy the Client ID and use it as
OKTA_DELEGATED_CLIENT_ID
Tools
Delegated End-User Tools (delegated mode only)
These tools are available when the server runs in delegated mode. They operate under the authenticated user's identity — no userId parameter needed.
| Tool | Description |
|---|---|
okta_my_profile |
View your own Okta profile |
okta_my_groups |
List groups you belong to |
okta_my_apps |
List applications assigned to you |
iga_my_access |
Full summary of your access: profile, groups, apps, and active grants |
iga_catalog_browse |
Browse the access catalog to discover requestable entitlements |
iga_request_access |
Submit an access request (requesterId auto-set to you) |
iga_my_requests |
List your own access requests and their status |
iga_my_pending_reviews |
List certification review items pending your decision |
iga_submit_review_decision |
Submit APPROVE/REVOKE decision on a review item assigned to you |
Admin Tools (read-only / governance-admin / full-admin modes)
Core Okta Tools (always available)
| Tool | Actions | Description |
|---|---|---|
okta_users |
list, get, groups, apps, lifecycle, assign_to_group, remove_from_group, assign_to_app, remove_from_app |
Manage users and their memberships |
okta_groups |
list, get, members |
Query groups and membership |
okta_apps |
list, get |
Query application integrations |
OIG Governance Tools (require OIG license)
| Tool | Actions | Description |
|---|---|---|
iga_entitlements |
list, get, search, set_owner |
Manage application entitlements |
iga_bundles |
list, get, create, update |
Entitlement bundles (access packages) |
iga_resource_collections |
list, get |
Resource collection groupings |
iga_policies |
list, get, create, update |
Governance policies |
iga_sod |
list, get, detect, create |
Separation of duties rules and violation detection |
iga_grants |
list, get, revoke |
Active entitlement grants |
iga_campaigns |
list, get, create, launch, list_items, submit_decision, close, archive |
Certification campaigns |
iga_access_requests |
list, get, create, decide |
Access request workflows |
iga_security_reviews |
list, get, submit_finding |
Security review management |
iga_list_catalog_entries |
— | Browse the access catalog |
iga_get_audit_log |
— | Query local mutation audit log |
Intelligence Tools (require OIG license)
These tools fan out to multiple APIs in a single call to provide aggregated insights.
| Tool | Description |
|---|---|
iga_investigate_user_access |
Full picture of a user's access: profile, groups, apps, grants, pending requests, SoD violations. Supports summary, detailed, and full depth. |
iga_explain_access_changes |
Timeline of governance-relevant changes for a user over a time range |
iga_governance_dashboard |
Org-wide governance snapshot: active campaigns, pending requests, open reviews, SoD violations |
iga_risk_assessment |
Risk scoring for a user based on grant count, auth failures, login recency, and account status |
iga_access_coverage_report |
Per-app governance coverage: entitlement discovery, active policies, recent certifications |
Architecture
src/
├── index.ts # Server entry point, transport setup
├── config.ts # Zod-validated configuration from env vars
├── auth/
│ ├── token-manager.ts # SSWS and OAuth2 (JWT client credentials) auth
│ ├── session-manager.ts # OAuth2 Auth Code + PKCE flow, session storage (delegated mode)
│ └── session-context.ts # AsyncLocalStorage for per-request session threading
├── api/
│ └── client.ts # HTTP client with caching, rate limiting, retries
├── state/
│ ├── repository.ts # State interface (audit log, token tracking)
│ └── sqlite.ts # SQLite implementation (better-sqlite3, WAL mode)
├── tools/
│ ├── registry.ts # Tool registry with deployment mode filtering
│ ├── delegated.ts # End-user tools for delegated mode (9 tools)
│ ├── users.ts # okta_users
│ ├── groups.ts # okta_groups
│ ├── apps.ts # okta_apps
│ ├── entitlements.ts # iga_entitlements
│ ├── bundles.ts # iga_bundles
│ ├── collections.ts # iga_resource_collections
│ ├── policies.ts # iga_policies
│ ├── sod.ts # iga_sod
│ ├── grants.ts # iga_grants
│ ├── campaigns.ts # iga_campaigns
│ ├── access-requests.ts # iga_access_requests
│ ├── security-reviews.ts # iga_security_reviews
│ ├── catalog.ts # iga_list_catalog_entries
│ ├── audit-log.ts # iga_get_audit_log
│ ├── investigate.ts # iga_investigate_user_access
│ ├── explain-changes.ts # iga_explain_access_changes
│ ├── dashboard.ts # iga_governance_dashboard
│ ├── risk.ts # iga_risk_assessment
│ └── coverage.ts # iga_access_coverage_report
└── util/
├── types.ts # Shared types (ToolResponse, ToolError, etc.)
├── errors.ts # Error classes and Okta error mapping
├── response.ts # Response builder helpers
├── confirmation.ts # HMAC-SHA256 confirmation token protocol
├── action-filter.ts # Per-action deployment mode filtering
└── audit.ts # Audit logger (stderr + SQLite) with field redaction
Security
All modes
- No credentials in code — all auth is via environment variables
- Rate limit awareness — proactively backs off when Okta rate limit headers show remaining capacity below threshold
- Concurrency control — configurable semaphore limits parallel API calls (default: 3)
- Deployment mode gating — tools and actions are filtered at registration time so the LLM never sees unavailable operations
Admin modes (read-only / governance-admin / full-admin)
- Confirmation tokens — write/destructive operations require HMAC-SHA256 signed, single-use tokens with 5-minute expiry
- Audit logging — all tool invocations logged to stderr; all mutations logged to SQLite with Okta request IDs
- Sensitive field redaction — fields named
password,secret,token,apikey,credentials,privatekey, orclientSecretare replaced with[REDACTED]in logs - Response caching — LRU cache reduces redundant API calls
Delegated mode
- OAuth2 PKCE — Authorization Code flow with Proof Key for Code Exchange; no client secret stored on the server
- Signed session cookies — session IDs are HMAC-signed to prevent tampering; cookies are
HttpOnlyandSameSite=Lax - No cross-user data — cache is skipped for all delegated API calls; each request uses only the authenticated user's token
- User identity auto-injected — tools cannot be tricked into querying other users;
userIdcomes from the session, not from parameters - Okta-enforced permissions — the server does not maintain its own permission model; Okta returns 403 if the user lacks access
- Session lifecycle — tokens refresh automatically; idle sessions expire after 24 hours; expired pending auth flows are cleaned up every 5 minutes
Development
# Watch mode (recompiles on change)
npm run dev
# Run tests
npm test
# Type check without emitting
npx tsc --noEmit
Dependencies
| Package | Purpose | License |
|---|---|---|
| @modelcontextprotocol/sdk | MCP server SDK | MIT |
| better-sqlite3 | SQLite for audit log and token tracking | MIT |
| jose | JWT signing for OAuth2 client credentials | MIT |
| lru-cache | API response caching | ISC |
| zod | Schema validation for config and tool inputs | MIT |
Disclaimer
This project is a test/proof of concept developed using publicly available Okta APIs. This MCP server implementation is not affiliated with Okta, its official MCP servers, or its developer tools. This was developed on a personal device without any use of Okta (Okta, Inc.) resources or information outside of what is available to the public.
License
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.