Skip to content

Tool authorization

Per-tool RBAC for MCP: allow or deny listing, search, calls and code-mode scripts based on validated JWT claims and header fields.

Every MCP tool can carry its own rbac block that allows or denies access based on the caller's validated JWT. Use it to serve one gateway to many audiences: unauthorized tools are hidden from tools/list and search and rejected on tools/call, so each agent only ever sees its own catalog.

Before you begin

Tool RBAC reads claims that the JWT filter has already validated, so envoy.filters.http.jwt_authn must run before the MCP gateway in the same filter chain — and its provider must publish the token into request metadata:

# inside the jwt_authn provider
payload_in_metadata: jwt_payload   # required for jwt_claim rules
header_in_metadata: jwt_header     # only needed for jwt_header rules

Without payload_in_metadata, no claims reach the gateway and every ALLOW rule denies — for everyone, including admins.

Restrict a tool

The admin-only pattern — action: ALLOW denies by default and permits only matching callers:

tools:
  - namespace: admin
    name: delete_user
    description: Delete a user account.
    input_schema:
      inline_string: '{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}'
    rbac:
      action: ALLOW
      permissions:
        - jwt_claim:
            field: role
            value: admin
    rest_backend:
      cluster: admin_api
      method: DELETE
      path: /users/{{id}}

Permissions use OR logic — the rule is satisfied if any entry matches:

  • action: ALLOW — deny unless some permission matches.
  • action: DENY — allow unless some permission matches (an exclusion list, for example blocking role: guest).

A request with no JWT matches nothing: ALLOW tools deny it, DENY tools admit it. At least one permission is required.

What you can match

jwt_claim compares one payload claim to an exact value. Standard claims work by short or long name (iss/issuer, sub/subject, aud/audience — matched against any audience entry — exp, iat, nbf, jti), and any other field name matches a custom claim; numbers and booleans are compared by their string form.

jwt_header compares a token header field, typically to pin service accounts to signing keys:

rbac:
  action: ALLOW
  permissions:
    - jwt_header:
        field: kid
        value: service-key-1
    - jwt_header:
        field: kid
        value: service-key-2

Supported header fields are alg, typ, cty, kid, jku and x5u (long aliases such as algorithm and key_id also work).

On a dynamic MCP server or an OpenAPI source, the same rbac block applies to every tool it contributes.

In code mode

A code-mode script runs with the identity of the execute call that started it, and every tool call it makes goes through the same check. A denied call resolves to { ok: false, error: { code: "access_denied" } }, which the script can branch on. The catalog counts, lists, searches and describes only permitted tools. A toolkit appears only when the caller may use every tool in it. A script cannot reach anything its caller could not reach with a direct tools/call.

Verify

Initialize a session without an admin token and export the returned mcp-session-id (the flow is shown in tools and REST backends), then call the restricted tool:

curl -s http://127.0.0.1:8080/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -H "mcp-session-id: $SESSION_ID" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"admin.delete_user","arguments":{"id":"42"}}}'

The HTTP status is 200 but the JSON-RPC payload is an error:

{"jsonrpc":"2.0","id":3,"error":{"code":-32600,"message":"Access denied by RBAC policy for tool 'admin.delete_user'"}}

With an admin token the call dispatches normally, and the tool appears in tools/list (if it is eager) and in search results.

Limits

  • Tools without rbac are visible and callable by every request that passed the filter chain. RBAC narrows access; the JWT filter grants it.
  • Matching is exact string equality; there are no wildcards, regexes or list-valued custom claims.
  • Denials are JSON-RPC errors over HTTP 200, so watch MCP results, not HTTP status codes.
  • Per-tool policy is JWT RBAC only. The Cedar policy filter evaluates HTTP requests, not individual MCP tool calls; for choosing between the gateway-level authorization filters, see authorization.

The rbac field table lives in the filter reference.

Continue

Start typing to search the site.

↑ ↓ to move Enter to open Esc to close