Understand tool access revocation

Tool revocation uses the authorization setup where tool access is controlled by roles in identity provider JWT tokens. Revoking a tool means removing the corresponding role from a user or group, so their next token no longer grants access.

Tip

You cannot revoke a specific in-flight token or revoke access instantly. JWT tokens govern access, and these tokens are valid until they expire. Revocation relies on the token expiring and being reissued with changed permissions. To force faster revocation, reduce the access-token lifespan in your identity provider.

The following two enforcement points apply:

  • tools/call: The AuthPolicy custom resource (CR) CEL expression checks the x-mcp-toolname header against the user’s resource_access roles. A revoked tool returns a failed to create session for mcp server: failed to create client: transport error: server returned 4xx for initialize POST, likely a legacy SSE server error.

  • tools/list: The broker filters the tools list by using the signed x-mcp-authorized header. A revoked tool is no longer displayed in the list.

The timing of tool revocation depends on the following session events:

  • New sessions: Users who authenticate after revocation receive a token without the revoked tool. Denial is immediate.

  • Existing sessions: Users with an active token keep access until the token expires. You configure token lifetime in your identity provider, for example, the Access Token Lifespan setting in Red Hat build of Keycloak. Shorter token lifetimes mean that tokens are refreshed more often, picking up role changes sooner. You must decide on the balance between revocation latency and token-refresh resource use.

  • In-flight requests: A tools/call request that is already being processed completes normally. The authorization check occurs before the request reaches the backend MCP server. Only new requests are denied.

Revoke tool and prompt call requests

Use your existing authorization setup to revoke tool and prompt requests for individual users and groups by removing either role from the user or group in your identity provider.

In this example, Red Hat build of Keycloak is used. Remove the client-role mapping. The client name corresponds to the namespaced MCPServerRegistration custom resource (CR), such as mcp-test/test-server1. Each role represents a tool name, such as greet, or headers. Use the same process for prompts.

Prerequisites
  • You installed MCP gateway.

  • You configured a Gateway object.

  • You are logged into a running OpenShift Container Platform cluster with an admin role.

  • You configured an HTTPRoute object for the gateway.

  • You registered a Model Context Protocol (MCP) server.

  • You created a Secret CR for authentication.

  • You configured authorization with a tool-level AuthPolicy CR.

Procedure
  1. Revoke a tool for a group by using the Red Hat build of Keycloak interface:

    1. Go to Groups > select the group, such as accounting.

    2. Go to Role mapping > remove the tool role from the relevant client.

  2. Revoke a tool for a single user by using the interface:

    1. Go to Users > select the user.

    2. Go to Role mapping > remove the tool role from the relevant client.

Verification
  • After revoking a tool, verify that the user can no longer call it by using MCP Inspector.

    • Log out of any existing MCP Inspector session and log back in as the affected user to get a fresh token.

    • Open MCP Inspector and connect to your gateway’s /mcp endpoint. Authenticate through the OAuth flow.

    • Under Tools > List Tools, the revoked tool is displayed. Try calling the revoked tool. The request should return 500.

Filter revoked tools and prompts from display

After you revoke a tool or prompt by using your identity provider, requests are blocked. However, the revoked capability is still displayed in responses.

To filter revoked tools and prompts from the displayed list, you must give the Model Context Protocol (MCP) broker component a signed header that carries the user’s authorized capabilities.

You can remove blocked tools from responses by configuring the Authorino Operator to generate the header by using a wristband JSON web token (JWT) signed with an Elliptic Curve Digital Signature Algorithm (ECDSA) key pair.

Prerequisites
  • You installed MCP gateway.

  • You configured a Gateway object with both mcp and mcps listeners.

  • You are logged into a running OpenShift Container Platform cluster with an admin role.

  • You configured an HTTPRoute object for the gateway.

  • You registered an MCP server.

  • You configured authentication with an AuthPolicy CR on the mcp listener.

  • You configured authorization with a tool-level AuthPolicy CR on the mcps listener.

