MastertheAgent
Reference · Solo · agentgateway · kagent · authorization · MCP · A2A
Reference · verified against the agentic-tool-privilege-kind lab

Two authorization layers: EnterpriseAgentgatewayPolicy vs AccessPolicy

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

Authorizing an agent interaction what are you gating? which TOOLS / LLMroutes a call gets who can INVOKEwhich agent Prerequisite Traffic passes through agentgateway (no service mesh required) EnterpriseAgentgatewayPolicy backend.mcp.authorization — CEL over jwt.* claims + mcp.tool.name filters tools/list · refuses tools/call (-32602) enforced at the gateway data plane Prerequisite Solo Istio ambient + a waypoint (else Accepted but NOT enforced) AccessPolicy action + from.subjects (Agent / userGroup) targetRef: Agent (+ optional tools[]) who may invoke which agent (A2A / user) enforced through the mesh waypoint Use BOTH — defence in depth AccessPolicy = the door: who reaches the agent CEL = what's behind it: which tools that call then gets a caller must pass the AccessPolicy, then its tools are scoped by CEL The agentic-tool-privilege-kind lab (Part 2) takes the left lane: CEL at the gateway, no mesh. agentic-a2a-kind uses the right lane: AccessPolicy on the agent graph.

The quick rule

Your questionUse
"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/call returns -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) or userGroup (claim/issuer).
  • Targets a kagent resource: targetRef: {kind, name, tools[]}.
  • Expression = structured action: Allow/Deny, from.subjectstargetRef.
  • 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.

Enforcement gotcha. An 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

See also