> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/administration-guide/federation/mcp-server/mcp-agents.md).

# Govern AI agents as non-human identities

Give every MCP client an AI agent record in the non-human identity inventory, register an agent that acts with no person behind it, declare its tools, read its usage and drift findings, and suspend it

## What this is, and when you need it

Every client of the OpenIAM MCP server has an **agent record**: a non-human identity (NHI) of type AI Agent, bound to the client's OAuth 2.0 registration. MCP is the Model Context Protocol, the protocol Claude and other assistants use to call tools on a server. The record is what makes an assistant governable the way a service account is: it names a human sponsor, carries an expiry date and a health score, lists the tools the agent declares it needs, records when it was last used, and can be suspended in one action that also revokes the client's tokens.

Two kinds of agent share the record. An assistant that acts for a signed-in person, such as Claude, gets its record automatically on the first sign-in; the person stays the identity every tool call runs as, and the record is context. An agent that acts alone, with no person in the loop, such as a compliance platform that reads OpenIAM on a schedule, is registered by you: it signs in with its own client credentials and runs as a **service user** the record owns, so its entitlements, its audit trail and its certification look like any other user's.

You are not in the right place if:

* You are deploying the MCP server or connecting Claude for the first time. Start with [Connect AI assistants through the MCP server](/administration-guide/federation/mcp-server.md). A Claude client's record needs nothing from you beyond one property, described below.
* You want to decide which tools an agent may call, and with what data. That is a policy decision; see [Decide MCP tool calls with policies](/administration-guide/federation/mcp-server/mcp-policies.md).
* You are registering a service account, key or bot that does not use the MCP server. Follow [Register a non-human identity](/administration-guide/nhi/register-an-nhi.md).

## At a glance