Procedure
  1. Generate an ECDSA key pair by running the following commands:

    1. Generate the private key and save it to a file by running the following command:

      $ openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem
    2. Generate the public key and save it to a file by running the following command:

      $ openssl ec -in private-key.pem -pubout -out public-key.pem
  2. Create a Kubernetes Secret object in the Authorino Operator namespace by adding the private key with the following command:

    $ oc create secret generic trusted-headers-private-key \
      --from-file=key.pem=private-key.pem \
      -n <kuadrant_system> \
      --dry-run=client -o yaml | oc apply -f -

    Replace <kuadrant_system> with the namespace where you installed Connectivity Link.

  3. Create a Kubernetes Secret object in the MCP broker component namespace by adding the public key with the following command:

    $ oc create secret generic trusted-headers-public-key \
      --from-file=key=public-key.pem \
      -n <mcp_system> \
      --dry-run=client -o yaml | oc apply -f -

    Replace <mcp_system> with the namespace where MCP gateway is installed.

  4. Delete only the existing AuthPolicy CR on the mcp listener by running the following command:

    $ oc delete authpolicy <mcp_authn_policy> -n <authn_namespace> --ignore-not-found
    • Replace <mcp_authn_policy> with the name of your AuthPolicy CR.

    • Replace <authn_namespace> with the namespace where your AuthPolicy CR is applied.

      Important

      You must delete the AuthPolicy CR and create a new one. Using $ oc apply merges both the new and old policies.

  5. Generate a new x-mcp-authorized header by creating and applying a new AuthPolicy CR. Use the following example:

    Example AuthPolicy CR
    $ oc create -f - <<EOF
    apiVersion: kuadrant.io/v1
    kind: AuthPolicy
    metadata:
      name: _<mcp_auth_policy>_
      namespace: _<authn_namespace>_
    spec:
      targetRef:
        group: gateway.networking.k8s.io
        kind: Gateway
        name: _<mcp_gateway>_
        sectionName: mcp
      when:
        - predicate: "!request.path.contains('/.well-known')"
      rules:
        authentication:
          'keycloak':
            jwt:
              issuerUrl: https://keycloak.example.com:port/realms/mcp
        authorization:
          'allow-mcp-method':
            patternMatching:
              patterns:
              - predicate: |
                  !request.headers.exists(h, h == 'x-mcp-method') || (request.headers['x-mcp-method'] in ["tools/list","prompts/list","initialize","notifications/initialized"])
          'authorized-capabilities':
            opa:
              rego: |
                allow = true
                capabilities = {
                  "tools": { server: tools |
                    server := object.keys(input.auth.identity.resource_access)[_]
                    tools := [substring(r, count("tool:"), -1) |
                      r := input.auth.identity.resource_access[server].roles[_]
                      startswith(r, "tool:")
                    ]
                  },
                  "prompts": { server: prompts |
                    server := object.keys(input.auth.identity.resource_access)[_]
                    prompts := [substring(r, count("prompt:"), -1) |
                      r := input.auth.identity.resource_access[server].roles[_]
                      startswith(r, "prompt:")
                    ]
                  }
                }
              allValues: true
        response:
          success:
            headers:
              x-mcp-authorized:
                wristband:
                  issuer: 'authorino'
                  customClaims:
                    'allowed-capabilities':
                      selector: auth.authorization.authorized-capabilities.capabilities.@tostr
                  tokenDuration: 300
                  signingKeyRefs:
                    - name: trusted-headers-private-key
                      algorithm: ES256
          unauthenticated:
            headers:
              'WWW-Authenticate':
                value: Bearer resource_metadata=https://mcp.example.com:port/.well-known/oauth-protected-resource
            body:
              value: |
                {
                  "error": "Unauthorized",
                  "message": "Access denied: Authentication required."
                }
          unauthorized:
            code: 401
            headers:
              'WWW-Authenticate':
                value: Bearer resource_metadata=https://mcp.example.com:port/.well-known/oauth-protected-resource
            body:
              value: |
                {
                  "error": "Forbidden",
                  "message": "Access denied: Unsupported method. New authentication required (401)."
                }
    EOF
    • Replace <mcp_auth_policy> with the name of your new AuthPolicy CR.

    • Replace <authn_namespace> with the namespace where you removed the AuthPolicy CR that was on the mcp listener.

    • Replace <mcp_gateway> with the name of your MCP gateway deployment.

  6. Configure the MCP broker component to validate the signed header by running the following command:

    $ oc patch mcpgatewayextension <mcp_gateway_extension> -n <mcp_system> --type='merge' \
      -p='{"spec":{"trustedHeadersKey":{"secretName":"trusted-headers-public-key"}}}'
    • Replace <mcp_gateway_extension> with the name of your MCPGatewayExtension CR.

    • Replace <mcp_system> with the namespace where MCP gateway is installed.

  7. Wait for the automatic MCP broker component redeployment to load the public key from the Secret CR created in the earlier step by running the following command:

    $ oc rollout status deployment/<mcp_gateway> -n <mcp_system> --timeout=60s
    • Replace <mcp_gateway> with the name of your MCP gateway deployment.

    • Replace <mcp_system> with the namespace where your MCP gateway deployment is applied.

  8. If the redeployment does not start, force it by running the following command:

    $ oc rollout restart deployment/<mcp_gateway> -n <mcp_system>
    • Replace <mcp_gateway> with the name of your MCP gateway deployment.

    • Replace <mcp_system> with the namespace where your MCP gateway deployment is applied.

Verification
  1. Verify that the new AuthPolicy CR is enforced by running the following command:

    $ oc get authpolicy <mcp_auth_policy> -n <authn_namespace> -o jsonpath='{.status.conditions[?(@.type=="Enforced")].status}'
    • Replace <mcp_auth_policy> with the name of your new AuthPolicy CR.

    • Replace <authn_namespace> with the namespace where your AuthPolicy CR is applied.

      Example output
      True
  2. Log out and log back in with the user credentials that you changed access for to get a fresh token.

  3. Request all available tools from your MCP servers for the user or group whose access you revoked. The expected response is a list of all tools that the user or group can access from all registered MCP servers.