Mcp Ory Kratos — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Mcp Ory Kratos (Agent Skill) and scored it 92/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 2 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 2 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
MCP server enabling AI assistants to manage Ory Kratos identities, sessions, and authentication flows. Built for developers integrating identity management into Claude Code, GitHub Copilot, or Gemini CLI workflows.
Note: Kratos deployment and configuration is out of scope for this MCP server. See the Ory Kratos documentation for deployment guidance.
Install via npm or run directly with npx:
# Using npm
npm install -g mcp-ory-kratos
# Using npx (no installation required)
npx mcp-ory-kratos
# Using bun
bun add -g mcp-ory-kratos| Variable | Required | Default | Description |
|---|---|---|---|
KRATOS_ADMIN_URL | Yes | - | Kratos Admin API base URL (e.g., http://localhost:4434) |
KRATOS_AUTH_TYPE | No | none | Authentication type: none, api-key, or custom-headers |
KRATOS_API_KEY | Conditional | - | Required when KRATOS_AUTH_TYPE=api-key |
KRATOS_CUSTOM_HEADERS | Conditional | - | JSON object of headers when KRATOS_AUTH_TYPE=custom-headers |
KRATOS_TIMEOUT_MS | No | 30000 | Request timeout in milliseconds |
KRATOS_TOOLSETS | No | all | Comma-separated toolsets to expose: identities, sessions, courier, recovery, health, analytics (or all) |
KRATOS_READ_ONLY | No | false | 1/true hides every tool that is not read-only |
KRATOS_CONFIRM_DESTRUCTIVE | No | true | 0/false disables the elicitation prompt before destructive tools run |
KRATOS_ALLOW_CREDENTIAL_EXPOSURE | No | false | 1/true returns raw credential config (password hashes, OIDC tokens, TOTP secrets) instead of redacting it |
KRATOS_MAX_SCAN_PAGES | No | 20 | Default page cap (1-1000) for tools that scan many pages (analytics, filtered session listing) |
LOG_LEVEL | No | info | Log level: trace, debug, info, warn, error |
Boolean variables accept 1, true, yes, on (case-insensitive); anything else is false.
Admin URL: The Kratos SDK appends/admin/...to the base URL itself. Trailing slashes are stripped, and ifKRATOS_ADMIN_URLalready ends in/admin(common behind a reverse proxy, e.g.https://ory.example.com/kratos/admin) that suffix is removed for SDK calls so paths do not become/admin/admin/.... Bothhttp://localhost:4434andhttp://localhost:4434/adminwork.
Note: This MCP server can run alongside other MCP servers in your configuration. Each server operates independently.
Add to ~/.claude.json for global configuration:
{
"mcpServers": {
"kratos": {
"type": "stdio",
"command": "npx",
"args": ["-y", "mcp-ory-kratos"],
"env": {
"KRATOS_ADMIN_URL": "http://localhost:4434"
}
}
}
}For project-scoped configuration, create .mcp.json in your project root:
{
"mcpServers": {
"kratos": {
"type": "stdio",
"command": "npx",
"args": ["-y", "mcp-ory-kratos"],
"env": {
"KRATOS_ADMIN_URL": "http://localhost:4434"
}
}
}
}Create .vscode/mcp.json in your workspace:
{
"servers": {
"kratos": {
"type": "stdio",
"command": "npx",
"args": ["-y", "mcp-ory-kratos"],
"env": {
"KRATOS_ADMIN_URL": "http://localhost:4434"
}
}
}
}For secrets handling with input variables:
{
"inputs": [
{
"type": "promptString",
"id": "kratos-url",
"description": "Kratos Admin API URL",
"password": false
}
],
"servers": {
"kratos": {
"type": "stdio",
"command": "npx",
"args": ["-y", "mcp-ory-kratos"],
"env": {
"KRATOS_ADMIN_URL": "${input:kratos-url}"
}
}
}
}Add to ~/.gemini/settings.json for global configuration:
{
"mcpServers": {
"kratos": {
"command": "npx",
"args": ["-y", "mcp-ory-kratos"],
"env": {
"KRATOS_ADMIN_URL": "http://localhost:4434"
}
}
}
}For project-scoped configuration, create .gemini/settings.json in your project:
{
"mcpServers": {
"kratos": {
"command": "npx",
"args": ["-y", "mcp-ory-kratos"],
"env": {
"KRATOS_ADMIN_URL": "http://localhost:4434"
}
}
}
}Choose your MCP client from the Configuration section above and add the appropriate configuration.
Start your MCP client and verify the tools are available:
/mcp to see available serversAsk your AI assistant:
"List all identities in Kratos"
The MCP server will execute kratos_list_identities and return the results.
27 tools, grouped by toolset (enable/disable groups with KRATOS_TOOLSETS). Kind is the MCP annotation: read-only tools survive KRATOS_READ_ONLY=1; destructive tools change or remove data and trigger a confirmation prompt (see Behaviour).
Toolset: identities
| Tool | Kind | Description |
|---|---|---|
kratos_list_identities | read-only | List identities; filter by credential identifier (exact or fuzzy), IDs, organization; optional includeCredential |
kratos_get_identity | read-only | Get an identity by ID; includeCredential (array of types) expands credentials (includeCredentials: true is deprecated) |
kratos_get_identity_by_external_id | read-only | Look up an identity by external_id |
kratos_list_identity_schemas | read-only | List identity JSON schemas (paginated) |
kratos_get_identity_schema | read-only | Get one identity JSON schema by ID |
kratos_create_identity | create | Create an identity with schema, traits, metadata, credentials and addresses |
kratos_batch_patch_identities | create | Bulk-create up to 100 identities in one non-atomic request, with per-item results and a summary |
kratos_update_identity | destructive | Full update of an identity (replaces all fields) |
kratos_patch_identity | destructive | Partial update using JSON Patch operations |
kratos_set_identity_state | destructive | Activate or suspend an identity; optionally revoke all its sessions |
kratos_delete_identity | destructive | Permanently delete an identity and all associated data |
kratos_delete_identity_credential | destructive | Delete a credential type; identifier (<provider>:<subject>) unlinks a single oidc/saml provider |
Toolset: sessions
| Tool | Kind | Description |
|---|---|---|
kratos_list_sessions | read-only | List sessions (pageSize/pageToken); filter (auth method, provider, time range) scans up to maxPages pages client-side |
kratos_get_session | read-only | Get session details by ID |
kratos_list_identity_sessions | read-only | List sessions for one identity |
kratos_disable_session | destructive | Revoke/disable a session (log user out) |
kratos_extend_session | destructive | Extend session expiration time |
kratos_delete_identity_sessions | destructive | Delete all sessions for an identity |
Toolset: courier
| Tool | Kind | Description |
|---|---|---|
kratos_list_courier_messages | read-only | List emails/SMS sent by Kratos with delivery status |
kratos_get_courier_message | read-only | Get courier message details including delivery attempts |
Toolset: recovery
| Tool | Kind | Description |
|---|---|---|
kratos_create_recovery_link | create | Generate an account recovery link (expiresIn Go duration, returnTo URL) |
kratos_create_recovery_code | create | Generate an account recovery code (expiresIn Go duration, flowType browser/api) |
Recovery links and codes are equivalent to full account takeover. Treat them as secrets.
Toolset: analytics
| Tool | Kind | Description |
|---|---|---|
kratos_session_analytics | read-only | Aggregated session statistics (auth methods, assurance levels, devices, browsers) |
kratos_credential_analytics | read-only | Credential type distribution, MFA and passwordless (passkey) adoption |
Both scan up to maxPages pages and report pagesScanned / truncated.
Toolset: health
| Tool | Kind | Description |
|---|---|---|
kratos_health_alive | read-only | Check if Kratos is alive and accepting requests |
kratos_health_ready | read-only | Check if Kratos is ready (database connectivity, dependencies) |
kratos_version | read-only | Get the Kratos server version |
Every tool declares an outputSchema and returns its result as structuredContent alongside the JSON text; errors come back as isError results with { code, message, kratosStatus?, suggestion? }.
List tools take pageSize (1-100, default 20) and pageToken, and return { items, count, nextPageToken }. nextPageToken is absent on the last page; pass it back as pageToken to continue. Tokens are opaque cursors bound to the Kratos instance.
Tools that walk many pages (analytics, kratos_list_sessions with filter) accept maxPages (default KRATOS_MAX_SCAN_PAGES) and return pagesScanned and truncated. When truncated is true the page cap was hit; raise maxPages or pass the returned nextPageToken back as pageToken to resume from the first unscanned page (both analytics tools accept pageToken; an analytics aggregate then covers only the resumed range).
When includeCredential is requested, the config of secret-bearing credential types (password, oidc, saml, totp, lookup_secret, webauthn, passkey) is replaced with "[redacted: set KRATOS_ALLOW_CREDENTIAL_EXPOSURE=1]". Type, identifiers, version and timestamps are kept so an agent can still see what is linked. Set KRATOS_ALLOW_CREDENTIAL_EXPOSURE=1 to return the raw config.
Tools annotated destructiveHint (delete, disable, update, patch, set state) ask the client to confirm via MCP elicitation before doing anything. If the user declines, the tool returns { cancelled: true } and nothing is changed. Clients without elicitation support skip the prompt (the annotation still lets them warn on their own). Disable the prompt with KRATOS_CONFIRM_DESTRUCTIVE=0, or hide destructive tools entirely with KRATOS_READ_ONLY=1.
| URI | Description |
|---|---|
kratos://schemas | All identity schemas ({ schemas: [{ id, schema }] }) |
kratos://schemas/{schema_id} | One identity JSON schema; listed and completable per schema ID |
kratos://config/connection | Non-sensitive connection info: base URL (credentials stripped), auth type, timeout, enabled toolsets, read-only flag, reachability and Kratos version |
List all identities in KratosFind the identity with email [email protected]Is Kratos healthy and ready?Show all active sessions in KratosWhat authentication methods are users using? Show session analytics.Create a recovery link for user with ID abc-1230.3.0 is a minor bump (the package is pre-1.0) that changes several contracts. Update clients and scripts as follows:
| Contract | 0.2.0 | 0.3.0 | Migration | ||
|---|---|---|---|---|---|
kratos_list_sessions input | limit | pageSize (1-100, default 20) + pageToken; maxPages when filter is set | Rename limit to pageSize; there is no alias. Pass nextPageToken back as pageToken to continue | ||
kratos_get_identity credentials | includeCredentials: true returned raw credential config | includeCredential: ["password", "oidc", ...] is canonical; the boolean is accepted as a deprecated "all types" alias (removed no earlier than 1.0.0). The config of secret-bearing types is redacted to "[redacted: set KRATOS_ALLOW_CREDENTIAL_EXPOSURE=1]" by default | Switch to includeCredential; set KRATOS_ALLOW_CREDENTIAL_EXPOSURE=1 only where the client context may hold secrets | ||
kratos_batch_patch_identities result | { results: [{ index, identityId, ... }], summary } | `{ results: [{ action: "create" \ | "error" \ | "unknown", identity?, patchId?, error? }], summary: { total, succeeded, failed } }` (Kratos SDK field names) | Read identity.id instead of identityId; correlate by patchId; summary.total is new |
| Destructive tools (update, patch, set state, extend/disable session, delete identity/credential/sessions) | always returned their result | may return { cancelled: true, message: "Cancelled by user" } as a success result when the operator declines the confirmation prompt; no Kratos call is made | Check cancelled before reading the payload; do not retry on it | ||
| Server version over MCP | hard-coded 0.1.0 | equals the package.json version (0.3.0) | Clients that pinned 0.1.0 in checks should compare against the package version | ||
| Runtime | Node >= 18 | Node >= 20 | Upgrade Node |
Additive changes (no action needed): every tool now carries a title, MCP annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint: false), an output schema and structuredContent; list tools return { items, count, nextPageToken? }; kratos_delete_identity_sessions returns sessionsExisted, kratos_set_identity_state returns sessionsRevoked; new env vars KRATOS_TOOLSETS, KRATOS_READ_ONLY, KRATOS_CONFIRM_DESTRUCTIVE, KRATOS_ALLOW_CREDENTIAL_EXPOSURE, KRATOS_MAX_SCAN_PAGES are all optional.
Symptom: ECONNREFUSED or connection timeout errors
Solutions:
curl http://localhost:4434/health/aliveKRATOS_ADMIN_URL is correct (use Admin API port, typically 4434)Symptom: Authentication errors when calling Kratos API
Solutions:
KRATOS_AUTH_TYPE=api-keyKRATOS_API_KEY with a valid API keyKRATOS_AUTH_TYPE=custom-headers with KRATOS_CUSTOM_HEADERSSymptom: MCP client doesn't show Kratos tools
Solutions:
npx mcp-ory-kratos manually to verify the server startsSymptom: Requests timeout before completing
Solutions:
KRATOS_TIMEOUT_MS (default: 30000ms)# Clone the repository
git clone https://github.com/feedback-loop-ai/mcp-ory-kratos.git
cd mcp-ory-kratos
# Install dependencies
bun install
# Start the MCP server
bun run start# Lint (Biome)
bun run lint
bun run lint:fix # Auto-fix issues
# Type check (src + tests)
bun run typecheck
# Unit tests (hermetic, with coverage) - this is what CI runs
bun run test:unit
# Dependency audit (high severity and above)
bun run audit
# Build the distributable
bun run buildThe integration suite in tests/api/ runs against a real Kratos (pinned to v26.2.0, configured from tests/kratos/). Start one with Docker and run the suite:
docker compose up -d --wait
bun run test:api
docker compose downTo target another instance, copy .env.test.local.example to .env.test.local and set KRATOS_ADMIN_URL / KRATOS_EXPECTED_VERSION (the suite fails fast on a version mismatch). Override the container version with KRATOS_VERSION=v26.x.y docker compose up -d.
CI runs lint, type check, audit and unit tests on every push and PR, plus the integration job against the docker-compose Kratos.
Contributions are welcome! Please follow these steps:
git checkout -b feature/my-feature)bun run lint && bun run typecheck && bun run test:unit)git commit -m 'Add my feature')git push origin feature/my-feature)If you find this project useful, consider sponsoring its development:
Your support helps maintain and improve the MCP Ory Kratos server.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.