|                          |                                                                                                                                                                                                                                                                             |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **You will end up with** | One AI Agent record per MCP client, listed under **Webconsole → Non-Human Identities → Inventory** with the **Agents** filter. For an agent that acts alone, a client ID and secret for its operator and a service user whose roles decide its scopes                       |
| **Where**                | **Webconsole → Non-Human Identities → Inventory → Register agent**; the **Agent** tab of an identity; `idp.ui.properties` and `mcp.ui.properties` for the properties below                                                                                                  |
| **You need first**       | 2026.10.2 on every component, with the migration applied; the MCP server connected as described in [Connect AI assistants through the MCP server](/administration-guide/federation/mcp-server.md); `org.openiam.oauth.cimd.agent.owner` set before the first Claude sign-in |
| **Takes**                | Minutes per agent                                                                                                                                                                                                                                                           |
| **Reversible**           | Yes — see [Roll it back](#roll-it-back)                                                                                                                                                                                                                                     |

## Before you begin

* **Set `org.openiam.oauth.cimd.agent.owner`** in `idp.ui.properties` to the user ID of an administrator account, and restart the identity provider (IdP). The IdP creates the record of a self-registering client with that account as owner and sponsor, and refuses to register such a client while the property is empty; the IdP log names the property.
* **Decide whether a new Claude client may be used at once.** `org.openiam.oauth.cimd.agent.approval` ships as `auto`, because the allowed origins already decide which clients may register. Set it to `required` if every new agent must be approved by a person first.
* **For an agent that acts alone**, know its sponsor, the scopes it needs and the tools it needs. The sponsor is an accountable person: under the shipped policies the sponsor's reporting line bounds which people the agent may read, and the sponsor's roles decide whether it may search the audit log.
* **Point Webconsole at the catalog.** Set `org.openiam.mcp.catalog.url` in `webconsole.ui.properties` to the server's catalog document, the same URL the policy service reads, as described in [Before you begin](/administration-guide/federation/mcp-server/mcp-policies.md#before-you-begin) on the policies page. Without it the declared-tools picker reports that the catalog is not available.
* **Schedule the two governance jobs.** 2026.10.2 seeds no batch task for the tool-drift job and the orphaned-sponsor job; see [Schedule the jobs](#schedule-the-jobs), which also says how to run either of them at once.
* You hold the rights to register and activate a non-human identity, as described in [Access policies for NHIs](/administration-guide/nhi/access-policies.md).

## How the record works

|                               | An assistant acting for a person                                                                                  | An agent acting alone                                                                           |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| **Examples**                  | Claude.ai, Claude Code, any client that signs a person in                                                         | A compliance platform's scheduled reads, an internal agent, a workflow bot                      |
| **How it obtains a token**    | The authorization code flow, with the person's consent                                                            | The `client_credentials` grant, with the client's secret                                        |
| **Who the tool call runs as** | The person who consented                                                                                          | The record's service user                                                                       |
| **What the record does**      | Supplies context to every policy decision, and is the kill switch                                                 | Is the principal: its declared tools, drift findings, health and certification govern the agent |
| **How the record is created** | By the IdP on the first sign-in of a client that registers itself; or by the wizard, choosing **Human-delegated** | By the wizard, choosing **Agent alone**                                                         |

The link lives on the identity: the record stores the ID of the authentication provider that is the client, and a client has at most one record. On every call the server looks the record up from the token's client and keeps it for `org.openiam.mcp.agent.cache.seconds` (60), then admits or refuses the call:

| The client and its record                                                                              | Result                                                                                                                                                                                                                                                                                                             |
| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| An authorization-code client with no record, while `org.openiam.mcp.agent.required` is `false`         | Admitted as the person, with no agent context: the behaviour of a server before this release                                                                                                                                                                                                                       |
| The same, once `org.openiam.mcp.agent.required` is `true`                                              | Refused with 401 and `The client is not registered as an agent`                                                                                                                                                                                                                                                    |
| The record is a Draft or Pending Approval                                                              | Refused with 401 and `The client's agent is pending approval`                                                                                                                                                                                                                                                      |
| The record is Suspended or Decommissioned                                                              | Refused with 401 and `The client's agent is suspended`. The suspension or decommission also revoked the client's tokens, so a client usually meets `The access token is not valid` first; this rule refuses a token the revocation missed. The client cannot repair either by signing in again, which is the point |
| The record's expiry date has passed                                                                    | Refused with 401 and `The client's agent has expired`                                                                                                                                                                                                                                                              |
| A client-credentials client whose record is Active and owns a service user                             | Admitted as the service user, with the record as principal                                                                                                                                                                                                                                                         |
| Any other client-credentials client: no record, no service user, or a token minted for another subject | Refused with 401 and `The access token was not obtained through a user authorization`, as before this release                                                                                                                                                                                                      |

The text quoted is the `error_description` of the 401. The name of the rule that refused the call, such as `AGENT_REQUIRED`, `AGENT_PENDING_APPROVAL`, `AGENT_SUSPENDED`, `AGENT_EXPIRED` or `GRANT_FLOW`, is not sent to the client: it is the `reason` label of the server's `openiam_mcp_auth_failures_total` meter, as listed in [Refusals answered over HTTP](/administration-guide/federation/mcp-server/mcp-server-reference.md#refusals-answered-over-http).

The IdP applies the same rule at its end. A client whose record is not Active receives no token: the token endpoint answers `invalid_client` with `The client's agent is pending approval`, `is suspended`, `is a draft`, `is decommissioned` or `has expired` as the description. Nor does it get a consent page: the authorization endpoint sends the browser back to the client with `error=access_denied` and the same description. The IdP keeps a record for 60 seconds, like the server, so an approval, a new binding, or a change of status or expiry date reaches the token and authorization endpoints up to a minute later.

`org.openiam.mcp.agent.required` ships as `false` so that clients registered before this release keep working. Set it to `true` once every client has a record; a Claude client registered earlier receives its record on its next sign-in, and a client you registered by hand is bound as described in [Bind an existing client](#bind-an-existing-client).

## How a Claude client gets its record

When a client registers itself from its metadata document, as described in [Let an MCP client register itself](/administration-guide/federation/oauth-and-oidc.md#let-an-mcp-client-register-itself), the IdP creates the agent record in the same request, right after the client:

| Field                                 | Value                                                                                                                                                                                            |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Identity Type                         | AI Agent                                                                                                                                                                                         |
| Name                                  | The client's name as registered, `<client_name> (<host>)`                                                                                                                                        |
| Identifier                            | The URL of the metadata document, which is also the client ID                                                                                                                                    |
| Purpose Statement                     | `MCP client <client_name> calling through the metadata document at <URL>`                                                                                                                        |
| Environment, Risk Classification      | `org.openiam.oauth.cimd.agent.environment` (`PROD`) and `org.openiam.oauth.cimd.agent.risk` (`MEDIUM`)                                                                                           |
| Owner, Backup Owner, AI Agent Sponsor | The account named by `org.openiam.oauth.cimd.agent.owner`. It is the backup owner too: a production record cannot be activated without one, and a self-registered client has nobody else to name |
| Expiry Date                           | Today plus `org.openiam.oauth.cimd.agent.expiry.days` (365)                                                                                                                                      |
| Custom Metadata                       | `ai.invoking_system` = `mcp`, `ai.client_origin` = the host                                                                                                                                      |
| Declared tools, service user          | None. The person's entitlements and the policies decide what the assistant may do                                                                                                                |
| Status                                | Active when `org.openiam.oauth.cimd.agent.approval` is `auto`; submitted for approval, so Pending Approval, when it is `required`                                                                |

With `required`, the person's first sign-in ends at once: the record is created and submitted for approval, then the IdP sends the browser back to the client with `error=access_denied` and `The client's agent is pending approval`, so no consent page is shown and no token is issued. A token issued earlier is refused by the server with 401 and `The client's agent is pending approval`. With the **Agents** filter on, the inventory lists such records under **Agents awaiting approval**, where **Approve** activates one. The person's next sign-in succeeds once the IdP's copy of the record has expired, up to a minute after the approval; a sign-in within that minute still ends with `access_denied`. The approval workflow of [Request and approve registration](/administration-guide/nhi/request-and-approval.md) applies as well. This is the control for a tenant where a new agent must be looked at by somebody before it answers anyone.

## Register an agent that acts alone

{% stepper %}
{% step %}
Open **Webconsole → Non-Human Identities → Inventory** and choose **Register agent**. The wizard has four steps: **Identity**, **Client**, **Tools** and **Review**.
{% endstep %}

{% step %}
Under **Identity**, fill in the record as on [Register a non-human identity](/administration-guide/nhi/register-an-nhi.md): a **Name**, the **Environment** and **Risk Classification**, an **Expiry Date**, a **Purpose Statement**, the **Owner** and the **AI Agent Sponsor**. The **Owner** starts as your own account, shown by its user ID until you choose a person.

The wizard has no **Backup Owner** field, and its **Environment** starts as **Prod**, which needs a backup owner before the record can be activated, as do the **High** and **Critical** risk classifications. Either choose another environment, or add the backup owner on the edit form before you activate the record.
{% endstep %}

{% step %}
Under **Client**, choose **Create a new client**, enter a **Client name**, and choose the **Grant flow** **Client credentials**. The other flow, **Authorization code**, is for a vendor assistant that signs people in but publishes no metadata document: it needs a **Callback URI**, creates no service user, and the rest of this procedure does not apply to it. **Use an existing client** binds a client you registered by hand instead.
{% endstep %}

{% step %}
Under **Tools**, pick the tools the agent declares from the catalog. You can change the list later on the **Agent** tab.
{% endstep %}

{% step %}
Under **Review**, check the summary and choose **Create agent**. The wizard creates the record, the client and, for client credentials, the service user, and binds them to each other. Choose **Open agent** to reach the record's **Agent** tab, which shows the client ID and, once only, the secret: copy it now for the agent's operator, because it cannot be read again.
{% endstep %}

{% step %}
The record is a **Draft**. Activate it, or request approval, from the edit form as described in [Lifecycle and status](/administration-guide/nhi/lifecycle.md). Until it is Active the IdP refuses the client's token requests with `invalid_client`, and the server refuses any token it holds with `The client's agent is pending approval`.
{% endstep %}
{% endstepper %}

### The client the wizard creates

| Setting                      | Value                                                                                                                  |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| **Authorization Grant Flow** | `CLIENT_CREDENTIALS`, or `AUTHORIZATION_CODE` for the other choice                                                     |
| **OAuth Client**             | Request body, with a generated client ID and secret                                                                    |
| **Default Scopes**           | Every `openiam:mcp:*` scope resource the deployment has                                                                |
| **Use Refresh Token**        | Off for client credentials: the agent asks for a new token with its secret when one expires. On for authorization code |
| **Redirect URL**             | The callback URI you entered; `urn:ietf:wg:oauth:2.0:oob` for a client that never redirects                            |
| **Token expiration**         | 60 minutes                                                                                                             |
| **Multiple Active Tokens**   | Allowed                                                                                                                |
| **Signing Algorithm**        | `RS256`                                                                                                                |

The client's resource is made public, so the entitlement check does not refuse its subject. Everything else is as for a client registered by hand under **Access Control → Authentication Providers**, where the client can be edited afterwards.

### The service user

Every agent that acts alone owns one user record, created by the wizard. It is what the agent's tool calls run as, so the organization and delegation scoping the backend applies to any requester applies to the agent, and it is the target user on the agent's audit events.

| Field        | Value                                                                              |
| ------------ | ---------------------------------------------------------------------------------- |
| Login        | `nhi:<NHI_ID>`, on the default managed system                                      |
| Name         | The record's name as the first name, `agent` as the last name                      |
| User type    | NHI service account (`NHI_SERVICE`)                                                |
| Status       | Active; disabled when the identity is suspended, deleted when it is decommissioned |
| Credentials  | None. It cannot sign in to Webconsole, SelfService or any application              |
| Organization | The sponsor's organization                                                         |

### Grant the scopes

The agent receives an OAuth scope only through the service user's entitlement to the scope resource, exactly as a person does: the IdP filters the client's scopes by the service user's entitlements when it issues the token. Create a role for agents, entitle it to the `openiam:mcp:*:read` scope resources the agent needs under **Access Control → Resources**, and add the role to the service user as described in [Grant and revoke access](/administration-guide/identities/grant-and-revoke-access.md). [Decide who may use it](/administration-guide/federation/mcp-server.md#decide-who-may-use-it) explains the four scopes.

The service user's roles also matter to policy. Under the shipped policy pack the audit tool is refused unless the principal, or for an agent its sponsor, holds the administrator role the policy names; see [The starter pack](/administration-guide/federation/mcp-server/mcp-policies.md#the-starter-pack).

## Bind an existing client

A client you registered by hand, as described in [A client without a metadata document](/administration-guide/federation/mcp-server.md#a-client-without-a-metadata-document), is bound from the record's **Agent** tab: choose **Bind client**, then **Use an existing client**, search the client by name and choose **Bind**. The same dialog offers **Create a new client**, which creates one as the wizard does, and **Bind another client** replaces a binding. The edit form's **Agent client** section shows the binding too. For a client on the `CLIENT_CREDENTIALS` flow the service user is created at binding; without one the IdP refuses the client with `invalid_client` and `The client's agent has no service user`.

Binding is refused for a decommissioned record and for a client already bound to another record, and binding the same client again changes nothing. The binding is recorded in the record's audit trail as a `CLIENT_BOUND` lifecycle event. The client's own settings do not change. The IdP keeps its copy of the record for 60 seconds, so for up to a minute after a binding it can still issue a client-credentials client a token for the built-in administrator `0001`, as it did while the client was unbound; request the token again after a minute. A bound client cannot be deleted under **Access Control → Authentication Providers** until its record is decommissioned; the deletion is refused with `AUTH_PROVIDER_BOUND_TO_NHI`.

## Declare the tools an agent needs

An agent declares the tools it needs as declared scope entries on the identity, of resource type `MCP_TOOL` with the tool name as the resource and `READ` as the access right. The **Declared tools** panel of the **Agent** tab fills them from the server's catalog, read from `org.openiam.mcp.catalog.url` in `webconsole.ui.properties`, so you pick names rather than type them; while that property is empty the panel reports that the catalog is not available. A declared tool the catalog does not list is marked **Not in the catalog**.

Declared tools do three things:

* The shipped policy `MCP-02` permits an active agent acting alone to call a tool it declared, and nothing else; see [The no-policy rule](/administration-guide/federation/mcp-server/mcp-policies.md#the-no-policy-rule) for why an agent needs a permit at all.
* Any policy can test `context.mcp.declaredTool` and `principal.declaredToolIds`.
* The drift checker compares them with the tools the agent uses, as described in [Drift findings and health](#drift-findings-and-health).

A change to the list reaches the server within `org.openiam.mcp.agent.cache.seconds` (60). The IdP declares no tools for a Claude client: an assistant acting for a person is governed by that person's entitlements and by policy, not by a list. The drift checker does not make that distinction, so a Claude client's record collects a finding for every tool people call through it; see [Drift findings and health](#drift-findings-and-health).

## Read the Agent tab

Open an AI Agent identity. Next to the tabs every identity has, an **Agent** tab shows what the bound client has been doing:

| Panel                        | What it shows                                                                                                                                                                                                                                                                                                                                                                                                           |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Bound client**             | **Client name**, **Client ID**, **Grant flow**, **Authentication**, **Service user**, **Active tokens** (how many access tokens of the client are live; zero after a suspension) and **Last used** (**Never** until the first call, then a day such as **today** or **3 days ago**; recorded at most once per `org.openiam.mcp.agent.lastused.throttle.seconds`, 300). When no client is bound, **Bind client** instead |
| **Client credentials**       | The client ID and, right after the wizard or the dialog created the client, the secret, shown once                                                                                                                                                                                                                                                                                                                      |
| **Declared tools**           | The list above, with the picker                                                                                                                                                                                                                                                                                                                                                                                         |
| **Tool usage, last 30 days** | **Calls**, **Not permitted** and **Last called** per **Tool**, from the audit log; a tool the agent did not declare is marked **Undeclared**. The window can be widened to a year                                                                                                                                                                                                                                       |
| **Drift findings**           | Each reads "Called `<tool>` without declaring it" or "Declared `<tool>` but has not called it", with the calls and when it was last seen, and its status: **Open**, **Acknowledged** or **Resolved**                                                                                                                                                                                                                    |
| **Suspend agent**            | The kill switch, with its effects spelled out in the confirmation                                                                                                                                                                                                                                                                                                                                                       |

The inventory gains an **Agents** filter, which lists AI Agent identities that have a bound client.

## Suspend or decommission an agent

Suspending or decommissioning the identity is the kill switch. It happens when you choose **Suspend agent** on the **Agent** tab, or **Suspend** or **Decommission** in the header of the identity's detail page; when the expiry job suspends an expired identity; and when the sponsor job suspends an orphaned one. **Suspend agent** asks first, and its confirmation says what follows: the server refuses the agent's next tool call within a minute, its tokens stop working and the client cannot mint new ones, and the record stays suspended until somebody activates it again. The header's **Suspend** and **Decommission** act at once, with no confirmation, and a decommission cannot be undone. Underneath, three things happen that a status change did not do before this release:

1. Every access token and refresh token of the bound client is revoked.
2. The service user, where there is one, is disabled on a suspension and deleted on a decommission.
3. The IdP refuses new tokens, with `invalid_client` and `The client's agent is suspended`, and sends a sign-in back with `access_denied`, while the identity is not Active.

Each outcome is a lifecycle event in the record's audit trail: `TOKENS_REVOKED`, or `TOKEN_REVOKE_FAILED` and `SERVICE_USER_UPDATE_FAILED` when a step could not be completed. A failed step is reported, never rolled back, so read the trail after a suspension you rely on.

The server keeps a checked token for 30 seconds and the agent record for 60, so a call may still succeed for up to 60 seconds after the status change; in practice the revoked token is refused within seconds. The 401 carries `The access token is not valid`, because the token no longer exists, not `The client's agent is suspended`, and the auth-failure meter counts it under the reason `UNKNOWN`. A refresh of the client's tokens is refused with `invalid_client` and `The client's agent is suspended`. When a person is behind the client, Claude reports that the server needs authentication; signing in again ends with `access_denied` and the same description until the identity is activated again.

**Activate**, which the header offers on a suspended identity, returns it to Active with an `ACTIVATED` lifecycle event and re-enables the service user. Revoked tokens are not restored: an agent signs in again, and a person consents again. **Decommission** is terminal: it also clears the binding, after which the client can be deleted.

## When the sponsor leaves

The governance job `ORPHANED_AGENTS` suspends every AI Agent identity whose sponsor is no longer an active user, records a `SUSPENDED` lifecycle event with the comment `Auto-suspended: sponsor inactive` and no actor, and mails the owner on the `NHI_SPONSOR_INACTIVE` template; a record with no sponsor recorded is skipped. The kill switch applies, so the agent's tokens are revoked. To bring the agent back, set a new **AI Agent Sponsor** first, then **Activate** it; without a sponsor the identity does not pass the activation requirements in [Lifecycle and status](/administration-guide/nhi/lifecycle.md#activation-requirements). The job runs only once it is scheduled, or when you run it on demand, as described in [Schedule the jobs](#schedule-the-jobs).

## Drift findings and health

For an AI Agent identity with a bound client, a second drift job, `TOOL_DRIFT`, sits next to the declared-scope drift job described in [Health and governance](/administration-guide/nhi/governance.md). It compares the tools the agent **used**, counted from the `MCP_TOOL_CALL` audit events of the last `org.openiam.nhi.drift.unused.days` (90), against the tools it **declared**:

| Finding                | Meaning                                                                                                                                                                                                           |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `UNDECLARED_TOOL_USED` | The agent called a tool it did not declare within the last `org.openiam.nhi.drift.window.days` (30). A drift finding: either the declaration is incomplete, or the agent is doing more than it was registered for |
| `DECLARED_TOOL_UNUSED` | The agent declared a tool it has not called for `org.openiam.nhi.drift.unused.days` (90). A hygiene finding: remove the declaration                                                                               |

The job treats every AI Agent with a bound client alike, including the record the IdP creates for a Claude client. That record declares no tools, so each tool people call through Claude becomes an `UNDECLARED_TOOL_USED` finding on it, and its **Entitlement fit** loses 5 points per open finding, at most 20. No shipped policy reads that score for a person's call: `MCP-04` governs agents acting alone. The job counts calls by client ID, so a record also inherits the calls its client made before the record was created or bound, with their original dates.

A finding is raised once, mailed to the owner on the `NHI_SCOPE_DRIFT` template, and kept while it holds; it becomes Resolved when the usage no longer supports it, and is reopened if it returns. An acknowledged finding stays acknowledged. Findings are listed on the **Agent** tab and in the identity's **Drift Detection** panel. The job reads the audit log in pages of `org.openiam.nhi.drift.audit.page.size` (500), at most `org.openiam.nhi.drift.audit.max.pages` (40) of them, and keeps the usage it found for `org.openiam.nhi.drift.usage.cache.seconds` (600), so the health score does not sweep the log on every save.

Three health dimensions change for an agent with a bound client. **Activity** starts scoring as soon as the server records a last-used time. **Entitlement fit**, neutral before this release, now costs 5 points per open drift finding, at most 20. A new **Denial rate** dimension costs 10 points when more than a fifth of the agent's calls over the window were refused by a policy, and is not evaluated until the agent has made a call: an agent that is refused often is misconfigured or probing. The score is recomputed when the identity is saved, changes status or is bound, and by the nightly rescore, not when the drift job runs: a finding the job opens or resolves moves the score at the next rescore. The shipped policy `MCP-04` refuses an agent acting alone whose score is below 60 or that has not been used for 90 days, once that policy is enforced.

### Schedule the jobs

2026.10.2 seeds no batch task for `TOOL_DRIFT` or `ORPHANED_AGENTS`, so neither runs until you create one. Follow [Create a batch task](/administration-guide/automation/batch-tasks.md) twice, with the execution type **Execute a Spring Bean**, the bean `nhiGovernanceInitializer.perform`, the job code as the task's parameter, and a nightly cron; the shipped `NHI Dormancy Notice` and `NHI Expiry Auto-Suspend` tasks are the pattern. The sponsor job's mail needs the `NHI_SPONSOR_INACTIVE` template, which 2026.10.2 does not seed either; create it under **Administration → Mail and notifications** from the `NHI_DORMANCY_WARNING` template. It also needs an owner with an e-mail address: the mail to an owner without one is not sent, and the e-mail service logs `Send notification failed. User <ID> has no email address on file`.

To run a job at once, for example while you set the feature up, call the Webconsole REST API from a signed-in Webconsole session as an administrator: `POST /webconsole/rest/api/nhi/governance/run?job=TOOL_DRIFT`, or `job=ORPHANED_AGENTS`. The call needs the session's cookie and its cross-site request forgery (CSRF) token, as every Webconsole call does; a bearer token alone is refused. It answers 200 once the job has run, and the NHI service log reports what the job found, in a line that ends `5 finding(s) raised` or `1 agent(s) suspended`. The same call with `job=HEALTH` runs the nightly rescore. A batch task is still what runs the jobs every night.

## Verification

Request a token for an agent that acts alone, then call `whoami` with it. The client authenticates in the request body here; a client set to Basic authentication passes the ID and secret in the `Authorization` header instead.

{% code overflow="wrap" %}

```bash
curl -s -X POST https://<OPENIAM_HOST>/idp/oauth2/token -d grant_type=client_credentials -d client_id=<CLIENT_ID> -d client_secret=<CLIENT_SECRET> -d 'scope=openiam:mcp:identity:read' -d 'resource=https://<OPENIAM_HOST>/mcp'
```

{% endcode %}

{% code overflow="wrap" %}

```bash
curl -s https://<OPENIAM_HOST>/mcp -H 'Authorization: Bearer <ACCESS_TOKEN>' -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"whoami","arguments":{}}}'
```

{% endcode %}

| Check             | Correct result                                                                                                                                                                                                                                                                                              |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The token request | JSON with `access_token`, `token_type` `Bearer` and the requested scope; no `refresh_token`                                                                                                                                                                                                                 |
| The `whoami` call | A user summary whose `login` is `nhi:<NHI_ID>`: the service user, not the built-in administrator                                                                                                                                                                                                            |
| The audit trail   | **Administration → Log Viewer** shows an `MCP_TOOL_CALL` event whose target user is the service user, with `MCP_PRINCIPAL_KIND` `NHI` and `MCP_AGENT_NHI_ID` the identity's ID                                                                                                                              |
| The Agent tab     | **Last used** reads **today** and **Active tokens** is 1                                                                                                                                                                                                                                                    |
| A Claude client   | Signing in creates an AI Agent record named after the client, Active, with the owner from the property, within one sign-in                                                                                                                                                                                  |
| The kill switch   | **Suspend agent**, wait a minute, repeat the `whoami` call: 401 with `The access token is not valid`, because the suspension revoked the token. The token request then answers `invalid_client` with `The client's agent is suspended`, and the record's audit trail shows `SUSPENDED` and `TOKENS_REVOKED` |

## When it goes wrong

| Symptom                                                                                                                                                                                     | Likely cause                                                                                                                                                        | Resolution                                                                                                                                                      |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A Claude sign-in fails at registration and the IdP log names `org.openiam.oauth.cimd.agent.owner`                                                                                           | The property is empty, so the IdP cannot create the agent record                                                                                                    | Set it to an administrator's user ID and restart the IdP                                                                                                        |
| 401 with `The client is not registered as an agent`                                                                                                                                         | `org.openiam.mcp.agent.required` is `true` and the client has no record                                                                                             | Bind the client, or let a Claude client sign in again so the IdP creates its record                                                                             |
| 401 with `The client's agent is pending approval`                                                                                                                                           | The record is a Draft or Pending Approval: `org.openiam.oauth.cimd.agent.approval` is `required` and nobody approved it, or the wizard's record was never activated | Approve it under **Agents awaiting approval**, shown with the **Agents** filter on, or activate it from the edit form                                           |
| 401 with `The client's agent is suspended`, or with `The access token is not valid` right after a suspension                                                                                | The identity was suspended by an administrator, by the expiry job or by the sponsor job, or it was decommissioned                                                   | Read the identity's audit trail for the reason; renew the expiry or set a new sponsor, then **Activate**                                                        |
| 401 with `The client's agent has expired`                                                                                                                                                   | The expiry date passed and the nightly job has not suspended the identity yet                                                                                       | Extend the expiry date                                                                                                                                          |
| The token endpoint answers `invalid_client` with `The client's agent is a draft`, `is pending approval`, `is suspended`, `is decommissioned` or `has expired`, although the secret is right | The record is not Active                                                                                                                                            | Approve, activate or extend the record                                                                                                                          |
| `invalid_client` with `The client's agent has no service user`                                                                                                                              | The record was bound to a client-credentials client without its service user                                                                                        | Bind the client from the **Agent** tab, which creates the service user for that flow                                                                            |
| A person's sign-in through Claude ends with `access_denied` and `The client's agent is …`                                                                                                   | The client's record is not Active, or became Active less than a minute ago                                                                                          | Approve or activate the record, then wait a minute: the IdP keeps its copy of the record for 60 seconds                                                         |
| The token endpoint answers `server_error` with `The client's agent record could not be checked`                                                                                             | The IdP could not reach the NHI service                                                                                                                             | Check the `openiam-nhi` service and its RabbitMQ virtual host                                                                                                   |
| 401 with `The access token was not obtained through a user authorization` for a client-credentials client                                                                                   | The client is not bound to an agent record with a service user, so its token names the built-in administrator; or the token was minted for another subject          | Bind the client and create the service user, then request a new token. See the warning below                                                                    |
| The token carries fewer scopes than the client is configured with                                                                                                                           | The service user is not entitled to the missing scope resources                                                                                                     | Add the role that grants them to the service user                                                                                                               |
| Every call from an agent answers `NOT_PERMITTED`                                                                                                                                            | In enforce mode no policy permits the call, which for an agent means a refusal: `MCP-02` is still in shadow, or the tool is not declared                            | Promote `MCP-02`, declare the tool, or write a policy; see [The no-policy rule](/administration-guide/federation/mcp-server/mcp-policies.md#the-no-policy-rule) |
| Calls keep succeeding for a minute after a suspension                                                                                                                                       | The server's 60-second agent cache                                                                                                                                  | Expected                                                                                                                                                        |
| The identity cannot be activated for a missing related human                                                                                                                                | An AI Agent needs a sponsor                                                                                                                                         | Set the **AI Agent Sponsor**                                                                                                                                    |
| An agent registered with the wizard cannot be activated for a missing backup owner                                                                                                          | The wizard has no **Backup Owner** field, and its environment starts as **Prod**                                                                                    | Add a **Backup Owner** on the edit form, then activate the record                                                                                               |
| A client-credentials client still receives the `0001` subject just after you bound it                                                                                                       | The IdP keeps its copy of the agent record for 60 seconds                                                                                                           | Request a new token after a minute                                                                                                                              |
| A bound client cannot be deleted; the refusal is `AUTH_PROVIDER_BOUND_TO_NHI`                                                                                                               | Its identity is not decommissioned                                                                                                                                  | Decommission the identity first, then delete the client                                                                                                         |
| The declared-tools picker reports that the catalog is not available                                                                                                                         | `org.openiam.mcp.catalog.url` in `webconsole.ui.properties` is empty, or the Webconsole cannot reach the URL                                                        | Set it to the server's catalog document and restart Webconsole                                                                                                  |
| No drift finding ever appears, and an agent whose sponsor left stays Active                                                                                                                 | The `TOOL_DRIFT` and `ORPHANED_AGENTS` batch tasks do not exist                                                                                                     | Create them as described in [Schedule the jobs](#schedule-the-jobs), or run the jobs on demand from there                                                       |
| The health score does not change after a drift finding is raised or resolved                                                                                                                | The drift job does not rescore; the nightly rescore does                                                                                                            | Wait for the rescore, or run it on demand with `job=HEALTH` as described in [Schedule the jobs](#schedule-the-jobs)                                             |
| The owner is not mailed when an agent is suspended for its sponsor                                                                                                                          | The `NHI_SPONSOR_INACTIVE` mail template is not seeded, or the owner has no e-mail address                                                                          | Create the template, or give the owner an e-mail address; the suspension happened regardless                                                                    |

{% hint style="warning" %}
**The `0001` subject is on its way out.** A `client_credentials` client that is not bound to an agent record still receives a token whose subject is the built-in administrator account `0001`, with every scope the client is configured with, while `org.openiam.oauth.client_credentials.legacy.subject` is `true`. 2026.10.2 ships it `true`; the next release ships it `false`, after which such a client is refused at the token endpoint with `invalid_client` and `The client is not bound to an agent record`. While the subject is still issued, the IdP logs a warning once per client that obtains it. The MCP server never admits these tokens. Bind every client-credentials client to an agent with a service user, or set the property to `false` now to find the clients that depend on it.
{% endhint %}

## Roll it back

{% stepper %}
{% step %}
To stop one agent, suspend its identity. Its tokens are revoked at once and the client is refused within a minute.
{% endstep %}

{% step %}
To readmit clients that have no record, set `org.openiam.mcp.agent.required=false` in `mcp.ui.properties` and restart the server; to stop holding new Claude clients for approval, set `org.openiam.oauth.cimd.agent.approval=auto` and restart the IdP.
{% endstep %}

{% step %}
To remove an agent that acts alone, decommission the identity, which deletes its service user and clears the binding, then delete the client under **Access Control → Authentication Providers**. The records created by the IdP for Claude clients are removed the same way; the next sign-in creates a new one.
{% endstep %}
{% endstepper %}

## Related

* [Connect AI assistants through the MCP server](/administration-guide/federation/mcp-server.md) — deploying the server and connecting Claude
* [Decide MCP tool calls with policies](/administration-guide/federation/mcp-server/mcp-policies.md) — what an agent may call, and what the result may contain
* [MCP tools, errors, properties and limits](/administration-guide/federation/mcp-server/mcp-server-reference.md) — the admission reasons, every property named here, the audit attributes and the catalog
* [Manage non-human identities](/administration-guide/nhi.md) — the inventory the record lives in
* [Lifecycle and status](/administration-guide/nhi/lifecycle.md) — the statuses and transitions, and what suspension does to an agent
* [Health and governance](/administration-guide/nhi/governance.md) — the health score, the drift job and the sponsor job
* [OAuth endpoints, grants, scopes and signing](/administration-guide/federation/oauth-and-oidc/oauth-and-oidc-reference.md) — the `client_credentials` grant and the subject it issues

*Applies to OpenIAM 2026.10.2. Last reviewed 22 October 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/federation/mcp-server/mcp-agents.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
