MCP Access Control: Implementing Per-Tool RBAC for AI Agents
A developer-first guide to per-tool role-based access control for MCP servers, with code, a decision matrix, real incidents, and a ready-to-use checklist.
Mohammed Kafeel
Machine Learning Researcher
On this page
- What Is MCP and Why Does Access Control Matter?
- What Is Per-Tool RBAC for AI Agents?
- Real-World MCP Security Failures (And What They Teach Us)
- How to Implement Per-Tool RBAC for MCP Agents: Step-by-Step
- Per-Tool RBAC in Practice: A Code Walkthrough
- Common Mistakes to Avoid
- MCP Access Control Checklist
- Key Takeaways
- FAQ
- Useful Sources
Last updated: June 2026
Eighty percent of organizations report their AI agents have already performed unintended actions - not because the models misbehaved, but because nobody told the system which tools each agent was allowed to touch. That's the access control gap. And with Gartner projecting that 80% of ISVs will embed GenAI by 2026, the blast radius of that gap is growing fast.
This guide is about closing it - specifically at the Model Context Protocol (MCP) tool level, where the real risk lives.
š TL;DR - Key Takeaways
- MCP tools are effectively arbitrary code execution. Without per-tool access control, any agent can call any tool it can discover.
- Per-tool RBAC assigns specific roles (e.g.,
analyst,engineer,admin) to specific tools - not just to servers or users.- RBAC is your baseline. Layer ABAC or PBAC on top for context-sensitive decisions.
- Enforce at the gateway or middleware layer, not inside individual tool handlers.
- Audit every tool call. Only 52% of organizations can currently audit agent data access.
- Real incidents (Asana, Atlassian, Moltbook) prove that vague, server-wide permissions are not enough.
What Is MCP and Why Does Access Control Matter?
MCP (Model Context Protocol) is an open protocol that standardizes how LLM applications connect to external tools, data sources, and services. The official MCP specification defines three core roles:
- Hosts - LLM applications that initiate connections (e.g., Claude Desktop, your custom agent).
- Clients - Connectors inside the host.
- Servers - Services that expose tools, resources, and prompts to the AI.
MCP uses JSON-RPC 2.0 for messaging and supports stateful connections with capability negotiation. It's elegant. It's powerful. And it has almost no built-in access control by default.
The MCP tool call flow
- The host connects to an MCP server and discovers available tools via
tools/list. - The LLM decides which tool to invoke based on the user's prompt.
- The client sends a
tools/callrequest with arguments. - The MCP server executes the tool and returns the result.
Notice what's missing: no step where the server checks whether this agent or this role is allowed to call this specific tool. That's your job.
What happens when there's no access control?
Five enterprise failure modes:
- Tool overexposure - agents discover and call tools they were never meant to use.
- Cross-environment leakage - a dev agent accidentally touches production data.
- Model privilege escalation - the LLM reasons its way into calling admin-level tools.
- Audit blind spots - no record of which tool was called, by whom, with what arguments.
- Security logic sprawl - every tool handler re-implements its own auth checks inconsistently.
The MCP spec itself is explicit: "Tools represent arbitrary code execution and must be treated with appropriate caution."
What Is Per-Tool RBAC for AI Agents?
Per-tool RBAC (Role-Based Access Control) means assigning specific roles to specific tools - not just to the MCP server as a whole. An analyst role can call read_database and list_files. An engineer role adds write_file and restart_service. An admin adds delete_db and grant_role.
This is different from server-level auth, where you authenticate into an MCP server and then have access to everything it exposes. Per-tool RBAC adds a second gate.
RBAC vs ABAC vs PBAC - which one fits MCP?
| Model | Full Name | How it works | Best for MCP use case | Complexity |
|---|---|---|---|---|
| RBAC | Role-Based Access Control | Permissions tied to roles; roles assigned to users/agents | Baseline tool-level permissions | Low |
| ABAC | Attribute-Based Access Control | Permissions based on attributes (user dept, data sensitivity, time of day) | Fine-grained, context-sensitive decisions | Medium |
| PBAC | Policy-Based Access Control | Centralized policy engine evaluates every request | Enterprise-wide governance, dynamic rules | High |
Our recommendation: Start with RBAC as your foundation. Layer ABAC on top when you need context sensitivity. Add PBAC via a policy engine like Open Policy Agent (OPA) or Cerbos when you need centralized, dynamic governance across many agents.
The OWASP Top 10 for Agentic Applications 2026 lists Tool Misuse & Exploitation (ASI02) and Identity & Privilege Abuse (ASI03) as two of the top risks.
Real-World MCP Security Failures (And What They Teach Us)
The Asana MCP cross-user data leak
On May 1, 2025, Asana launched an experimental MCP server. On June 4, 2025, they discovered a tenant isolation bug: one customer's AI requests could see cached responses tied to another organization's data. Roughly 1,000 enterprise customers across 190 countries were potentially affected during a 34-day exposure window.
Root cause: The server relied on user tokens but didn't enforce per-AI-agent boundaries. Classic confused-deputy problem. (Closing this gap is the focus of our guide to multi-tenant access isolation.)
Lesson: Authenticating the user isn't enough. You need per-agent, per-tool scoping validated on every single request.
The Atlassian MCP privilege escalation flaw
The mcp-atlassian server shipped with two critical CVEs in early 2026:
- CVE-2026-27825: A path traversal bug allowed unauthenticated attackers to write arbitrary files to the host - achieving local privilege escalation or RCE.
- CVE-2026-27826: An SSRF vulnerability let attackers force the MCP server to make outbound HTTP requests to internal services.
Both were fixed in mcp-atlassian v0.17.0.
Lesson: Individual tool handlers must validate and sanitize their inputs. A gateway-level RBAC check is necessary but not sufficient.
The Moltbook key exposure
In January 2026, Wiz researchers discovered that Moltbook had left its Supabase database publicly accessible. An API key in client-side JavaScript granted full read/write access to ~4.75 million records, including 1.5 million API tokens used by AI agents to connect to OpenAI, Anthropic, AWS, GitHub, and Google Cloud.
Lesson: AI agents accumulate credentials at scale. If you don't have secrets management combined with per-tool least-privilege, a single misconfiguration can expose your entire agent fleet's access.
How to Implement Per-Tool RBAC for MCP Agents: Step-by-Step
Step 1 - Map your tools and define roles
Before writing a single line of auth code, build a tool inventory. For every tool your MCP server exposes, answer:
- What does this tool do?
- What data or systems does it touch?
- What's the blast radius if it's misused?
- Who legitimately needs to call it?
Then define your roles:
| Role | Description | Example tools |
|---|---|---|
viewer |
Read-only access | read_db, list_files, view_logs |
analyst |
Read + query | read_db, run_query, export_report |
engineer |
Read + write | write_file, restart_service, deploy_config |
admin |
Full access | delete_db, grant_role, manage_users |
Separate read and write tools explicitly. Don't make read_db and write_db the same tool with a parameter flag - make them two distinct tools.
Step 2 - Assign permissions at the tool level
Create a permission map - a data structure that declares which roles can call which tools.
# tool_permissions.py
TOOL_PERMISSIONS = {
"read_database": ["viewer", "analyst", "engineer", "admin"],
"run_query": ["analyst", "engineer", "admin"],
"write_file": ["engineer", "admin"],
"restart_service": ["engineer", "admin"],
"delete_database": ["admin"],
"grant_role": ["admin"],
"export_report": ["analyst", "engineer", "admin"],
}
Step 3 - Enforce at the MCP gateway or middleware layer
Don't scatter auth checks inside individual tool handlers. Enforce at a single choke point.
# middleware/rbac_middleware.py
from tool_permissions import TOOL_PERMISSIONS
def check_tool_permission(agent_role: str, tool_name: str) -> bool:
allowed_roles = TOOL_PERMISSIONS.get(tool_name, [])
return agent_role in allowed_roles
def enforce_rbac(request: dict, agent_context: dict) -> dict:
tool_name = request.get("params", {}).get("name")
agent_role = agent_context.get("role")
if not tool_name or not agent_role:
raise ValueError("Missing tool name or agent role in request context.")
if not check_tool_permission(agent_role, tool_name):
raise PermissionError(
f"Role '{agent_role}' is not authorized to call tool '{tool_name}'."
)
return request
Deny by default. If a tool isn't in TOOL_PERMISSIONS, the answer is no.
Step 4 - Add audit logging for every tool call
Only 52% of organizations can currently audit agent data access.
# middleware/audit_logger.py
import logging
import time
from typing import Optional
logger = logging.getLogger("mcp.audit")
def log_tool_call(
agent_id: str,
agent_role: str,
tool_name: str,
arguments: dict,
outcome: str,
error_message: Optional[str] = None,
):
logger.info({
"timestamp": time.time(),
"agent_id": agent_id,
"agent_role": agent_role,
"tool_name": tool_name,
"arguments": arguments,
"outcome": outcome,
"error": error_message,
})
Ship these logs to your SIEM. Correlate by agent_id to reconstruct full agent sessions. (For the full field-by-field breakdown of logging per-tool access, see our audit logging guide.)
Step 5 - Test and iterate
Write explicit tests - especially negative tests that verify denied access:
# tests/test_rbac.py
from middleware.rbac_middleware import check_tool_permission
def test_viewer_cannot_write():
assert check_tool_permission("viewer", "write_file") is False
def test_admin_can_delete():
assert check_tool_permission("admin", "delete_database") is True
def test_unknown_tool_is_denied():
assert check_tool_permission("engineer", "nonexistent_tool") is False
def test_unknown_role_is_denied():
assert check_tool_permission("hacker", "read_database") is False
Per-Tool RBAC in Practice: A Code Walkthrough
# mcp_server.py (simplified)
from middleware.rbac_middleware import enforce_rbac
from middleware.audit_logger import log_tool_call
from tools import tool_registry
def handle_tool_call(request: dict, agent_context: dict) -> dict:
tool_name = request.get("params", {}).get("name", "unknown")
agent_id = agent_context.get("agent_id", "unknown")
agent_role = agent_context.get("role", "unknown")
arguments = request.get("params", {}).get("arguments", {})
try:
enforce_rbac(request, agent_context)
handler = tool_registry.get(tool_name)
if not handler:
raise ValueError(f"Tool '{tool_name}' not found in registry.")
result = handler(arguments)
log_tool_call(agent_id, agent_role, tool_name, arguments, "allowed")
return {"result": result}
except PermissionError as e:
log_tool_call(agent_id, agent_role, tool_name, arguments, "denied", str(e))
return {"error": {"code": 403, "message": str(e)}}
except Exception as e:
log_tool_call(agent_id, agent_role, tool_name, arguments, "error", str(e))
return {"error": {"code": 500, "message": "Internal server error."}}
This pattern gives you:
- One enforcement point - no auth logic scattered in tool handlers.
- Complete audit trail - every call logged, regardless of outcome.
- Graceful error responses - the agent gets a structured error, not a crash.
Common Mistakes to Avoid
- Server-level auth only. Authenticating into the MCP server doesn't mean every tool is safe to call.
- Allow-by-default. If a tool isn't in your permission map, it should be denied.
- Combining read and write in one tool. A
manage_databasetool with amodeparameter is a footgun. - Hardcoding roles in tool handlers. Centralize auth logic.
- No negative tests. Testing that
admincan delete is easy. Testing thatviewercannot delete is what catches bugs. - Skipping audit logs for denied calls. A denied call is often more interesting than an allowed one.
- Long-lived agent credentials. Use short-lived tokens and rotate them.
- Ignoring tool discovery. Filter
tools/listby role - don't showdelete_databaseto aviewer. - No quarterly permission review. Roles accumulate permissions over time.
MCP Access Control Checklist
Planning
- Tool inventory complete - every tool documented with blast radius assessment
- Roles defined and minimal
- Read and write tools are separate
- Permission map created and stored centrally
Implementation
- RBAC enforced at the gateway or middleware layer
- Deny-by-default policy in place
- Input validation in every tool handler
- Tool discovery (
tools/list) filtered by role - Short-lived agent tokens
Audit & Monitoring
- Every tool call logged
- Denied calls logged separately and alerted on
- Logs shipped to SIEM and correlated by agent session
- Human approval gates configured for high-impact tools
Testing & Governance
- Positive RBAC tests pass
- Negative RBAC tests pass
- Unknown tool and unknown role tests pass
- Quarterly permission review scheduled
- Incident response runbook includes MCP tool call forensics
Key Takeaways
- Per-tool RBAC is the foundational control. Map tools ā roles ā permissions in one central place.
- Enforce at the gateway, not inside tool handlers.
- Audit everything. Denied calls matter as much as allowed ones.
- Real incidents prove the risk. Asana lost tenant isolation. Atlassian shipped RCE via path traversal. Moltbook exposed 1.5M agent credentials. All preventable.
- 92% of organizations agree agent governance is critical. Only 44% have implemented policies. Don't be in the 56%.
- Layer ABAC or PBAC on top of RBAC when you need context-sensitive or dynamic rules.
FAQ
What is Model Context Protocol security and why does it matter in 2026?
Model Context Protocol security refers to the set of controls - authentication, authorization, input validation, audit logging - that protect MCP servers and the tools they expose to AI agents. MCP tools execute arbitrary code on your infrastructure. Without proper controls, any connected agent can call any tool it discovers.
What's the difference between server-level auth and per-tool RBAC?
Server-level auth verifies that an agent or user is allowed to connect to an MCP server. Per-tool RBAC verifies that the authenticated agent is allowed to call a specific tool. You need both.
Can I use OAuth scopes instead of building custom RBAC?
Yes, and you should - they're complementary. Use narrow OAuth scopes to control what the MCP server can access on behalf of the user. Use per-tool RBAC to control which tools each agent role can invoke. (See our MCP OAuth 2.1 implementation guide for the scope-issuing side.)
How does per-tool RBAC relate to the OWASP Agentic AI Top 10?
Directly. The OWASP Top 10 for Agentic Applications 2026 lists Tool Misuse & Exploitation (ASI02) and Identity & Privilege Abuse (ASI03) as critical risks. Per-tool RBAC is the primary control for both.
What's the performance overhead of adding RBAC middleware to MCP?
Negligible at the gateway level. TrueFoundry's MCP Gateway adds ~10ms of latency overhead while handling 350+ RPS on a single vCPU. A simple in-memory permission map lookup adds microseconds.
How often should I review and update my tool permission map?
At minimum, quarterly - and immediately after any role change, team restructuring, or new tool deployment. Permission creep is real.
What should I do if an agent calls a tool it shouldn't have access to?
Check your audit logs first. Identify the agent ID, tool, arguments, and timestamp. Revoke credentials immediately if you suspect compromise. Patch the permission map and add a regression test. (If you need to pull a single tool fast, see disabling tools without downtime.)
Useful Sources
- Model Context Protocol Specification (2025-06-18)
- OWASP Top 10 for Agentic Applications 2026
- NIST SP 800-207: Zero Trust Architecture
- NSA MCP Security Guidance (CSI)
- Asana MCP Data Exposure Incident
- CVE-2026-27825: Critical RCE in mcp-atlassian
- Moltbook Breach: 1.5M AI Agent Credentials Exposed
- Red Hat: MCP Security Risks and Controls
Keep reading
Multi-Tenant MCP: How to Isolate Agent Access Across Clients
Running multiple clients through a single MCP server without proper isolation is a data breach waiting to happen. Here's how to architect tenant boundaries that hold.
MCP SSO Integration: Connecting Enterprise Identity Providers
A deep-dive guide to MCP SSO integration - OAuth 2.1, SAML 2.0, LDAP, SCIM, agent identity, and step-by-step setup for Okta, Azure AD, Google, Keycloak.
How to Audit Third-Party MCP Servers Using mcp-scan
A step-by-step guide to auditing third-party MCP servers with mcp-scan ā installation, CLI commands, threat types, tool pinning, CI/CD integration, and security best practices.



