Skip to main content

Team Access

Concepts

Access Bindings map who (a team, user, or agent) may use what (whole MCP servers, or individual tools across servers) — with a decision mode, optional input/output conditions, and rate limits — without hand-writing policy for the common cases.

What an Access Binding is​

An Access Binding is a first-class object, not a policy you write by hand. New bindings express every principal in Subjects. Existing bindings that used the legacy Groups field keep those values when loaded and show them as group subjects when you edit.

FieldDescription
groupsLegacy IdP group references. Existing values remain effective and are preserved; new bindings use subjects instead.
subjectsPrincipals for new bindings: team, group, user (email), agent (ID, trailing * glob allowed), or agentLabel (key + value). When subjects are present, they replace the legacy groups field. A legacy groups-only binding falls back to those groups and displays them as group subjects when edited. Subjects are optional: a binding with none grants access only to workloads that reference it. See Binding subjects.
serversMCP servers (datasources) this binding covers. Its effective tool selectors are its own toolSelectors plus those of the enabled Toolsets it references. With no effective selector, each server is granted whole. With any, the servers grant no tools on their own and only set where conditions and scopes apply.
toolSelectorsOptional narrowing to individual tools, across servers. Each selector names servers (or "*" for all) and tools — exact names or simple globs like read_*. A binding with no selectors grants the whole server, exactly as before.
scopesOptional narrowing of allowed tool scopes. Enforced at both token issuance and per tool call — a request whose scope exceeds the binding's allowed scopes is denied.
decisionModeAllow, Audit, Warn, Hold (require-approval on the wire), or Deny — see decision mode mapping below. With toolSelectors set, the mode applies to the selected tools only.
conditionsOptional CEL expressions that must also evaluate to true, each with its own denial message. They apply at token issuance for the binding's servers and to calls on those servers. A binding with effective tool selectors and no servers applies them, for every subject, at token issuance for the servers its selectors cover and to calls on the selected tools. A condition that fails at issuance denies the token before any tool is called.
inputConditionsOptional CEL conditions on the request — typically tool arguments (object.tool.arguments). A violation always denies, regardless of the binding's decision mode.
outputConditionsOptional CEL conditions evaluated on the tool's response (object.mcp.response.* — Access Bindings compile against MCP tool responses only). A violation denies the response before it reaches the agent.
rateLimitsOptional call-rate limits — see Rate limits below.
modelSelectorsOptional LLM model grants. Each selector names models (exact names or globs like claude-*) and either an optional route or optional resolved upstream providers and upstreams (exact names of configured providers). With decision mode Allow they grant the models, and a route steers the grant's callers to that model route; with another mode they apply that mode to the models. See Model selectors.

A binding needs at least one server, tool selector, Toolset, or model selector.

The binding editor​

The Access Binding editor has these sections in Setup → Settings → Access Bindings. In a Blueprint's Access section, Edit binding opens the same editor for the binding the Blueprint selects. A new binding that you create in a Blueprint has only a name, Models, and MCP tools; set its subjects and Advanced options later in Settings:

SectionWhat you set
Applies toThe subjects: teams, IdP groups, users, agents, and agent labels. Leave it empty for a binding that only Blueprints and workloads use.
ModelsThe models the binding grants, chosen from the models your providers advertise. On an allow-mode binding, each grant has a Via route list; see Grants that name a route. Below it, Other model limits for these callers shows, read-only, the org ceiling, team and personal access token model scopes, and virtual-key allowed models that also apply to the binding's subjects.
MCP toolsFor each connected server, All tools, Selected tools, or None, plus tool patterns.
AdvancedDecision mode, OAuth scopes, conditions, and rate limits. It also lists the Toolsets the binding already references, read-only.

See Control MCP tool access for the steps, and Manage access by team for layering bindings across teams.

Applies to also lists the Workload Blueprints that select the binding. Their workloads get the binding's grants along with its subjects, and the Access Bindings list shows the Blueprint count in its Applies to column.

Grants that name a route​

