# How MCP tool restriction enforcement works, end to end

> Follow one rule through MCP tool restriction enforcement in Elaichi, from the admin who writes it to the final check on the resolved outbound URL.

**TL;DR** An admin writes a restriction on a role or one member. Elaichi then checks it everywhere a person could reach a connector or tool. That means listing, connecting, saving an entry, advertising tools to an AI client, executing a call, and one last check on the resolved outbound URL. Each check is evaluated for the person making the call, not the connection's owner. A restriction change reaches every surface within about two minutes.

## What happens after someone saves a restriction rule

An admin ticks a tool in the Governance screen, saves, and closes the tab. Whether that rule holds depends on how many places a tool call can enter from. A rule that only filtered a tool list would be a suggestion. A rule that only ran at execution would let the model waste turns on tools it can never call. MCP tool restriction enforcement is everything in between.

This post follows one rule from the moment it is written to the last check before the request leaves. It names each place Elaichi checks the rule. It ends with the check that reads the fully resolved outbound URL rather than the tool's name.

A restriction is Elaichi's rule about which connectors and tools a role or a member may reach. It is not a permission, which is what a role lets a member do. It is not a share, which is who may use a given connection or toolbox. [Keeping those three layers apart](/blog/roles-restrictions-acls-three-mechanics/) is most of understanding what follows.

## What does MCP tool restriction enforcement check first, the name or the operation?

It checks the operation for allow rules, and either the name or the operation for blocks. When the rule is saved, Elaichi looks each named connector and tool up in the organization's catalog. A name it cannot find is refused with a 400. Each tool's operation, meaning its resource and method, is stored beside the name.

That stored operation is what MCP tool restriction enforcement uses afterwards. An allow rule matches a tool only by its stored operation. A block rule matches by name or by operation.

