Skip to main content
Glama
fso-datawarrior

lab-mcp-identity-server


🎯 The 60-second version

  • An AI assistant is given five tools that operate a real Okta directory: look up, create, grant, revoke, deactivate.

  • The destructive tools (revoke, deactivate) cannot execute in the model's turn. They write a durable pending request and stop. Release happens on a separate local channel, behind a secret the model never sees. There is no approve, confirm, or resolve tool anywhere on the model's surface, and a test fails the build if one ever appears.

  • The design was built to make one bounded claim true by construction, then attacked on purpose (three adversarial probes) to see whether the claim held.

  • The whole 115-test suite runs with zero credentials (108 at publish, then 115 after concurrency hardening), so you can clone it and inspect the exact seam where the destructive branch stops, without an Okta account.

The claim. For the five enumerated identity tools, even a fully compromised model cannot execute a destructive action alone, because the control lives in the server and the out-of-band approval channel and not in the model's behavior. This holds while the server and that channel are uncompromised and the approver cannot be spoofed. The model can still be hijacked, and can still make the call. The gate constrains the outcome, not the model.

Read the full write-up: Even a Fully Compromised Model Cannot Deprovision a User Alone and its appendices. This repository is a learning artifact, not production software.


Related MCP server: okta-auth

πŸ” Why this exists

The failure mode of AI governance is rarely a missing control. It is a control that exists in name and not in mechanism. A "separate" confirm tool the model could still call. An "append-only" log anyone could edit. A "read" that wrote instructions into the model's context. An "additive" grant that escalated a user to admin. Each named a safe idea while its enforcement lived in a convention the constrained thing could reach.

This lab is a documented case study of catching that gap before shipping: an independent blind review before any code, validation against what is actually on disk during the build, one live run against a real directory, and a deliberate adversarial pass, each stage leaving evidence you can re-check. The full story is in the methodology docs and the whitepaper.

🧱 Architecture: where the control actually lives

flowchart LR
  subgraph outside["OUTSIDE the trust boundary"]
    M["AI model / MCP client<br/>(assume fully compromised)"]
  end

  subgraph inside["INSIDE the trust boundary"]
    S["MCP server<br/>5 tools, no resolver tool"]
    P[("Durable pending store<br/>single-use, TTL")]
    CLI["Out-of-band approver<br/>separate process + secret<br/>the model never holds"]
    LOG[("Hash-chained, signed<br/>audit log")]
  end

  OKTA[("Real Okta directory")]
  LAB1[("Lab 1 SCIM server<br/>downstream system")]

  M -->|"Tier 1-2: get_user, grant, provision (execute + audit)"| S
  M -->|"Tier 3: revoke, deactivate"| S
  S -->|"write pending, return only an id"| P
  S -. "NO approve / confirm / resolve tool exists" .-> M
  CLI -->|"approve or deny with the secret"| P
  P -->|"approved: precondition re-check, then execute"| S
  S -->|"read / write"| OKTA
  S --> LOG
  OKTA -->|"group removal cascades via SCIM (~2.8s)"| LAB1

The destructive branch of every gated action terminates in the pending store and can only be released by the out-of-band approver, which runs in a separate process behind a secret the model never sees. A test pins the registered tool set to exactly the five operational tools and fails the build if a resolver ever appears. Two independent layers make the claim true: the resolver fails closed by construction, and the model has no tool that reaches it.

πŸ› οΈ The five tools

Tool

Kind

Tier

Gated

get_user

read

1

no (audited)

provision_user

additive

2

no (audited)

grant_access

additive

2 or 3

Tier 3 when the target group is protected

revoke_access

destructive

3

yes, out-of-band approval

deactivate_user

destructive

3

yes, out-of-band approval

Tier is a function of the target, not the verb: a grant into an ordinary group flows and is audited, a grant into a protected group is gated exactly like a removal, because adding a user to an administrative group escalates privilege as surely as any removal. delete_user was cut, because in Okta a delete requires a prior deactivation and is irreversible.

