Skip to content
🎯 New workshop: Govern AI Costs in Real Time — Hands-On with agentgateway agentgateway has joined the Agentic AI FoundationLearn more

For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.

Page as Markdown

MCP authorization

Verified Code examples on this page have been automatically tested and verified.

Control access to MCP tools and resources with CEL-based authorization rules

Note

This policy works only for MCP traffic. Note that all standard HTTP policies also apply to MCP traffic.

The MCP authorization policy works similarly to HTTP authorization, but runs in the context of an MCP request. Authorization rules are written as CEL expressions that evaluate against specific MCP method invocations, such as list_tools and call_tools, to control which tools, prompts, and resources clients can access.

If a tool or other resource is not allowed, agentgateway automatically filters the resource from list responses so that unauthorized clients never see it.

The mcpAuthorization policy is attached at the backend level, where it applies to all MCP targets in the backend. To apply different rules to individual targets, match on the mcp.tool.target variable in your CEL rules. For more about which policies can be configured per target, see MCP target policies.

Allow specific tools

The following configuration exposes an MCP server and allows any client to call the echo tool. All other tools from the server are blocked.

# yaml-language-server: $schema=https://agentgateway.dev/schema/config
mcp:
  port: 3000
  policies:
    mcpAuthorization:
      rules:
      - 'mcp.tool.name == "echo"'
  targets:
  - name: everything
    stdio:
      cmd: npx
      args: ["@modelcontextprotocol/server-everything"]

Role-based access with JWT claims

When you combine MCP authorization with MCP authentication, you can write rules that reference JWT claims. The following configuration restricts tools based on the authenticated user’s identity and role.

In this example:

  • The MCP authentication policy validates JWTs against a local authorization server, such as Keycloak, running on port 9000.
  • Any authenticated user can call the echo tool.
  • Only the user test-user can call the add tool.
  • Only users with the nested claim nested.key == "value" can call the printEnv tool.
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
mcp:
  port: 3000
  policies:
    mcpAuthentication:
      issuer: http://localhost:9000
      audiences:
      - http://localhost:3000/mcp
      jwks:
        url: http://localhost:9000/.well-known/jwks.json
      resourceMetadata:
        resource: http://localhost:3000/mcp
        scopesSupported:
        - read:all
        bearerMethodsSupported:
        - header
    mcpAuthorization:
      rules:
      # Any authenticated user can call 'echo'
      - 'mcp.tool.name == "echo"'
      # Only the test-user can call 'add'
      - 'jwt.sub == "test-user" && mcp.tool.name == "add"'
      # Claim-based access for 'printEnv'
      - 'mcp.tool.name == "printEnv" && jwt.nested.key == "value"'
  targets:
  - name: everything
    stdio:
      cmd: npx
      args: ["@modelcontextprotocol/server-everything"]

Different rules per target

When you multiplex several MCP servers behind a single agentgateway listener, you can apply different authorization rules to each target by matching on the mcp.tool.target variable in a single backend-level policy.

In this example:

  • Any user can access tools on the public-tools target.
  • Only users with admin in the JWT roles can access tools on the admin-tools target.
  • The JWT is validated against a local authorization server running on port 9000.
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
mcp:
  port: 3000
  policies:
    cors:
      allowOrigins: ["*"]
      allowHeaders:
      - mcp-protocol-version
      - content-type
      - cache-control
    mcpAuthentication:
      mode: optional
      issuer: http://localhost:9000
      audiences:
      - http://localhost:3000/mcp
      jwks:
        url: http://localhost:9000/.well-known/jwks.json
      resourceMetadata:
        resource: http://localhost:3000/mcp
        scopesSupported:
        - read:all
        bearerMethodsSupported:
        - header
    mcpAuthorization:
      rules:
      # Allow anyone to access tools on the public-tools target
      - 'mcp.tool.target == "public-tools"'
      # Only authenticated admins can access tools on the admin-tools target
      - 'mcp.tool.target == "admin-tools" && has(jwt.sub) && "admin" in jwt.roles'
  targets:
  - name: public-tools
    stdio:
      cmd: npx
      args: ["@modelcontextprotocol/server-everything"]
  - name: admin-tools
    stdio:
      cmd: npx
      args: ["@mycompany/admin-server"]

CEL expression reference

Authorization rules use Common Expression Language (CEL) expressions. Rules are evaluated as an OR expression: if any rule matches, the request is allowed.

Review the following table of common variables in MCP authorization rules. For the full list of supported variables and functions, refer to the CEL reference.

VariableDescription
mcp.tool.nameThe name of the tool being called
mcp.tool.targetThe target backend handling the tool call
mcp.prompt.nameThe name of the prompt being accessed
mcp.resource.nameThe name of the resource being accessed
jwt.subThe sub (subject) claim from the JWT
jwt.<claim>Any top-level or nested JWT claim (such as jwt.roles, jwt.nested.key)
has(jwt.<claim>)Check whether a JWT claim exists
Was this page helpful?
Agentgateway assistant

Ask me anything about agentgateway configuration, features, or usage.

Note: AI-generated content might contain errors; please verify and test all returned information.

Tip: one topic per conversation gives the best results. Use the + button in the chat header to start a new conversation.

Switching topics? Starting a new conversation improves accuracy.
↑↓ navigate select esc dismiss

What could be improved?

Your feedback helps us improve assistant answers and identify docs gaps we should fix.

Need more help? Join us on Discord: https://discord.gg/y9efgEmppm

Want to use your own agent? Add the Solo MCP server to query our docs directly. Get started here: https://search.solo.io/.