Skip to content
AgentEnv Framework

RBAC

Decide which agents access which tools

Tool Access by Role

Set the rules before the agents connect, and every agent's tools/list and calls follow its role; change them mid-run, and the next call sees the change, so an agent has to find another way.

The gateway keeps rules that turn tools off or on for a role, and applies them to every tool it serves. They come with the gateway topology, on the card as urn:agentenv:disable-tool/v1 and urn:agentenv:enable-tool/v1, so email and contacts know nothing of roles.

The Caller's Role

A caller names its role in the AgentEnv-Role header, over MCP and /step alike, and a request without the header is default. The gateway takes the header as given: a role decides what an agent is offered, not who may connect.

Disable and Enable Tools

POST /tools/disable and POST /tools/enable take a role and its tools, a list of names or "*" for all of them, and answer with that role's rules:

Terminal
curl -sS -X POST http://localhost:61854/tools/disable -H 'content-type: application/json' \
  -d '{"role": "reviewer", "tools": ["send"]}'
Output
{"role": "reviewer", "disabled": ["send"], "allowed": []}

A body without a role, or with tools that is neither a list of names nor "*", gets a 400. Tool names aren't checked, so a rule can name a tool the instance doesn't serve yet.

What a Role Sees

A tool that is off for a role is left out of that role's tools/list over MCP and list_tools over /step. If the agent calls it anyway, the call never reaches the server: /step answers 403, and MCP returns the same message as a tool error with isError: true.

Terminal
curl -sS -X POST http://localhost:61854/step \
  -H 'AgentEnv-Role: reviewer' \
  -d '{"action": "call_tool", "tool_name": "send", "arguments": {"to": "sam.lee@example.com", "subject": "Q3 plan", "body": "Draft attached."}}'
Output (403)
{"error": "Tool 'send' is disabled for role 'reviewer'"}

The rules cover the gateway's own tools too, such as get_time. The gateway doesn't tell an agent that its tools changed: its next list shows the change, and every call is checked when it arrives.

How Rules Combine

For each call, the gateway looks for four rules in order, and the first one it finds decides. A tool that none of them names is on.

OrderRule
1The role's rule for the tool
2The role's rule for *
3Role *'s rule for the tool
4Role *'s rule for *

Enabling doesn't remove a rule, it overwrites it. Enabling a tool after disabling it leaves an allowed rule, which comes before any rule of role *.

Wildcards overwrite more. Tools "*" replace all of the role's rules with one, role "*" also writes its rule into every role that has rules, and "*" for both replaces every rule of every role.

Read the Rules

GET /state lists each role's rules next to the servers and their tools. The rules live in the gateway's memory, and no request deletes one: sending "*" for both with enable leaves a single rule that turns every tool on.

GET /state (roles)
{
  "roles": {
    "*": {"disabled": ["send"], "allowed": []},
    "reviewer": {"disabled": [], "allowed": ["send"]}
  }
}

From a Task

The modify_env_tool_access step sends one of these requests to a deployed env. It takes env_id, action (disable or enable), role and tools, all required; tools is always a list, and ["*"] means all tools.

The reviewer-no-send step
{"id": "reviewer-no-send", "type": "modify_env_tool_access", "env_id": "office", "action": "disable", "role": "reviewer", "tools": ["send"], "depends_on": [{"task_step_id": "office"}]}

The step finds the endpoint on the instance's card and fails before sending anything if the card doesn't list the action. It records the role's rules afterwards in metadata["tool_access_changes"].

One Role per Agent

deploy_agent's role gives an agent its role, through the agent's urn:agentenv:agent-config/v1 when that lists role among its fields. The SDK hands it to run() as request.config.role, and the agent's own code sends it in AgentEnv-Role on its calls to the gateway:

Two agents on one office
[
  {"id": "assistant", "type": "deploy_agent", "env_ids": ["office"], "agent_name": "assistant", "role": "assistant", "depends_on": [{"task_step_id": "office"}]},
  {"id": "reviewer", "type": "deploy_agent", "env_ids": ["office"], "agent_name": "reviewer", "role": "reviewer", "depends_on": [{"task_step_id": "office"}]}
]

With reviewer-no-send after them, both agents share one instance, and only assistant can send. The verify_a2a_role step checks that an agent accepts a role and reads it back, not that it sends the header. The CLI that build_mcp_cli builds sends the value of AGENT_ENV_ROLE, or cli when that is unset.

Change Access Mid-Run

A trigger's permission action sets the same rules when its condition fires, so a role can gain or lose a tool partway through a run. Triggers covers the conditions and the register_env_triggers step.

A permission action
{"type": "permission", "action": "disable", "role": "assistant", "tools": ["send"]}

Without the Gateway

In the server topology nothing sits in front of your server, so there are no rules unless it implements the two extensions itself. modify_env_tool_access calls whatever the card lists, so the same step drives either; Env Topology Extensions compares the two cards.

Last updated on

Ask a question · Report an issue

On this page