⚑ Quickstart (zero credentials)

git clone https://github.com/fso-datawarrior/lab-mcp-identity-server
cd lab-mcp-identity-server
pnpm install
pnpm test        # 115 tests, all green, no Okta account required

The whole suite runs against an in-memory mock directory, so a stranger can clone it, inspect the exact seam where the destructive branch stops, and run every test without any secret. That zero-credential path is a first-class property of the design: a control a stranger cannot inspect and exercise is a control a stranger has no reason to trust.

πŸ”Œ Running against real Okta (optional)

cp .env.example .env       # add your Okta org, service-app key, demo group id
pnpm seed:demo             # creates the demo group and users
pnpm smoke:okta            # 5/5 fixture check
pnpm start:real            # or register scripts/start-desktop.mjs in your MCP client

The go/no-go checklist (docs/MCP-SERVER-CHECKLIST.md) and the recovery runbook (docs/BREAK-GLASS.md) are the operator surface. pnpm approve <id> / pnpm deny <id> is the out-of-band channel. Point the real-mode path only at a throwaway developer org with the demo fixture.

πŸ“Š What we found (the honest part)

This lab scores its own gaps down, not up. A five-category agent-governance self-assessment, conservative and carrying no external audit:

Category

Score

The named gap

Audit trail

74

deletion-evidence is verify-time, not caller-independent; in-process signing key

Policy enforcement

71

code-enforced, not a policy engine

Human oversight

70

single approver, and in the demo a single shared secret, not an authenticated approver

Error handling

66

fails closed, but the mock is synchronously consistent while a real directory is not

Revocation capability

58

the service credential is scoped organization-wide (Okta scopes are org-wide)

Three adversarial probes, with honest denominators:

  • Probe A (gate bypass): 11 of 12 held. The one gap, a blank justification satisfying "justification required," was closed the same session.

  • Probe B (audit integrity): 7 of 10 held. The deletion-evidence family was closed with a named boundary via ADR-0003.

  • Probe C (live hostile-profile injection): one witnessed run where a poisoned read created nothing.

Full write-up in docs/PENTEST-FINDINGS.md.

One approved revoke removes an Okta group membership and, about 2.8 seconds later, deprovisions the same user in a second system entirely, through Okta's own provisioning, verified at the far end. One approval, two systems.

πŸ“– The methodology (how it was built and checked)

The most transferable thing here is not the server, it is the pipeline that produced it. Three curated records tell that story:

  • Assumption and Discovery Ledger, every design assumption the build walked in with, the point it broke, and what the design does instead. The reversals are the primary finding.

  • Build Evidence, what the code actually proved, milestone by milestone, with the monotonic test spine (7 to 115) and commit hashes.

  • Prompt Archive, how the build was driven, the one-unit-of-work-with-acceptance-criteria loop, and two representative verbatim prompts.

The portable heuristic: put the control where the constrained thing cannot reach it, then try to break it from the outside.

πŸ—ΊοΈ Repository map

Path

What it is

docs/whitepaper/

The whitepaper body and appendices

docs/methodology/

The ledger, build evidence, and prompt archive

ADR/

ADR-0001 (out-of-band gate), ADR-0002 (audit fidelity), ADR-0003 (deletion-evidence)

docs/PENTEST-FINDINGS.md

Probes A / B / C, held and gaps

docs/BREAK-GLASS.md

Recovery runbook (kill-switch, restore)

docs/MCP-SERVER-CHECKLIST.md

Live-demo go/no-go

src/

The server, the out-of-band approver, the audit chain, the Okta clients

test/, tests/

The 115-case zero-credential suite

πŸ§ͺ Try to break it

The invitation is the whole point. Clone it, read the architecture, and try to make a destructive action complete without the out-of-band approval. If you find a way, that is a finding worth more than the demo. The probes we already ran, and the gaps we already found and named, are in docs/PENTEST-FINDINGS.md.

