The Solo agentic stack has two ways to authorize what an agent can do, and they
guard different boundaries. EnterpriseAgentgatewayPolicy
(mcp.authorization) decides, at the agentgateway, which MCP tools
or LLM routes a caller gets — keyed on JWT claims, no mesh needed.
AccessPolicy (kagent-enterprise) decides which agent or
user-group may invoke which agent — enforced through the ambient mesh. This
is how to pick one, the other, or both.
Decision diagram
The quick rule
| Your question | Use |
|---|---|
| "Which MCP tools / LLM routes does this token get?" | EnterpriseAgentgatewayPolicy (CEL) |
| "Can agent A invoke agent B? Can this user hit that agent directly?" | AccessPolicy |
| "I have no mesh — just a gateway in front of the MCP server" | EnterpriseAgentgatewayPolicy (AccessPolicy won't enforce) |
| "Agents talk pod-to-pod, not through the gateway" | AccessPolicy (+ ambient mesh) |
"Hide the tool entirely from tools/list" | EnterpriseAgentgatewayPolicy (MCP-aware filtering) |
| "Gate reachability and tools" | both |
The two layers side by side
EnterpriseAgentgatewayPolicy · mcp.authorization
Per-tool CEL, at the gateway
- Enforced at the agentgateway data plane (the proxy in front of MCP / LLM). No mesh.
- Identity = raw JWT claims via CEL:
jwt.groups,jwt.sub,jwt.aud. - Targets an
AgentgatewayBackend(MCP tools) or a Gateway (LLM routes). - Expression = arbitrary CEL — flexible, per-tool, per-claim.
- MCP-aware: unauthorized tools vanish from
tools/list;tools/callreturns-32602.
AccessPolicy · policy.kagent-enterprise.solo.io
Identity graph, through the mesh
- Enforced through kagent's Istio authz translation / the ambient waypoint — needs the mesh.
- Identity = kagent subjects:
kind: Agent(name/ns) oruserGroup(claim/issuer). - Targets a kagent resource:
targetRef: {kind, name, tools[]}. - Expression = structured
action: Allow/Deny,from.subjects→targetRef. - Understands the agent graph: agent→agent (A2A), the acting/OBO identity, user→agent.
What each looks like
yamlEnterpriseAgentgatewayPolicy — per-tool authz (from agentic-tool-privilege-kind)
apiVersion: enterpriseagentgateway.solo.io/v1alpha1
kind: EnterpriseAgentgatewayPolicy
metadata: { name: mcp-tool-authz, namespace: mock-db }
spec:
targetRefs:
- { group: agentgateway.dev, kind: AgentgatewayBackend, name: mock-db-mcp }
backend:
mcp:
authorization:
action: Allow
policy:
matchExpressions:
- 'has(jwt.groups) && "db-operator" in jwt.groups'
- 'has(jwt.groups) && "db-reader" in jwt.groups &&
mcp.tool.name in ["db_status","list_tables","db_query"]'
yamlAccessPolicy — who may invoke which agent (from agentic-a2a-kind)
apiVersion: policy.kagent-enterprise.solo.io/v1alpha1
kind: AccessPolicy
metadata: { name: allow-orchestrator-to-dba, namespace: kagent }
spec:
action: ALLOW
from:
subjects:
- { kind: Agent, name: sre-orchestrator, namespace: kagent }
targetRef:
kind: Agent
name: dba-agent
# tools: [...] # optionally scope to specific MCP tools on that target
---
apiVersion: policy.kagent-enterprise.solo.io/v1alpha1
kind: AccessPolicy
metadata: { name: deny-user-direct-dba, namespace: kagent }
spec:
action: DENY
from:
subjects:
- kind: UserGroup
userGroup: { claimName: groups, claimValue: field-fte,
issuer: http://keycloak.keycloak.svc.cluster.local/realms/solo }
targetRef: { kind: Agent, name: dba-agent }
Use both — defence in depth
They compose. AccessPolicy is the door: it decides which agent or user
may reach an agent at all (mesh-enforced, identity-graph aware).
EnterpriseAgentgatewayPolicy is what's behind the door: once a call is
through, CEL scopes which MCP tools that call is allowed. A caller must satisfy the
AccessPolicy to reach the agent, and the tools it can then invoke are still limited
by the gateway. Use one when a single boundary is enough; use both when you want to
govern reachability and capability independently.
AccessPolicy with no ambient
mesh / waypoint installed shows Accepted but does not actually block
anything — this trips people up. If you are not running Solo Istio ambient, put the
boundary at the gateway with EnterpriseAgentgatewayPolicy instead.
Seen in the labs
- agentic-tool-privilege-kind (Part 2) — the left lane: two agents, two identities, one MCP server, and an
EnterpriseAgentgatewayPolicyper-tool CEL that gives the operatordb_reset_credentialsand the reader only the read tools. No mesh. - agentic-a2a-kind — the right lane:
AccessPolicyso only the orchestrator may invoke the DBA and end users can't reach it directly (with the OBO acting identity). - agentic-mcp-rbac-kind — per-user MCP tool RBAC at the gateway (the CEL layer, human identities).