Applicability
When to Use
✓When different users should have different MCP server access
✓When implementing multi-tenant agent systems
✓When compliance requires access controls
Overview
How It Works
This pattern implements a middleware layer that checks user roles before allowing MCP server tool invocations. Each tool is mapped to required roles, and the middleware verifies the requesting user has the necessary permissions before forwarding the request.
Roles are defined in a central policy store and can be managed via the Auth0 or Keycloak MCP Servers. The middleware intercepts every tool call, checks the user's roles against the required roles for that tool, and either allows or denies the request.
Implementation
Code Example
typescript
const toolPermissions = {
"postgres.query": ["admin", "analyst"],
"postgres.execute": ["admin"],
"slack.sendMessage": ["admin", "agent"],
"stripe.createCharge": ["admin", "billing"],
"github.mergePR": ["admin", "developer"]
};
async function rbacMiddleware(user, server, tool, args) {
const requiredRoles = toolPermissions[`${server}.${tool}`];
if (!requiredRoles) throw new Error(`No permissions defined for ${server}.${tool}`);
const userRoles = await auth0.getUserRoles({ userId: user.id });
const hasAccess = requiredRoles.some(r => userRoles.includes(r));
if (!hasAccess) {
await postgres.query("INSERT INTO access_denied_log (user_id, tool, timestamp) VALUES ($1, $2, NOW())", [user.id, `${server}.${tool}`]);
throw new Error(`Access denied: ${user.email} lacks role for ${server}.${tool}`);
}
return await mcpServers[server][tool](args);
}Quick Info
Categorysecurity
ComplexityMedium