Identity governance

Role-based access control for AI agents

An MCP server is a target system and its tools are entitlements. From there the role model falls out: four kinds of role, and your IGA sees each of them as a role it already knows how to certify.

I come from identity governance (IGA), so that was my starting point for Straza's role model. An MCP server is a target system. Its tools are the entitlements. I wanted the IGA to assign and review an agent's access using the roles it already understands.

Straza manages agent identities and controls what agents can do. It connects to your IGA for role assignments, enforces access and approval policies on MCP tool calls, and uses hooks in the agent’s harness to apply policies to local shell, file, and network actions.

Out of that I ended up with four kinds of role.

  • Application roles carry the tools of an MCP server.
  • Business roles compose other roles.
  • Approver roles grant no tools and decide approval requests.
  • Straza roles grant administration and self-enrollment permissions.
Four role kinds in separate rows: application binds tools; an agent holds a business role that includes application roles; Ana holds an approver role; Ivan holds a Straza administration role.01 / ROLE KINDSFour kinds of role1ApplicationTool accessAgentholdsgithub-codebinds toolsGitHub MCP2BusinessRole compositionAgentholdsjava-agent-developergithub-codejira-ticketsincludes3ApproverHuman approvalAnaholdsprod-deployersApproveor deny4StrazaAdministrationIvanholdsstraza-adminAdministerStrazaYour IGA assigns membership in all four.
Four kinds of role01 / ROLE KINDSFour kinds of role1ApplicationAgent holdsgithub-codeGrants access to selectedGitHub MCP tools.2BusinessAgent holdsjava-agent-developerIncludes github-codeand jira-tickets.3ApproverAna holdsprod-deployersCan approve or deny requestssent to that approver role.4StrazaIvan holdsstraza-adminCan administer Straza.Approval authority is separate.Your IGA assigns all four kinds of role.
FIG 1 · Four role kinds, each with a different purpose. Your IGA assigns membership in all four.

Application roles

The obvious move is one role per tool. I didn't want that. GitHub's official MCP server exposes about 80 tools in 20 toolsets; give an agent five servers like it and you're at a few hundred roles, and nobody is going to certify those one by one.

So in Straza the application role, the one kind that carries tools, is a pack of them, and I cut a server into a few packs by risk: github-code gets the read and pull-request tools, github-admin the rest. One GitHub server ends up as two or three roles.

Illustrative GitHub MCP server with 80 tools: 20 read and pull-request tools in github-code, and the remaining 60 in github-admin.02 / APPLICATION ROLESOne server, two bundlesGitHub MCP80 toolsEach mark is one tool.github-code20 toolsRead + pull-request toolsgithub-admin60 toolsRemaining toolsIllustrative counts. You choose which tools each role includes.
Illustrative GitHub MCP server with 80 tools: 20 read and pull-request tools in github-code, and the remaining 60 in github-admin.02 / APPLICATION ROLESOne server, two bundlesGitHub MCP80 toolsEach mark is one tool.github-code20 toolsRead + pull-request toolsgithub-admin60 toolsRemaining toolsIllustrative counts.You choose which toolseach role includes.
FIG 2 · One server, two roles. Eighty tools cut by risk: the read and pull-request tools go to github-code, the rest to github-admin.

A role can name its tools, or it can take every tool of a server, including ones the server adds later. The console warns you when you pick the second. I name them.

Nothing forces one server per role. An application role can bind tools from several servers if you want it to. I keep to one server because a few entitlements per target system is the shape your IGA already expects.

Roles grant access. Policies set conditions.

Holding the role is the grant. If an agent holds github-code and no policy says otherwise, the call runs. A policy never grants anything. It names an application role and says which of its tools have to wait for a human, or which shell commands are refused outright. Shell and file calls on the agent's own machine are decided by the same policies; no role carries them.

A policy can only match application roles, and Straza refuses to activate one that names any other kind. That's because the application role is the one that actually carries the tools, so matching it covers everyone who holds it, directly or through a role that implies it. What a policy can say is its own post.

Business roles