πŸ”— The Identity Lab series

  • Lab 1: lab-okta-scim-server, the downstream SCIM target this lab cascades into.

  • Lab 3 (this repo), the AI-directed identity operations server with the out-of-band gate.

A LinkedIn series ("Building the Gate," three parts) and a Dev.to series ("Lab 3 Engineering Notes") tell the story for a general engineering audience.

πŸ–ΌοΈ Visual summary

βš–οΈ License and disclaimer

MIT. This is a demonstration build for learning, not production software. It performs real, destructive identity operations against whatever Okta org you point it at, so run the real-mode path only against a throwaway developer org with the demo fixture. Nothing here should be read as a claim that any system is production-ready, tamper-proof, injection-proof, or secure by default. The one thing it demonstrates is bounded, stated above, and scored openly with its residuals.

Available Tools

5 tools
deactivate_userA

Request user deactivation (Tier 3). Creates a pending approval; does not execute.

ParametersJSON Schema
NameRequiredDescriptionDefault
userIdYesOkta user id
justificationYesRequired justification

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It reveals that the tool creates a pending approval and does not execute immediately, which is critical behavioral context. However, it does not mention any additional traits like who can request, what the response looks like, or whether the request can be canceled, leaving some gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two short, front-loaded sentences: the first states the action and tier, the second clarifies the deferred execution. Every word contributes value with no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (2 required params, no output schema, no annotations), the description covers the core functionality and behavioral distinction. Some details about the approval workflow or return value would improve completeness, but it is adequate for the agent to understand the tool's purpose and basic behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% (both parameters have clear descriptions: 'Okta user id' and 'Required justification'). The tool description adds no additional parameter-level context beyond the schema, so a baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('Request user deactivation') and the resource ('user'), and specifies the scope ('Tier 3'). It also distinguishes this tool by stating it creates a pending approval and does not execute immediately, which differentiates it from sibling tools like revoke_access or provision_user.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for requesting deactivation with Tier 3 approval, but it does not explicitly state when to use this tool versus alternatives (e.g., revoke_access). No guidance on prerequisites or exclusions is provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_userA

Look up an Okta user by id. Returns a sanitized profile or not-found.

ParametersJSON Schema
NameRequiredDescriptionDefault
userIdYesOkta user id to look up

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description must convey behavioral traits. It does disclose that the result is a 'sanitized profile or not-found', which is helpful. However, it does not mention authentication requirements or rate limits, which would be valuable for safe invocation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is highly concise at two sentences, front-loading the key action and resource. Every word adds value, with no redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simplicity (single parameter, no output schema), the description covers the core functionality and return type adequately. It could mention possible error states beyond 'not-found', but overall it is complete enough for a straightforward lookup tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the input schema already describes the sole parameter (userId) as 'Okta user id to look up'. The description adds no additional meaning beyond this, so the baseline score of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states the verb 'look up' and resource 'Okta user by id', clearly differentiating it from sibling tools which are all mutation operations (deactivate, grant, provision, revoke). It also specifies the result format, adding completeness.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

While no explicit when/when-not guidance is given, the sibling tool context makes it clear this is the read-only lookup tool. The description is straightforward and implies use when a user's ID is known. A slight deduction for not explicitly listing alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

grant_accessA

Add a user to a group. Normal groups are Tier 2; protected groups are Tier 3 and fail closed until gated.

ParametersJSON Schema
NameRequiredDescriptionDefault
groupYesGroup name
userIdYesOkta user id
justificationYesRequired justification

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full behavioral disclosure burden. It discloses that protected groups (Tier 3) fail closed until gated, which is valuable. However, it does not indicate required permissions, reversibility, or response behavior, which are important for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two concise sentences with no fluff. The first sentence states the core purpose, and the second adds a key behavioral nuance. All information is front-loaded and efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema and no annotations, the description moderately covers the tool's behavior. It explains the core action and a critical failure mode, but lacks details on success outcomes, error conditions, and differentiation from siblings, leaving some gaps for an agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already describes all three parameters. The description adds no additional parameter-level details beyond the schema, but it does provide context about group tiers, which indirectly relates to the 'group' parameter. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it adds a user to a group, with specific verb and resource. It differentiates between normal and protected groups, providing some context for distinguishing from sibling tools like 'revoke_access', though it could be more explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides context about tier-based failure behavior (Tier 2 vs Tier 3), which aids in usage decisions for different group types. However, it does not explicitly state when to use this tool versus siblings like 'provision_user' or 'revoke_access', leaving room for ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