An allow-mode binding's model grant can name a model route. The binding's subjects then reach that route for the granted models, instead of the most specific route. Via route offers only routes whose model patterns cover the grant's model; leaving it empty, shown as Most specific route (…), keeps the most specific route.

  • When several grants cover a caller and a model, a grant through the caller's workload wins, then one through its Workload Blueprint, then a team, group, user, agent, or agent-label grant, then Everyone.
  • Saving a binding is refused with 409 route_grant_conflict when another binding grants the same subject the same model pattern at the same level through a different route.
  • While no binding grants any model, every caller is granted every model. Once one does, a caller without a grant for the requested model is refused with MODEL_DENIED.

See Access Bindings steer routing and Steer a team through a route.

Owned bindings​

Most bindings are shared: you create them on the Bindings tab, or while you create a Workload Blueprint, and any Blueprint can select them. One kind of binding has an owner instead. The binding that Promote creates belongs to one workload registration (owner kind workloadRegistration). Only that workload can reference it, and the Access Bindings page and API refuse to edit or delete it (409 owned_binding). Change it by promoting recommendations. On a workload's AI access tab, this binding appears as Extra access for this workload.

Names, history, and deletion​

A binding's name is unique and fixed after you create it. AIControls refuses a rename with 400 binding_name_immutable. A binding that a Workload Blueprint selects is live: an edit applies to every Blueprint and workload that uses it, after an impact preview and a confirmation. Each create, edit, and restore appends a revision to the binding's history, and Restore returns the binding to an earlier revision through the same preview. You can't delete a binding that a Blueprint selects. AIControls refuses the delete with 409 binding_in_use and lists the Blueprints. See Review and restore a binding change.

Workloads that reference a binding​

A Workload Blueprint selects one Access Binding, and a registration's override can reference more by ID. Each workload that uses a binding becomes a derived subject of the binding: the binding's gates, tool grants, conditions, scopes, decision mode, model selectors, and rate limits apply to the workload exactly as they apply to the binding's own subjects. Derived subjects key only on the workload's verified ServiceAccount identity and its coherent registration snapshot, never on workload names or labels. See Binding subjects.

Binding contents are live. Saving an edit to a binding changes the access of every workload that uses it on its next call, without a rollout. The binding editor's Used by panel lists the Workload Blueprints and registrations that use it, and the impact preview shows what the edit does to each workload before you confirm. Each row on the Access Bindings page shows "Used by N Blueprints and M workloads" for the binding. Disabling a binding that workloads use asks you to confirm; afterward it grants nothing.

A workload's own deny lists only narrow access and beat any referenced grant. See Access policy.

Tool-level grants​

Tool selectors answer the granular question — "this team may use get_issue and list_pull_requests, nothing else on that server" — and the cross-server one — "this agent may use any read_* tool anywhere":

  • Tool names are the per-server names shown in the tool inventory, never a prefixed form — renaming a server's tool prefix cannot orphan a grant.
  • deny-mode selectors always win: if one binding grants a tool and another denies it for the same caller, the call is denied.
  • An allow-mode binding with selectors doesn't emit a blocking rule of its own — its grants feed the server's unlisted-tool posture below.

Unlisted-tool posture (default deny)​

Each MCP server has a tool default action — what happens when a tool has no explicit grant:

  • allow (default): tools not named in any binding remain reachable, subject to your other policies.
  • deny: only tools granted by an enabled binding are reachable; everything else on that server is denied with a clear message. Granting a whole server (a binding with no selectors, or a selector with tools "*") satisfies the posture for all its tools.

Set it per server — see Control MCP tool access. Combined with the server-level rule below, this gives a full default-deny-then-allowlist configuration.

Seeing effective access​