The asymmetry is deliberate. A connector's `tool_name` documentation entries replace the tool names Elaichi advertises, matched by resource and method. A tool's label is therefore something whoever edits the connector controls. A block written against a label would be escaped by a rename; a block written against the operation would not. [Why blocks match tool names but allows don't](/blog/block-matches-name-allow-matches-operation/) works through that choice.

A rule has a mode, allow or block, and a target. It carries a list of whole connectors and a list of single tools, each tool written as a connector and tool-name pair. One rule can name connectors from any number of vendors, up to 1000 connectors and 1000 tools.

## Who the rule applies to, and who it cannot

In Elaichi, restriction targets are role or user only; the organization default is the absence of a rule, which means allow everything. A rule written on a role applies to every member who holds it. To keep a team's members off a tool, write the rule on the role they hold, or write one rule per member.

A member is governed by two layers at once: the rules on their role and the rules aimed at them personally. A connector or tool is reachable only when both layers admit it. A personal rule can narrow what a role allows. It never replaces or loosens a role rule. Within each layer, allow rules merge into one allowlist and block rules into one blocklist, and a block always beats an allow.

The empty allow rule is the trap. An allow rule turns on the allowlist for its layer just by existing, whatever it names. An allow rule that names nothing blocks every connector and tool for its target. Its summary says so: "Allows nothing. Every connector and tool is blocked."

The mirror of that trap is a block-only layer. A layer with only block rules has no allowlist, so everything it does not block passes. Blocking some of an app's write tools leaves every write tool the rule does not name reachable. On Microsoft Intune, `msintune_managed_devices_execute_action` stays reachable unless a rule names it.

## Where the check runs

Restrictions are enforced through the same evaluators everywhere a member can reach a connector or tool. [The list of enforcement points](/blog/restriction-resolver-four-enforcement-points/) is longer than four:

- Listing connectors and their tools, in the console, the REST API and the AI catalog operations.
- Connecting or reconnecting an account.
- Forking a connector.
- Saving a toolbox entry or a synthetic tool step.
- Advertising tools to a client: MCP `tools/list`, `search_tools` and toolbox views.
- Executing any tool call.
- The final outbound-request guard.
- Opening a stored tool file that came out of a now-restricted tool.

A toolbox entry or synthetic tool step cannot be saved naming a tool restricted for the person saving it. The write is refused: "Tool \<name\> (\<connector\>) is restricted for your account". An entry saved before the restriction existed is not refused again on later saves. It is withheld when the toolbox resolves, and refused when called.

At advertise time, a restricted tool is left out of `tools/list` and out of a toolbox's runnable tools. The check runs for the person using the toolbox, on the catalog tool's own name and operation. An entry's rename, schema override or frozen parameters change nothing.

Search is the one place a restricted tool is still named. `search_tools` searches the tools the caller may run. It then ranks their restricted tools separately against the same query and returns those matches by name, flagged restricted and with a `restricted_count`. A flagged row carries no description, no schema and no way to call the tool. A connector blocked whole shows as one row for the connector. The person can see what to ask for.

Clients and admin platforms have controls of their own, and they sit elsewhere. Claude's connector tool permissions take Always allow, Needs approval or Blocked. Each setting applies to a whole connector, a category of its tools or a single tool ([Anthropic's help page](https://support.claude.com/en/articles/11176164-use-connectors-to-extend-claude-s-capabilities), checked October 2026). Cursor's beforeMCPExecution hook runs before each MCP tool call and answers allow, deny or ask ([Cursor's hooks documentation](https://cursor.com/docs/hooks), checked October 2026). Power Platform advanced connector policies let an admin block an entire MCP server. Microsoft says granular control over individual MCP tools within an MCP server is currently not available there ([Microsoft's documentation](https://learn.microsoft.com/en-us/power-platform/admin/advanced-connector-policies), checked October 2026). Each of those controls runs on the client or on the admin's own platform. Elaichi's checks run on the endpoint, so they hold whatever client sends the call.

## Every call passes one clamp, evaluated for the caller

Every connected-tool call Elaichi runs passes one restriction clamp, evaluated for the member the call acts as. MCP `tools/call` and `execute_tool`, `toolbox.execute`, the in-app assistant and each step of a synthetic tool all go through the same executor. That executor checks the member's role and personal rules before the connection is used.

Two consequences follow. Restrictions bind whatever client or automation sends the call, not just the console. And they narrow what a member can do through a connection, whatever the credential behind it could do upstream. A toolbox shared at use runs its entries on the delegator's connection, but the restrictions applied are the caller's own. A restriction that blocks a tool for a member blocks it inside every toolbox entry for that member, frozen or not. Restrictions match the catalog tool, not the entry's name.

A restriction is also the wrong instrument for closing the unfrozen path around a locked argument. It would block the frozen entry too. Close that path by not sharing the connection. [Locking AI agent tool arguments](/blog/frozen-parameters-wire-transfer-receiver/) covers the case in full.

Inside a declared fork chain, Elaichi compares what a called tool really sends with what each blocked upstream tool sends. It compares the HTTP method, the path and the host. Renaming a tool does not escape a block written against the original, and neither does renaming the resource and method keys inside a fork's config.

## The last check: the substituted outbound URL

As a final step before a call leaves, Elaichi compares the fully resolved outbound URL with the block rules that name tool-level HTTP operations. Those rules are read from the called connector and its declared ancestors. Every redirect hop is compared the same way. A match is refused.

The word to notice is resolved. By this point path parameters have been substituted and the URL is the one that will actually be sent. The guard never refuses a request merely for reaching a host; a whole-connector block is enforced earlier, before the request is built. Redirect hops are checked too, so a 302 cannot carry a call onto a blocked operation.

A block on a connector also blocks every fork that declares descent from it, including forks of forks. The lineage is walked toward its root for up to 8 hops. A chain that is longer, loops or is broken is refused outright rather than checked partially. Lineage only ever adds blocks: an allow rule naming a connector does not admit its forks, which have to be named on the allowlist themselves.

## What the guard does not catch

For restrictions, [a connector's identity is its own slug plus its declared fork lineage](/blog/declared-lineage-not-dial-host/). The host it calls is deliberately not part of that identity. A connector built from scratch against the same API as a blocked connector is not caught by that block.

This is why `connector:create` is the only permission Elaichi flags as high trust. The role editor shows a "High trust" tag on it, and its description warns that connector governance binds the connector, not the host it calls. Elaichi checks a custom connector's config for shape, not for where it points. Among the system roles, only Org Owner and Org Admin hold `connector:create`.

So a restriction program is only as tight as the list of people who can author connectors. If that list is wide, a block on one Salesforce connector does not stop a new connector aimed at Salesforce.

## When a change takes effect

A member's roles, teams, permissions and resolved restrictions are cached for 60 seconds. The cache keys include a per-organization epoch, and every role, membership, team, restriction and grant change writes a new epoch. A change reaches every surface within about two minutes in the worst case at a remote point of presence. That covers MCP, the console, the REST API and the in-app assistant. Write the rule, then wait, then test.

That is slower than two other things people confuse it with. Revoking a grant, removing or suspending a member, revoking a share and disconnecting an account all take effect on the caller's next call. Each MCP request re-reads the grant, and reads toolboxes, shares and connections fresh.

The resolver fails closed. If it gets an answer in a shape it does not recognize, it refuses the call rather than allowing it. That happens, for example, when an organization's data store is still running older code during a deploy. The refusal is not cached.

## What the refused caller is told

A refused tool call tells the caller where the block comes from, their role or their own account. The wording is "This tool is restricted for your role by an organization policy", or "for your account". It adds that an admin can lift it through an access request. It does not name the rule, who wrote it, why, or what else it covers.

Filing that request needs no permission and is always for the caller themselves. From an AI client, a grant holding `mcp:read` or `mcp:tools` can file one; it does not need write access. A request for one tool on a connector that is blocked whole for the requester is refused. The message names the connector to ask for instead.

Approving is a person's decision in the console, under Governance, then Access requests. It needs `member:manage` plus a fresh identity check, and no AI surface can decide a request. Approving a connector or tool request creates an access grant for that one person: the role's rules are never edited, and nobody else's access changes. A grant only lifts role rules. It never lifts a rule written for the member personally.

## When you do not need any of this

If your whole organization is three people who all get the same apps, restrictions buy you nothing. Everyone holds the same role, and no rule reaches anyone differently. When no restriction rule reaches a member, nothing is restricted for them: every connector and tool passes the check. The honest starting point is one role and no rules, then a rule the first time two people need different reach.

The second case is an app whose own permission model already does the work. A per-person OAuth connection reaches only what that person can reach in the app. If an app's roles already stop a support agent deleting an account, a restriction on top is a second place to maintain the same decision. [Restrict one AI tool or the whole app?](/blog/per-tool-vs-per-app-restrictions/) walks through which cases are worth a rule.

For the wider picture of who decides what across roles, shares and the audit trail, read [MCP governance](/blog/mcp-governance/). For the connectors a rule can name, see the [connector catalog](/connectors/).

## FAQ

### Which targets can a restriction rule name in Elaichi?

A role, or one member. In Elaichi, restriction targets are role or user only; the organization default is the absence of a rule, which means allow everything. A rule written on a role applies to every member who holds it. To keep a set of people off a connector or tool, write the rule on the role those people hold, or write one rule per member.

### How long does a restriction change take to apply in Elaichi?

Allow about two minutes in the worst case. Elaichi caches a member's roles, permissions and resolved restrictions for 60 seconds, under keys that include a per-organization epoch. A restriction change writes a new epoch, so a remote point of presence can take about two minutes to see it. Grant revocation, member removal or suspension, revoking a share and disconnecting an account are different: those take effect on the next call.

### Does blocking a connector in Elaichi also block another connector that calls the same API?

No. For restrictions, a connector's identity is its own slug plus its declared fork lineage; the host it calls is deliberately not part of that identity. A block on a connector also blocks every fork that declares descent from it, up to 8 hops toward the root. A connector built from scratch against the same API is not caught. This is why Elaichi flags the connector:create permission as high trust, and why among system roles only Org Owner and Org Admin hold it.

### Can an AI client see a tool that is restricted for the person using it?

It can see the name but not call it. Elaichi leaves a restricted tool out of the MCP tools/list and out of a toolbox's runnable tools. The search_tools operation then ranks the caller's restricted tools separately and returns those matches by name, flagged restricted, with a restricted_count. A flagged row carries no description, no schema and no way to call the tool. A connector blocked whole appears as one row. Calling the name returns a restriction refusal that explains how to ask for access, not "Unknown tool".

### Whose restrictions apply when a toolbox is shared with someone else?

The caller's own. Every connected-tool call Elaichi runs passes one restriction clamp, evaluated for the member the call acts as. It is not evaluated for the connection's owner or the person who pinned the entry. A restriction that blocks a tool for a member blocks it inside every toolbox entry for that member. Restrictions match the catalog tool's own name and operation, not the entry's label or overrides.

## Read next

- [Where MCP restriction enforcement points actually sit](/blog/restriction-resolver-four-enforcement-points/) — The mcp restriction enforcement points in Elaichi run at eight places, not four, and a ninth check inside the AI client would prove nothing.
- [MCP connector lineage identity and fork blocks](/blog/declared-lineage-not-dial-host/) — MCP connector lineage identity is a connector's own slug plus the forks that declare descent from it, walked toward the root, block-only, and failing closed.
- [Roles, Restrictions and ACLs in MCP: Three Layers](/blog/roles-restrictions-acls-three-mechanics/) — Roles restrictions and ACLs in MCP are three layers: a role says what a member may do, a restriction which tools they reach, an ACL who may use one connection.