Business roles compose other roles, and they're optional. For agent access, a business role composes application roles: java-agent-developer implies github-code, jira-tickets and artifactory-read. It can't bind a tool itself. Try it and Straza refuses, with the reason: a business role composes application roles and reaches tools through them. Straza resolves the whole chain when the agent connects, so an agent holding java-agent-developer reaches every tool of the three underneath.

Business roles can also compose Straza roles. For example, a team role can imply the administration roles of several MCP servers. Each member then administers those servers without receiving platform-wide administration rights.

You don't have to compose in Straza at all. Every Straza role is an entitlement your IGA can assign, so you can do the composition on the IGA side instead: a business role in your IGA that includes the imported application roles, and no business role in Straza. The midPoint setup I ship as a reference does exactly that (midPoint is an open-source IGA). Its access bundles are midPoint business roles that include imported Straza roles.

Compose application roles in Straza or in the IGA. Straza supplies role definitions and the IGA supplies membership.03 / BUSINESS ROLESTwo places to composeIN STRAZAjava-agent-developerStraza business rolegithub-codejira-ticketsartifactory-readIN YOUR IGAJava agent accessIGA business rolegithub-codejira-ticketsartifactory-readorStraza → IGARole definitionsIGA → StrazaRole membership
Compose application roles in Straza or in the IGA. Straza supplies role definitions and the IGA supplies membership.03 / BUSINESS ROLESTwo places to composeIN STRAZAjava-agent-developerStraza business rolegithub-codejira-ticketsartifactory-readorIN YOUR IGAJava agent accessIGA business rolegithub-codejira-ticketsartifactory-readStraza → IGARole definitionsIGA → StrazaRole membership
FIG 3 · Two places to compose. The edges can live in Straza or in your IGA; the roles are born in Straza either way, and who holds them always comes from your IGA.

One direction is fixed: roles are born in Straza. Your IGA imports them and masters who holds them. It can't create one over SCIM.

On the wire a Straza role is a SCIM Group resource. That's SCIM's name for it, and it's still a role. Your IGA reads each one as an entitlement on the Straza system and writes membership back the way it does for any SCIM target. The export carries the role's kind, its servers and its tools, so a reviewer certifying github-code sees which tools that role grants. The sponsor is an attribute on the user.

Approver roles

The third kind grants no tools. An approver role is a set of people whose job is to decide. When a policy holds a call for a human, the rule names the approver roles that decide it. That can go one of two ways.

A hold is for a decision that takes minutes: the agent waits. An MCP call sits in the gateway until someone answers or the hold times out, and a hold nobody answers is a refusal. A shell command isn't held. It's refused with a reference, and when the agent retries it after a human has said yes, it runs.

A ticket is for anything slower. The agent gets a reference right away and moves on. It comes back later, day scale, and the grant covers one exact call. I prefer the ticket for anything that isn't a quick confirmation.

If the rule names no approver role at all, the agent's sponsor decides (the human your IGA names as accountable for that agent). If there's no approver role and no sponsor either, the request is refused.

Say you run an MCP server that can redeploy production over SSH. Bind its tools to an application role, require approval through a policy, and name prod-deployers as the approver role. When the call comes in, every member of that role gets it on their phone, and the first signed decision releases or refuses it. One signature is enough; there's no two-approver rule today. Each decision is signed on the phone by a key that was generated there and never leaves it.

An MCP call waits in Straza. All prod-deployers are notified. Ben signs an approval first and the call runs; deny or timeout refuses it.04 / APPROVER ROLESHuman approval before executionAgentREQUESTStrazaHolds the MCP callApproved → runsMCP serverPROD-DEPLOYERS / ALL NOTIFIEDanabenchenApproval requestBen signs approval firstDeny or timeoutThe call is refused.Admin rolesDo not confer approval authority.
Human approval before execution04 / APPROVER ROLESHuman approval before executionAgent requests an MCP callStraza holds it for approval.PROD-DEPLOYERS / ALL NOTIFIEDAnaBenChenBen signs approval firstStraza accepts the signed decision.Straza runs the MCP callThe tool executes on its MCP server.Deny or timeout: call refused.Admin status alone does not grantapproval authority.
FIG 4 · One held call. Every member of the approver role is rung, the first signature decides. Admin status alone does not put someone in the approver pool. With no approver role in the rule the sponsor decides; with no sponsor, or nobody answering, the call is refused.

