> 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-policies.md).

# Decide MCP tool calls with policies

Put every MCP tool call through a Cedar policy decision — the vocabulary a policy can test, the off, shadow and enforce modes and the order to roll them out in, the no-policy rule, obligations that mi

## What this is, and when you need it

From 2026.10.2 every tool call on the OpenIAM MCP server can be a policy-based access control (PBAC) decision. MCP is the Model Context Protocol, the protocol Claude and other assistants use to call tools on a server. Scopes and entitlements still decide which tools a client may reach at all, and the checks Webconsole applies still apply beneath. A policy decides, on top of that, whether this principal may call this tool on this target right now, and what the result may contain: an agent may read only the people in its sponsor's reporting line, an audit search needs an administrator, e-mail addresses reaching an assistant are masked.

Use this page to switch the decision on, to understand what a policy can test, and to take the shipped policies from shadow to enforced. It assumes you can author and roll out a Cedar policy; if not, start with [Decide access with policies](/administration-guide/pbac.md). You are not in the right place if:

* You want to register an agent, declare its tools or suspend it. See [Govern AI agents as non-human identities](/administration-guide/federation/mcp-server/mcp-agents.md).
* You want to decide which users may use an assistant at all. That is a scope grant, described in [Decide who may use it](/administration-guide/federation/mcp-server.md#decide-who-may-use-it).

## At a glance

|                          |                                                                                                                                                                                                                    |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **You will end up with** | Every tool call recorded with the decision a policy reached, then refused or trimmed when the policy says so                                                                                                       |
| **Where**                | `org.openiam.mcp.pbac.mode` in `mcp.ui.properties`; `org.openiam.pbac.mcp.catalog.url` in the policy service's properties; **Administration → PBAC Policies** for the policies, the simulator and the decision log |
| **You need first**       | 2026.10.2 on the MCP server, the policy service (`pbac-manager`) and Webconsole, with the migration applied, which seeds the seven policies in shadow                                                              |
| **Takes**                | Minutes to switch on; two weeks of shadow traffic before enforcing is the recommendation                                                                                                                           |
| **Reversible**           | Yes — see [Roll it back](#roll-it-back)                                                                                                                                                                            |

## Before you begin

* **Point the policy service at the catalog.** Set `org.openiam.pbac.mcp.catalog.url` in `pbac-manager`'s properties to the server's catalog document, `http://mcp:8080/.well-known/mcp-catalog` inside a Kubernetes namespace or `http://ui_mcp:8080/.well-known/mcp-catalog` in the Docker Compose deployment, and restart the service. The catalog is what gives the policy service the tool groups; without it a policy can still name a tool exactly, but `action in Openiam::McpTool::Action::"openiam:mcp:identity:read"` matches nothing. Set the same URL as `org.openiam.mcp.catalog.url` in `webconsole.ui.properties`, which the policy editor's autocomplete and the declared-tools picker read; empty means no MCP vocabulary in Webconsole.
* **Read the seven shipped policies** under **Administration → PBAC Policies**, named `MCP - …`. They are active in shadow, so they already record what they would decide, and two of them carry values an example tenant edits: the administrator role in `MCP-03` and the working hours in `MCP-07`.
* **Know the rule for agents.** An agent acting alone is refused when no policy permits the call. Read [The no-policy rule](#the-no-policy-rule) before you set enforce mode.

## The three decision points

| Point                | When                                                            | Principal                               | Action                                     | Resource                                                                 |
| -------------------- | --------------------------------------------------------------- | --------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------ |
| **The tool gate**    | Before the tool runs, after its arguments are bound             | The person, or the agent's service user | `Openiam::McpTool::Action::"<tool>"`       | The target the tool resolves, when it has one; otherwise the tool itself |
| **The object guard** | Inside a tool that reads another person, as before this release | The same                                | `VIEW_USER` of the user-management actions | The user being read                                                      |
| **Obligations**      | After the tool runs, on its result                              | Carried by the gate's decision          |                                            | The result                                                               |

The gate is new; `org.openiam.mcp.pbac.mode` switches it. The object guard is the `VIEW_USER` decision the reference page describes under [The tools](/administration-guide/federation/mcp-server/mcp-server-reference.md#the-tools), unchanged except that an agent's service user can now be its principal. The two overlap on purpose: your `VIEW_USER` policies keep governing MCP reads while tool policies are introduced in shadow. Once your tool policies are enforced the guard is redundant, and `org.openiam.mcp.pbac.guard.enabled=false` switches it off.

While the guard runs, it decides first for an agent acting alone that reads another person. The agent's principal is its service user, which holds none of the roles your `VIEW_USER` policies permit unless you grant one, and the shipped `user-management-seed-role9` policy permits only role `9`. So with the guard on, an agent's `get_user`, `list_access`, `check_access` or `list_direct_reports` on another person is refused by the guard, and `MCP-05` decides nothing for it; switch the guard off before you rely on `MCP-05`.

## The vocabulary

### Principals

| Cedar entity           | When                                                                                                                                | Parents                                                                                     |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `Openiam::User::Human` | A person consented to the client, through Claude or any client that signs people in. The agent, when the client has one, is context | The person's entitlements, as on every PBAC decision                                        |
| `Openiam::Nhi`         | An agent acting alone: a `client_credentials` token of a client bound to an AI Agent identity with a service user                   | The service user's entitlements, so `principal in Openiam::Role::"<id>"` works for an agent |

Cedar takes one principal. When a person consented, the person is the principal; the agent is the principal only when nobody is in the loop.

An `Openiam::Nhi` principal carries, besides the attributes every non-human identity exposes to policy:

| Attribute            | Type           | Meaning                                                             |
| -------------------- | -------------- | ------------------------------------------------------------------- |
| `status`             | string         | `A` active, `P` pending approval, `S` suspended, `X` decommissioned |
| `healthScore`        | long           | The health score, `0` until the identity has been scored            |
| `lastUsedDaysAgo`    | long           | Days since the last tool call; `-1` when it has never been used     |
| `relatedHumanUserId` | string         | The sponsor's user ID                                               |
| `riskClassification` | string         | `LOW`, `MEDIUM`, `HIGH` or `CRITICAL`                               |
| `declaredToolIds`    | set of strings | The declared tools, empty when none                                 |
| `authProviderId`     | string         | The bound client's provider ID. Optional: guard it with `has`       |

An `Openiam::User::Human` entity, as a principal or as a resource, gains `managerIds`, the set of its managers' user IDs, always present.

### Actions

Every tool is an action `Openiam::McpTool::Action::"<tool name>"`. The server's catalog groups them by scope, and the policy service makes each group a parent action, so `action in` reaches a whole scope:

| Action group                                            | Members                                                                                                    |
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `Openiam::McpTool::Action::"openiam:mcp:identity:read"` | `whoami`, `get_user`, `search_users`, `list_direct_reports`, `get_organization`, `search_organizations`    |
| `Openiam::McpTool::Action::"openiam:mcp:access:read"`   | `list_access`, `check_access`, `describe_entitlement`, `search_entitlements`, `list_requestable_items`     |
| `Openiam::McpTool::Action::"openiam:mcp:workflow:read"` | `list_my_requests`, `get_request`, `list_pending_approvals`, `get_approval_task`, `list_my_certifications` |
| `Openiam::McpTool::Action::"openiam:mcp:audit:read"`    | `search_audit_events`                                                                                      |
| `Openiam::McpTool::Action::"openiam:mcp"`               | The four groups above                                                                                      |

The policy service reads the catalog every `org.openiam.pbac.mcp.catalog.refresh.seconds` (600). A tool that is not in the catalog has no group, and a policy that names it exactly still works.

### Resources

| Tool                                                             | Resource                                                                                                                                                           |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `get_user`, `list_direct_reports`, `list_access`, `check_access` | The `Openiam::User::Human` named by the argument, or the caller when the argument is absent                                                                        |
| `get_organization`                                               | The `Openiam::Organization`                                                                                                                                        |
| `describe_entitlement`                                           | The `Openiam::Role`, `Openiam::Group` or `Openiam::Resource` the `kind` argument names. The catalog advertises no `target` for it, because the kind varies by call |
| Every other tool                                                 | `Openiam::McpTool::"<tool name>"`, with `scope`, `readOnly` and `group` as attributes                                                                              |

The entities a target resolves to are the ones every PBAC decision already loads, with their attributes and ancestry, so a policy about a user's organization or a role's managed system needs nothing new.

### Context

Every decision the policy service makes carries a `context.mcp` record. On a decision the MCP server did not ask for, the record holds the defaults below, so a policy may read any field without a `has` guard.

| Field             | Type           | Default   | Meaning                                                                                                                                                                                                    |
| ----------------- | -------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `toolName`        | string         | `""`      | The tool being called                                                                                                                                                                                      |
| `toolScope`       | string         | `""`      | The scope the tool belongs to, for example `openiam:mcp:identity:read`                                                                                                                                     |
| `readOnly`        | bool           | `true`    | The tool's read-only annotation; `true` for every tool of this version                                                                                                                                     |
| `clientId`        | string         | `""`      | The OAuth client ID; for Claude the URL of its metadata document                                                                                                                                           |
| `providerId`      | string         | `""`      | The authentication provider's ID                                                                                                                                                                           |
| `clientOrigin`    | string         | `""`      | The host of a client whose client ID is an `https` URL, which is how a client that registered itself from a metadata document is identified: `claude.ai` for Claude. Empty for a client registered by hand |
| `agentNhiId`      | string         | `""`      | The ID of the client's agent record; empty when it has none                                                                                                                                                |
| `agentStatus`     | string         | `""`      | The record's status, `A`, `P`, `S` or `X`                                                                                                                                                                  |
| `sponsorUserId`   | string         | `""`      | The sponsor's user ID                                                                                                                                                                                      |
| `sponsorRoleIds`  | set of strings | `[]`      | The sponsor's role IDs, direct and inherited, kept for five minutes; empty when the access manager could not be asked                                                                                      |
| `declaredTool`    | bool           | `false`   | `true` when the tool is among the agent's declared tools                                                                                                                                                   |
| `principalKind`   | string         | `"HUMAN"` | `HUMAN` or `NHI`                                                                                                                                                                                           |
| `onBehalfOf`      | bool           | `false`   | `true` when a person consented, so the principal is the person                                                                                                                                             |
| `argumentDigest`  | string         | `""`      | The SHA-256 of the arguments as canonical JSON, the same value the audit event records                                                                                                                     |
| `argKeys`         | set of strings | `[]`      | The names of the arguments the call supplied                                                                                                                                                               |
| `targetKind`      | string         | `""`      | `HUMAN`, `ORGANIZATION`, `ROLE`, `GROUP`, `RESOURCE`, or empty when the tool has no target                                                                                                                 |
| `resourceUrl`     | string         | `""`      | The server's resource URL, `https://<OPENIAM_HOST>/mcp`                                                                                                                                                    |
| `contractVersion` | string         | `"1"`     | The version of this record's shape                                                                                                                                                                         |

The context every decision already carries, `contentProviderId`, `timeOfDay` and `dayOfWeek`, is present too; `timeOfDay` is UTC, as [The policy model](/administration-guide/pbac/policy-model.md#what-cedar-can-see) warns.

A policy that limits workflow tools to clients that registered from `claude.ai` reads:

{% code overflow="wrap" %}

```
forbid(principal, action in Openiam::McpTool::Action::"openiam:mcp:workflow:read", resource)
unless { context.mcp.clientOrigin == "claude.ai" };
```

{% endcode %}

## The modes, and the order to roll them out in

`org.openiam.mcp.pbac.mode` in `mcp.ui.properties` is the switch for the tool gate. Restart the server after a change.

| Mode      | What the server does                                                                                                                                                                                                                                                |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `off`     | No gate. The behaviour of 2026.10.2 before this feature; the object guard still applies                                                                                                                                                                             |
| `shadow`  | The gate asks the policy service on every call and always allows. The decision the policies would have reached, and the policies that reached it, go on the audit event and into the metrics                                                                        |
| `enforce` | A deny refuses the call with the tool error `NOT_PERMITTED`: `forbidden by policy` with the policy ID when a forbid matched, `no policy permits the call` when the applied policies permitted nothing; never the policy text. Obligations are applied to the result |

Beneath the server's mode, each policy keeps its own enforcement mode from [Roll a policy out safely](/administration-guide/pbac/roll-out-a-policy.md): a policy in `SHADOW` records what it would decide but does not decide, even when the server is in `enforce`; `PARTIAL` governs a percentage of users; `FULL` governs everyone. The seven shipped policies are active in `SHADOW`.

Roll out per tenant in this order:

{% stepper %}
{% step %}
Set `shadow` and leave it for two weeks. Every call is recorded with its would-be decision. Review the impact view and the decision log, described in [Read the decision log](#read-the-decision-log-and-the-impact-view), until the would-change calls are the ones you intend.
{% endstep %}

{% step %}
Promote the policies that only affect agents acting alone: `MCP-02`, then `MCP-04`. `MCP-02` first, because it is the permit that lets an agent do anything at all once the server enforces.
{% endstep %}

{% step %}
Promote `MCP-03`, `MCP-05` and `MCP-06`, which affect agents and people through agent clients. `MCP-06` changes what every Claude user sees: e-mail addresses are masked.
{% endstep %}

{% step %}
Promote `MCP-01` and `MCP-07`. `PARTIAL` with a percentage is available for the ones that govern people.
{% endstep %}

{% step %}
Set `enforce` and restart the server. Set `org.openiam.mcp.pbac.guard.enabled=false` once your tool policies cover what the `VIEW_USER` guard covered.
{% endstep %}
{% endstepper %}

## The no-policy rule

When no policy applies to a call, the gate decides by the kind of principal, and the two kinds differ:

| Principal             | No applicable policy                                                                                                                                   | Recorded as       |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- |
| A person              | **Allowed.** Scopes, consent and entitlements already decided; a policy only narrows                                                                   | `NO_POLICY_ALLOW` |
| An agent acting alone | **Refused**, with `NOT_PERMITTED` and the message `NO_POLICY: no policy permits this agent to call the tool`. An agent runs only what a policy permits | `NO_POLICY_DENY`  |

{% hint style="warning" %}
**In enforce mode an agent acting alone needs a permit, and the shipped permit is in shadow.** `MCP-02` permits an active agent the tools it declared, but it ships in `SHADOW` like the rest of the pack, and a shadow policy does not count as applying. Promote `MCP-02` to `FULL` before you set `enforce`, or every agent acting alone is refused with `NOT_PERMITTED` on every call, with `NO_POLICY_DENY` on the audit event. A person using Claude is not affected: the rule allows them.
{% endhint %}

The object guard keeps the rule it had before this release, which refuses both kinds when no `VIEW_USER` policy applies. The policy service logs every decision the MCP server asks for, even when no policy governs it, because `org.openiam.pbac.log.nopolicy.invokingsystems` ships as `mcp`; such a `PBAC_REQUEST` event carries the attribute `PBAC_NO_POLICY` set to `true`. So the decision log shows the whole population in shadow, not only the calls a policy touched. Simulations are not logged.

## Obligations on results

A permit can carry obligations, and in enforce mode the server applies them to the result before it leaves the server, on every record the result contains:

| Obligation | Parameters                   | Effect                                                                                                                                                                                                                                                                                                                                                                     |
| ---------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MINIMIZE` | `fields=<a>,<b>`             | Removes the named fields from every record of the result, at any depth                                                                                                                                                                                                                                                                                                     |
| `MASK`     | `fields=<a>,<b>;pattern=<p>` | Replaces the values. `FULL` writes `***`; `LAST_4` keeps the last four characters; `INITIALS` keeps the first letter of each word, with a dot, and an e-mail address's domain, so `jane.doe@example.com` becomes `j.d.@example.com`; `HASH` writes `sha256:` and sixteen hexadecimal characters, so equal values still compare equal. A blank or unknown pattern is `FULL` |
| `LOG`      | `note=<text>`                | Changes nothing in the result; the note is recorded on the call's audit event                                                                                                                                                                                                                                                                                              |

The field names are the ones in [Result shapes](/administration-guide/federation/mcp-server/mcp-server-reference.md#result-shapes), such as `email`, `login` or `managerId`, separated by commas or spaces; the catalog repeats them per tool as `resultFields`. A name no record has is ignored and counted in the server's `openiam_mcp_policy_obligation_unknown_fields_total` meter. A mandatory obligation of any other type refuses the call with `NOT_PERMITTED` and a message that starts with `OBLIGATION_UNSUPPORTED`, the rule the privacy stage already applies.

Obligations are annotations on the policy, in the syntax the privacy obligations use. The shipped `MCP-06` reads:

{% code overflow="wrap" %}

```
@obligation1("MASK") @obligation1_params("fields=email;pattern=INITIALS") @obligation1_mandatory("true")
permit(principal, action in Openiam::McpTool::Action::"openiam:mcp:identity:read", resource)
when { context.mcp.agentNhiId != "" };
```

{% endcode %}

The audit event records the obligations applied in `MCP_OBLIGATIONS_APPLIED`, as `MASK(fields=email;pattern=INITIALS),LOG(note=…)`.

## The starter pack

The migration seeds seven policies in the `pbac` schema, active in `SHADOW` at priority 500. Each is a starting point, written to be read; copy one rather than edit it when your rule differs.

| ID       | Name                                                              | What it does                                                                                                                                                                                                                                                                                                                                    |
| -------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MCP-01` | `MCP - Humans may use the read tools`                             | Permits a person every identity, access and workflow tool; the audit tool is left to the no-policy rule, which allows a person, and to `MCP-03`                                                                                                                                                                                                 |
| `MCP-02` | `MCP - Active agent may call a declared tool`                     | Permits an agent acting alone any tool it declared, while its status is `A`                                                                                                                                                                                                                                                                     |
| `MCP-03` | `MCP - Audit tool needs an admin`                                 | Forbids `search_audit_events` unless the principal, or for an agent its sponsor, holds role `9`. Edit the role ID to your administrator role                                                                                                                                                                                                    |
| `MCP-04` | `MCP - Unhealthy or dormant agents are refused`                   | Forbids every tool to an agent acting alone whose `healthScore` is below 60 or whose `lastUsedDaysAgo` exceeds 90                                                                                                                                                                                                                               |
| `MCP-05` | `MCP - Agent reads people only inside its sponsor reporting line` | Forbids `get_user`, `list_access`, `check_access` and `list_direct_reports` to an agent acting alone when the target user's `managerIds` does not contain the sponsor                                                                                                                                                                           |
| `MCP-06` | `MCP - Identity tools through an agent client mask e-mail`        | Permits the identity tools to any principal through a client that has an agent record, whether or not the agent declared the tool, and masks `email` to initials as a mandatory obligation. Once enforced it governs people using Claude as well as agents acting alone. A suspended agent never reaches it: admission refuses the client first |
| `MCP-07` | `MCP - Agents do not touch workflow outside 06:00-22:00 UTC`      | Forbids the workflow tools to an agent acting alone when `context.timeOfDay` is before 6 or from 22. Edit the hours: they are UTC                                                                                                                                                                                                               |

### Promote a starter policy

Open the policy under **Administration → PBAC Policies**, and change its enforcement mode from `SHADOW` to `FULL`, or to `PARTIAL` with a percentage for a policy that governs people, as described in [Roll a policy out safely](/administration-guide/pbac/roll-out-a-policy.md). The change reaches the policy service at once: see [How a change takes effect](#how-a-change-takes-effect).

## Simulate a tool call

The simulation screen under **Administration → PBAC Policies → Simulation**, described in [Simulate a policy](/administration-guide/pbac/simulate-a-policy.md), offers no MCP action type in 2026.10.2. The simulation API does: a run launched through `POST /rest/api/pbac/simulations`, listed in [PBAC reference](/administration-guide/pbac/pbac-reference.md#rest-api), accepts `actionType` `MCP_TOOL`, a tool name as the action, and an `mcp` record with the fields above, so you can ask what an agent would get for `search_users` against the real policies before you promote one. Fields you leave out take the defaults above; the record is used for the run and not stored with it.

The policy editor helps with authoring: its Cedar autocomplete offers the tool action type, the tools and groups from the catalog, and the `context.mcp` fields, once `org.openiam.mcp.catalog.url` is set for Webconsole.

## Read the decision log and the impact view

The decision log under **Administration → PBAC Policies → Decision Log**, described in [Trace a decision](/administration-guide/pbac/trace-a-decision.md), gains an **MCP** column and four filters: **Invoking system** (**MCP server**), **Agent**, **Tool** and **Gate mode** (**Off**, **Shadow** or **Enforce**), next to the decision filter, so you can answer "what did this agent do last week, and what was refused". The decision served is one of `ALLOW`, `DENY`, `NO_POLICY_ALLOW`, `NO_POLICY_DENY` and `UNAVAILABLE`; the two no-policy outcomes are never folded into "allowed".

The **Policy impact** card on the same screen is the impact view. Enter a **Policy ID** and a time range and choose **Calculate**: it answers how many of the decisions in the range would change, with a sample of the calls, by counting the shadow decisions that named the policy and would refuse the call once the gate enforces it. It is the number to watch in shadow: a policy whose impact is zero is safe to promote and does nothing, and one whose impact is large is the one to read call by call.

Both views read the `PBAC_REQUEST` events the policy service writes and the `MCP_TOOL_CALL` events the server writes; there is no separate store, so the audit retention applies.

## How a change takes effect

Saving a policy, appending a version, making a version current or deleting a policy publishes a change notice that every node of the policy service acts on at once, so a promoted policy decides the next call. Before this release a saved policy took up to six minutes to apply, the cache sweeps described in [Caching and propagation](/administration-guide/pbac/pbac-reference.md#caching-and-propagation); the sweeps remain as the fallback.

The entity caches keep their intervals: an attribute of the agent record, such as its health score, may be read from a snapshot up to 15 minutes old. Two things do not wait for it. The kill switch is applied at admission, before any policy runs, so a suspended agent is refused within the server's 60-second agent cache whatever the policy service holds. And `context.mcp.declaredTool`, which `MCP-02` reads, comes from the server's own copy of the agent record, refreshed every 60 seconds.

## When the policy service is unreachable

The gate waits `org.openiam.mcp.pbac.timeout.ms` (500) for a decision, so a slow policy service never consumes the tool call's budget. After `org.openiam.mcp.pbac.breaker.failures` (5) consecutive failures the gate stops asking for `org.openiam.mcp.pbac.breaker.open.seconds` (30) and reports the outage at once.

| Mode      | A call during the outage                                                                                                                                  |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `shadow`  | Allowed, counted as `UNAVAILABLE` in the metrics and on the audit event                                                                                   |
| `enforce` | Refused with the tool error `UNAVAILABLE` and the message `The access decision could not be obtained`. Nothing is returned, and the assistant is told why |

## Verification

{% code overflow="wrap" %}

```bash
curl -s http://mcp:8080/.well-known/mcp-catalog
```

{% endcode %}

| Check              | Correct result                                                                                                                                                                                                                                                                                 |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The catalog        | JSON with `umbrella` `openiam:mcp`, four `groups` and 17 `tools`, each with its `group`, `readOnly`, `target` and `resultFields`. Run the command from inside the deployment; the gateway publishes no pattern for this path                                                                   |
| The policy service | Its log reports the catalog loaded, and a policy whose head reads `action in Openiam::McpTool::Action::"openiam:mcp:identity:read"` compiles and matches in the simulator                                                                                                                      |
| Shadow mode        | After a tool call, the `MCP_TOOL_CALL` event in **Administration → Log Viewer** carries `MCP_POLICY_MODE` `SHADOW`, `MCP_POLICY_DECISION` and `MCP_POLICY_IDS`, and the decision log filtered to invoking system **MCP server** shows the same call                                            |
| Enforce mode       | An agent acting alone that calls a tool it did not declare receives `NOT_PERMITTED` with `no policy permits the call`, once `MCP-02` is `FULL`; the message names no policy, because none forbade the call. Declaring the tool on the **Agent** tab and calling again succeeds within a minute |
| An obligation      | With `MCP-06` in `FULL`, `get_user` through Claude returns `email` as initials, and the event's `MCP_OBLIGATIONS_APPLIED` names the obligation                                                                                                                                                 |
| The metrics        | `/actuator/prometheus` on a server pod shows `openiam_mcp_policy_decisions_total` with `decision` and `mode` (`SHADOW` or `ENFORCE`) labels, and `openiam_mcp_policy_latency_seconds`                                                                                                          |

## When it goes wrong

| Symptom                                                                                                                                     | Likely cause                                                                                                                                                                                                                                    | Resolution                                                                                                                           |
| ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Every call from an agent acting alone is `NOT_PERMITTED`, and the event shows `NO_POLICY_DENY`, or `DENY` with `no policy permits the call` | The server enforces and no `FULL` policy permits the agent; usually `MCP-02` is still in `SHADOW`. The event shows `NO_POLICY_DENY` only while no policy at all applies to the call; once another policy applies to it, the refusal is a `DENY` | Promote `MCP-02`, or write your own permit                                                                                           |
| A policy with `action in Openiam::McpTool::Action::"openiam:mcp:…"` never applies                                                           | The policy service has no catalog, so the groups do not exist                                                                                                                                                                                   | Set `org.openiam.pbac.mcp.catalog.url` and restart; check the service log for the fetch                                              |
| A policy compiles but errors on evaluation                                                                                                  | It reads an optional attribute without a guard, typically `authProviderId`                                                                                                                                                                      | Guard it with `has`; see [The attribute rule](/administration-guide/pbac/policy-model.md#the-attribute-rule-that-causes-most-errors) |
| `MCP-04` refuses an agent that was just registered or re-scoped                                                                             | Its health score is below 60: a missing backup owner, a purpose with no machine-readable scope or an imminent expiry each cost points, and the policy service may read a snapshot up to 15 minutes old                                          | Fix the identity; the per-dimension breakdown is on its detail page                                                                  |
| `MCP-03` refuses an administrator                                                                                                           | The policy names role `9`, which is not your administrator role                                                                                                                                                                                 | Edit the role ID in the policy                                                                                                       |
| Calls answer `UNAVAILABLE` in enforce mode                                                                                                  | The policy service is down or slow, and the breaker is open                                                                                                                                                                                     | Check `pbac-manager` and its RabbitMQ virtual host; calls resume 30 seconds after it answers again                                   |
| A promoted policy does not apply for minutes                                                                                                | The change notice did not reach the policy service                                                                                                                                                                                              | Check RabbitMQ; the sweep applies the change within six minutes regardless                                                           |
| `NOT_PERMITTED` with a message that starts with `OBLIGATION_UNSUPPORTED` on every call a policy permits                                     | The policy carries a mandatory obligation of a type the server does not apply                                                                                                                                                                   | Use `MINIMIZE`, `MASK` or `LOG`, or make the obligation optional                                                                     |
| The policy editor offers no MCP tools, and the declared-tools picker reports no catalog                                                     | `org.openiam.mcp.catalog.url` in `webconsole.ui.properties` is empty                                                                                                                                                                            | Set it to the same URL the policy service reads and restart Webconsole                                                               |
| An obligation names a field and nothing changes                                                                                             | The field is not in the result's records                                                                                                                                                                                                        | Use the names in [Result shapes](/administration-guide/federation/mcp-server/mcp-server-reference.md#result-shapes)                  |

## Roll it back

{% stepper %}
{% step %}
Set `org.openiam.mcp.pbac.mode=shadow` and restart the server. Every call is allowed again, and the policies keep recording what they would do.
{% endstep %}

{% step %}
To silence one policy, set its enforcement mode back to `SHADOW`, or deprecate it as described in [Roll a policy out safely](/administration-guide/pbac/roll-out-a-policy.md#retire-it-by-deprecating-not-deleting).
{% endstep %}

{% step %}
Set `org.openiam.mcp.pbac.mode=off` to stop asking the policy service at all. The object guard still applies.
{% endstep %}
{% endstepper %}

## Related

* [Govern AI agents as non-human identities](/administration-guide/federation/mcp-server/mcp-agents.md) — the agent record these policies read
* [Connect AI assistants through the MCP server](/administration-guide/federation/mcp-server.md) — the scopes beneath the policies
* [MCP tools, errors, properties and limits](/administration-guide/federation/mcp-server/mcp-server-reference.md) — the catalog document, every property named here, the audit attributes and the metrics
* [Decide access with policies](/administration-guide/pbac.md) — how a PBAC decision is reached
* [The policy model](/administration-guide/pbac/policy-model.md) — what Cedar can see, including the attribute rule
* [Roll a policy out safely](/administration-guide/pbac/roll-out-a-policy.md) — `SHADOW`, `PARTIAL` and `FULL`
* [Trace a decision](/administration-guide/pbac/trace-a-decision.md) — the decision log

*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-policies.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.
