@warnyin/zitadel-mcp
v0.2.0
Published
MCP (Model Context Protocol) server for ZITADEL identity & access management. Exposes ZITADEL v2 + management APIs as tools usable from any MCP-compatible client (Claude Desktop, Claude Code, Cursor, etc.).
Readme
@warnyin/zitadel-mcp
MCP (Model Context Protocol) server that exposes the ZITADEL identity & access management API as tools you can call from any MCP-compatible client — Claude Desktop, Claude Code, Cursor, Continue, etc.
- Built against ZITADEL's v2 APIs (users, sessions, organizations) plus the v1 management API for projects, applications, roles, and tokens.
- Authenticates with a service-account Personal Access Token (PAT).
- Ships as a zero-config CLI; run it via
npx @warnyin/zitadel-mcp— no global install required.
Quick start
1. Provision a service account & PAT in ZITADEL
In the ZITADEL console:
- Create (or reuse) an organization that the server will manage.
- Create a Service User (Console → Users → Service Users → New). Pick Access Token Type = Bearer.
- Grant it the roles you need (
IAM_OWNERfor instance-wide, orORG_OWNER,ORG_USER_MANAGER,PROJECT_OWNER, … for narrower scopes). - Open the service user → Personal Access Tokens → New and copy the token. It is shown only once.
2. Configure your MCP client
Add a server entry pointing at npx zitadel-mcp. For example, Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"zitadel": {
"command": "npx",
"args": ["-y", "@warnyin/zitadel-mcp"],
"env": {
"ZITADEL_INSTANCE_URL": "https://your-instance.zitadel.cloud",
"ZITADEL_PERSONAL_ACCESS_TOKEN": "pat_xxxxxxxxxxxxxxxxx",
"ZITADEL_DEFAULT_ORG_ID": "344648897353810062"
}
}
}
}Claude Code (~/.claude/mcp.json or .mcp.json at the project root) follows the same shape. Cursor uses the same JSON under Settings → MCP.
Restart the client and the zitadel tools should appear in the tool list.
3. (Optional) Try it locally
export ZITADEL_INSTANCE_URL=https://your-instance.zitadel.cloud
export ZITADEL_PERSONAL_ACCESS_TOKEN=pat_xxxxxxxxxxxxxxxxx
npx @warnyin/zitadel-mcp --help
npx -y @modelcontextprotocol/inspector npx -y @warnyin/zitadel-mcpThe MCP Inspector opens a web UI where you can introspect every tool, see its Zod schema, and call it with sample arguments.
Environment variables
| Variable | Required | Description |
| --- | --- | --- |
| ZITADEL_INSTANCE_URL | ✅ | Base URL of your ZITADEL instance, no trailing slash. e.g. https://acme-abc123.zitadel.cloud |
| ZITADEL_PERSONAL_ACCESS_TOKEN | ✅ | Bearer PAT issued to a service account that holds the roles your tools need. |
| ZITADEL_DEFAULT_ORG_ID | ⛔ optional | Default organization ID sent as the x-zitadel-orgid header for management calls. Each tool can override with its own organizationId argument. |
| ZITADEL_TIMEOUT_MS | ⛔ optional | HTTP timeout per request in ms. Default 30000. |
The server prints fatal config errors to stderr and exits — your MCP client surface will show the error in its log.
Tool catalog (37 tools)
Users (v2)
zitadel_create_human_user— create an interactive (human) user with profile + email (+ optional password/phone/metadata)zitadel_create_machine_user— create a service-account user (bearer or JWT access tokens)zitadel_get_user— fetch a user by IDzitadel_search_users— paginated search; accepts raw ZITADELqueriesfor any combination of filterszitadel_set_user_password— admin reset or self-service (withcurrentPasswordverification)zitadel_deactivate_user/zitadel_reactivate_userzitadel_lock_user/zitadel_unlock_userzitadel_delete_user(destructive)
Organizations
zitadel_create_organization— create an org (v2)zitadel_setup_organization—_setupadmin endpoint: create org + initial admin in one call (needsIAM_OWNER)zitadel_search_organizationszitadel_get_my_organization— metadata for the caller's org
Projects
zitadel_create_projectzitadel_get_projectzitadel_list_projectszitadel_deactivate_project/zitadel_reactivate_projectzitadel_delete_project(destructive)zitadel_add_project_role/zitadel_bulk_add_project_roleszitadel_list_project_roles
Applications
zitadel_create_oidc_app— full OIDC config (PKCE, auth method, response/grant types, app type, role assertions…)zitadel_create_api_app— machine-to-machine API appzitadel_list_apps,zitadel_get_appzitadel_delete_app(destructive)zitadel_regenerate_oidc_client_secret(destructive — rotates secret)
Sessions (v2)
zitadel_create_session— initiate session with optional user/password/totp/intent checkszitadel_get_sessionzitadel_update_session— add factor checks (requiressessionToken)zitadel_delete_session
Service-account credentials
zitadel_add_personal_access_token— issue PAT for a machine user (token returned once)zitadel_list_personal_access_tokenszitadel_remove_personal_access_tokenzitadel_add_machine_key— JSON key for JWT Profile auth
PKCE / OIDC example
For a Next.js (or any SPA/native) PKCE client without a static client secret:
// zitadel_create_oidc_app
{
"projectId": "278123847301234567",
"name": "Web App (PKCE)",
"appType": "OIDC_APP_TYPE_USER_AGENT",
"authMethodType": "OIDC_AUTH_METHOD_TYPE_NONE",
"responseTypes": ["OIDC_RESPONSE_TYPE_CODE"],
"grantTypes": [
"OIDC_GRANT_TYPE_AUTHORIZATION_CODE",
"OIDC_GRANT_TYPE_REFRESH_TOKEN"
],
"redirectUris": ["https://app.example.com/api/auth/callback"],
"postLogoutRedirectUris": ["https://app.example.com"],
"accessTokenType": "OIDC_TOKEN_TYPE_BEARER"
}This returns clientId plus clientSecret: "" (none for PKCE). Use clientId together with PKCE in your app.
Development
git clone <this-repo>
cd zitadel-mcp
npm install
npm run dev # tsx watch — auto-restarts on edits
# build & sanity-check
npm run build
node dist/index.js --help
# inspect tools interactively
npx -y @modelcontextprotocol/inspector npx -y @warnyin/zitadel-mcpProject layout
src/
├── index.ts # entry point (#!/usr/bin/env node, stdio transport)
├── constants.ts # env keys, limits, version
├── types.ts # shared interfaces
├── schemas/
│ └── common.ts # Zod schemas reused across tools (pagination, org header, response format)
├── services/
│ ├── config.ts # env -> typed config + validation
│ ├── client.ts # axios wrapper, injects x-zitadel-orgid for /management
│ ├── errors.ts # ZITADEL → human messages
│ └── formatter.ts # JSON/Markdown response formatting + CHARACTER_LIMIT
└── tools/
├── util.ts # okJson / okFormatted / withErrorHandling
├── users.ts # v2 users
├── organizations.ts # v2 orgs + admin _setup
├── projects.ts # /management/v1/projects
├── applications.ts # OIDC/API apps
├── sessions.ts # v2 sessions
└── tokens.ts # PATs + machine keysTroubleshooting
| Symptom | Likely cause | Fix |
| --- | --- | --- |
| Error 401 (Unauthenticated) | PAT expired / revoked / wrong instance | Rotate the PAT in console and update the env var |
| Error 403 (Permission denied) | Service account missing role | Grant the needed role (e.g. ORG_OWNER, IAM_OWNER) |
| Error 404 (Not found) | Wrong ID or wrong org context | Pass organizationId explicitly or set ZITADEL_DEFAULT_ORG_ID |
| Request to ZITADEL timed out | Slow network or instance | Increase ZITADEL_TIMEOUT_MS (e.g. 60000) |
| DNS lookup failed | Typo in ZITADEL_INSTANCE_URL | Use the full URL incl. https:// |
License
MIT.