Two complementary views compose bindings, tool selectors, and posture into a single answer, so you never have to work it out by hand:

  • Setup → Settings → Access Bindings → Access Matrix — pick one identity (team/user/agent/label) and see its effective allow/deny across every server and tool.
  • Inventory → MCP Servers → (a server's) Access tab — pick one server and see every identity's effective allow/deny across its tools.

Both read the same bindings, toolsets, and posture, so a grant reads identically in either view.

Rate limits​

A binding can cap how often its tools are called: calls per window (1m, 1h, or 1d), counted per user, agent, team, label, or all (everyone combined), optionally scoped by a selector to a subset of the binding's tools. Counts are shared across all AIControls replicas, so the limit is exact fleet-wide. On exceed, onExceed picks the response: deny rejects the call (HTTP 429, audited with the policy name binding-rate-limit:<binding>), warn lets it through and raises a rate_limit_exceeded notification, or require-approval parks each over-limit call in the Approvals queue ("approve to burst"). See Rate-limit MCP tools.

Default is no access​

A server has no access until a binding says otherwise. Creating the first binding for a server doesn't loosen anything that was previously open — it's the mechanism that grants access in the first place. A team with no binding covering (team, server) is denied by default, not allowed.

Where bindings enforce​

PointWhat happens
Token issuanceWhen a developer's tool requests a Cross-App Access token for a server, AIControls checks whether any binding covers (the caller's team, that server), that the binding's conditions hold, and — if the binding sets scopes — that the requested scope doesn't exceed it. A binding with no servers covers the servers its tool selectors name. No covering binding, a failed condition, or a scope request that exceeds it, means the request fails with invalid_grant before a token is ever minted. Bindings that carry only model selectors don't cover an MCP server, so they neither grant nor block token issuance.
Per tool callThe binding's decisionMode, conditions, and scopes are evaluated alongside your existing policies on every call to a bound server.

Because the check happens at token issuance, an unauthorized team is stopped at the earliest possible point — it never ends up holding a token it can't use.

Decision mode mapping​

decisionMode isn't a separate enforcement engine — each value maps directly onto the same decisions every other AIControls policy produces:

decisionModeResulting decision
AllowAllow
AuditAudit
WarnWarn
HoldHeld (routed to the Approvals queue; require-approval on the wire)
DenyDeny — invalid_grant at issuance, denied at the tool call

See Decision Types for what each of these means downstream.

Compiled to CEL, not a second engine​

Every binding compiles internally to the same CEL evaluation the policy engine already runs for every other request — bindings are a structured way to express a common access pattern, not a parallel access-control system. Editing a binding in the UI is equivalent to editing the CEL rule it compiles to; there's exactly one place a request's fate is decided.

The binding editor shows the compiled policies live, and each synthesized policy has a predictable name you'll see in audit events:

Policy nameWhat it enforces
mcpaccessbinding-gate:<server>The per-server default: callers matching no binding for the server are denied
mcpaccessbinding:<binding>The binding's own decision mode (whole-server bindings)
mcpaccessbinding:<binding>:toolsThe decision mode scoped to the binding's tool selectors
mcpaccessbinding:<binding>:conditions / :scopesCEL condition refinements and scope narrowing
mcpaccessbinding:<binding>:input / :outputInput (request) and output (response) condition refinements
mcptooldefault:<server>The server's unlisted-tool deny posture
accessbinding:<binding>:llm-allow-markerAn allow-mode binding's model grant (sets the access match on the LLM plane)
accessbinding:<binding>:llmAnother decision mode applied to the binding's model selectors
workloadaccess:<id>:deny:mcp / :deny:llmA workload's own tool or model deny list
binding-rate-limit:<binding>A rate-limit denial (enforced by the counter engine, named like a policy for audit traceability)

Precedence​

When several rules apply to one call, the most restrictive outcome wins: an explicit deny beats any allow; Hold beats Warn beats Allow. Rate-limit denials and the unlisted-tool posture deny independently of decision modes, and a drifted or quarantined tool is blocked regardless of any grant.

Audit trail​

Every audit event for a call to a bound server records binding_name — the specific binding that permitted or governed it — alongside the existing grant_jti for the Cross-App Access grant that authorized the call. See Audit Log.

Deleting a legacy organization removes its group mappings and repairs affected user memberships. Users with no remaining team move to default. Legacy organization records are removed even if the corresponding team is already absent. These changes and their configuration audit record succeed or roll back together; repeating a completed deletion creates no additional audit record.

See also​