provision_userA

Provision a new Okta user in STAGED status (Tier 2, additive, audited).

ParametersJSON Schema
NameRequiredDescriptionDefault
loginYesUser login (email)
displayNameYesDisplay name
justificationNoOptional justification

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must disclose behavioral traits. It mentions the staged status and that it is additive and audited, but it omits details like idempotency, error behavior, or permission requirements. Provides baseline but not comprehensive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, well-structured sentence that front-loads the action and key details. Every word adds value, and following sentences are unnecessary for this simple tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (3 parameters, no output schema, no nested objects), the description covers purpose, status, and operational properties. It lacks error handling or prerequisite details but is largely complete for agent invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% description coverage with meaningful property descriptions. The tool description adds no additional parameter-level information beyond what the schema already provides, so it meets the baseline but does not exceed it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Provision', the resource 'new Okta user', and specifies the status 'STAGED', along with context (Tier 2, additive, audited). This distinguishes it from siblings like deactivate_user or grant_access, making the tool's purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies this tool is for initial user creation but does not explicitly state when to use it versus alternatives. No exclusion or when-not-to-use guidance is provided, though sibling names help infer context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

revoke_accessA

Request revoke of group membership (Tier 3). Creates a pending approval; does not execute.

ParametersJSON Schema
NameRequiredDescriptionDefault
groupYesGroup name
userIdYesOkta user id
justificationYesRequired justification

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description discloses key behavior: it is a non-executing request that requires approval. Additional details like follow-up actions or prerequisites are missing, but the core behavioral trait is clear.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences efficiently convey purpose and key behavioral constraint with no redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the low complexity (3 required params, no nested objects, no output schema), the description covers purpose and behavior adequately. It lacks mention of return values or prerequisites, but these are not critical for a simple request tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with basic parameter descriptions. The tool description adds context ('group membership', 'Tier 3') but does not enhance understanding of individual parameters beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool requests revocation of group membership and contrasts with sibling tools like grant_access by specifying it creates a pending approval without immediate execution.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description notes the tool creates a pending approval and does not execute, providing context for use in approval workflows, but does not explicitly state when not to use or list alternatives like immediate revocation methods.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.1.0
    • First observeddeactivate_user
    • First observedget_user
    • First observedgrant_access
    • First observedprovision_user
    • First observedrevoke_access

TDQS

A4/5.0

Scored across 5 tools

Disambiguation5/5

Each tool targets a distinct action (provision, get, grant, revoke, deactivate) with no overlap. The descriptions clearly differentiate execute vs. pending approval operations.

Naming Consistency5/5

All tools follow a consistent verb_noun pattern using snake_case (e.g., deactivate_user, grant_access).

Tool Count5/5

Five tools cover the essential identity management operations without being excessive or insufficient for the stated purpose.

Completeness4/5

Core lifecycle is covered (provision, read, group grants, deactivation) but lacks update user profile or password reset, leaving a minor gap.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    An MCP server for managing Okta users (CRUD operations) with full OAuth 2.1 compliance, enabling secure integration with Claude Desktop and other MCP clients.
    -
  • A
    license
    A
    quality
    C
    maintenance
    MCP server for Okta authentication, enabling AI agents to log in, check sessions, list, delete, and get cookies using saved Okta sessions.
    5
    4
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP server for managing Ory Kratos identities, sessions, and authentication flows, enabling AI assistants to perform identity management tasks via natural language.
    16
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server that provides human-in-the-loop approval for risky AI agent actions, with durable state and audit logs.
    MIT