Straza's own administrators are not automatically in that pool. Running Straza and approving a deployment are two different permissions. If you want admins deciding, the policy has to name them.

Straza roles

The last kind covers permissions inside Straza itself, including platform administration, MCP server administration, and enrolling an approval device. These roles grant no tools to an agent.

straza-admin administers the whole platform. Straza MCP Global Admin (straza-global-mcp-admin) is narrower: it administers every MCP server, including registering servers, changing their configuration and credentials, and managing which application roles can access their tools. It does not grant platform-wide administration or the right to decide approval requests.

You can also delegate one server. When you register jira, Straza automatically creates its administration role, mcp-admin-jira. Your IGA imports that role and assigns it to the people who should maintain that server. Creating the role does not assign it to anyone.

A holder can maintain that server’s connection and credentials, enable or disable it, inspect its health and logs, test calls, and define the roles that reach it: application roles named after the server, each with an explicit list of its tools, which your IGA assigns like any other role. Holding that role alone does not let them register or remove a server, give an existing role access to it, assign roles, or edit policy. Changes to the command or container runtime also require broader MCP administration rights because they affect what runs on the gateway host.

For example, Petra can create jira-readers and select the Jira tools it includes. This is an application role owned by jira, not a fifth kind of role. It names tools explicitly; it cannot include other servers or automatically include tools added later. Your IGA assigns the role and can include it in a business role.

Once the role has holders, directly or through a business role, Petra cannot change its tool list or delete it. She can define a new role for changed access and let the IGA assign and review it. Server administration alone still grants neither permission to edit approval policies nor authority to approve a request.

Three administration scopes: platform, all MCP servers, one MCP server. Registration creates an admin role that the IGA assigns.05 / STRAZA ROLESAdministration has a scopeWHOLE PLATFORMPlatform adminstraza-adminALL MCP SERVERSMCP global adminstraza-global-mcp-adminONE MCP SERVERServer adminmcp-admin-jiraRegistration creates an admin roleRegister jiraStraza creates the role automatically.mcp-admin-jiraNo holders until assigned.Your IGA assigns it to PetraPetra administers jira only.Petra defines jira-readersAn application role with explicit tools from jira only.Your IGA assigns jira-readers to the agentApproval authority remains separate.
Administration has a scope05 / STRAZA ROLESAdministration has a scopePlatform adminWhole platformstraza-adminMCP global adminAll MCP serversstraza-global-mcp-adminServer adminOnly jiramcp-admin-jiraHow delegation worksRegister jiraStraza creates mcp-admin-jira.Nobody holds the role yet.Your IGA assigns mcp-admin-jiraPetra becomes the server admin.Petra creates jira-readersShe selects tools from jira.This is an application role.Your IGA assigns jira-readersThe agent gains those tool permissions.Once the role has holders, Petracannot change its tools or delete it.Defining roles, assigning access,and approving calls are separatepermissions.
FIG 5 · Registering a server creates its administration role. Your IGA assigns the role; its holder administers that server only. The server admin defines application roles such as jira-readers; the IGA assigns them. Approval authority remains separate.

The role follows the server’s lifecycle. Remove the server and Straza removes its generated administration role, the roles the server owned, and their assignments, each on the audit chain. A team that maintains several servers can receive their administration roles through a business role in Straza or a bundle in the IGA.

The self-enrollment roles remain separate: straza-enroll-mobile lets a person enroll their own phone as an approval device; straza-enroll-browser does the same for a signed-in browser. Enrolling a device does not itself grant approval authority.

To your IGA, all of these are ordinary entitlements it can import, assign, review, and revoke. An access review can distinguish who administers Straza, who administers all MCP servers, and who maintains one server.

Classic RBAC, applied at runtime

This builds on classic RBAC. Your IGA imports, assigns, certifies and revokes all four kinds of role. Approval roles are familiar too: existing IGA workflows already use roles and approver relations to decide who may approve an access request.

In Straza, the approver role decides whether a particular agent tool call may run. The agent already holds the role that grants access to the tool; a policy requires a human decision for that call. Who may make that decision is itself an entitlement your IGA grants and reviews.