Ana içeriğe geç

MCP Gateway Tool Authorization: Access Rules, Argument Constraints and Risk Policy

What You Build​

In this scenario you add three checks to the Order Desk MCP proxy. The proxy is a Passthrough proxy. The checks run on every tools/call.

CardQuestion that it answers
Identity Access RulesWhich caller may see and call which tool?
Argument constraintsAre the arguments of the call valid?
Tool risk policyDoes a person need to approve the call first?

A call passes the checks in this order. A refusal at one step stops the call. The next steps do not run.

On an Expose Tools proxy, the Required Scopes check of the tool runs between step 1 and step 2. See Expose Tools Proxies.

The reference for each card is in MCP Gateway. This page shows how to use the cards in one scenario.

Prerequisites​

Complete the setup of MCP Gateway Browser Sign-In Without Keycloak first. The checks need a caller identity. The examples use this setup:

ItemExample
ProxyOrder Desk MCP, Passthrough, path /order-desk, Standard MCP OAuth profile.
Token policyA JWT policy in Manage From ACL mode.
ApplicationClaude Code, with the public API client claude-code-desktop.
Consumersjane.doe (role Order Viewer) and sam.lee (roles Order Viewer and Order Manager).
RolesOrder Viewer (OAuth Scope Name orders.read) and Order Manager (OAuth Scope Name orders.write).
Backend MCP serverorder-desk, with three tools.

The tools of the backend server:

ToolAnnotationArguments
get_order_statusreadOnlyHintorderId
list_open_ordersreadOnlyHintNone
cancel_orderdestructiveHintorderId, reason

Where the Cards Are​

Open the Routing tab of the proxy. The cards are below the list of backend MCP servers. Each card has a Configure button. The button is disabled if you do not have manage permission on the API proxy.

Cards of the Routing tab: Argument constraints, Tool Output Filter, Identity Access Rules and Tool risk policy

The same cards exist on an Expose Tools proxy. On that proxy, the default risk tier of a tool comes from the HTTP method of the operation: GET is Low, POST is Medium and DELETE is High.

Identity Access Rules​

This card sets who may see and call which tool. A caller that a rule does not allow does not see the tool in tools/list and cannot call it.

Open the card

On the Routing tab, select Configure on the Identity Access Rules card.

Add the rule for viewers

Select Add Rule. Set Applies To to Role and Name to Order Viewer. In Allow, add get_order_status and list_open_orders.

Add the rule for managers

Select Add Rule again. Set Applies To to Role and Name to Order Manager. In Allow, add cancel_order.

Save and deploy

Select Save. Deploy the proxy.

Identity Access Rules dialog with a rule for the role Order Viewer and a rule for the role Order Manager

How the rules work:

  • A rule applies to a Consumer, a Role or an Organization.
  • An empty Allow list means every name.
  • A name in Deny always wins.
  • When the list has no rules, every authenticated caller sees and calls every tool. When the list has rules, a caller that no rule matches gets nothing.
  • The rules of all matching principals combine. sam.lee has both roles, so sam.lee gets the tools of both rules.

See Identity-Based Access Control.

Try it​

Send tools/list with the access token of each user. Get the token with the steps of the sign-in tutorial.

curl -s -X POST https://gateway.example.com/apigateway/order-desk \
-H "Authorization: Bearer <access token of jane.doe>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
CallerTools in the answer
jane.doeget_order_status, list_open_orders
sam.leeget_order_status, list_open_orders, cancel_order

Now call cancel_order with the token of jane.doe:

curl -s -X POST https://gateway.example.com/apigateway/order-desk \
-H "Authorization: Bearer <access token of jane.doe>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"cancel_order","arguments":{"orderId":"SO-10421","reason":"Customer withdrew the order"}}}'

The answer is an HTTP 200 response with a JSON-RPC error. Depending on the client, the body is JSON or an SSE data: frame:

{"jsonrpc":"2.0","id":2,"error":{"code":-32603,"message":"Tool call rejected: the target API requires policies (e.g. authentication or rate-limit) that the MCP gateway cannot apply on this path."}}
Note

The gateway uses one generic refusal message for this case. It does not tell the caller why the call is refused. You can change the text of the message on the Error Messages screen.

Argument Constraints​

This card validates the arguments of a tool call before the gateway sends the call.

Open the card

Select Configure on the Argument constraints card.

Add the rules of get_order_status

Add the tool get_order_status. Add one rule: Argument path orderId, Rule Regex (full match), Value ^SO-[0-9]{5}$.

Add the rules of cancel_order

Add the tool cancel_order. Add three rules:

Argument pathRuleValue
orderIdRegex (full match)^SO-[0-9]{5}$
reasonRequiredNot used
reasonMaximum length200
Save and deploy

Select Save. Deploy the proxy.

Argument constraints dialog with the rules of get_order_status and cancel_order

Rules that you must know:

RuleValueThe call is rejected when
Regex (full match)A regular expressionThe argument does not match the whole expression.
Maximum lengthA whole numberThe text of the argument is longer.
Allowed valuesValues separated by commasThe argument is not one of the values.
RequiredNoneThe argument is missing.
  • Regex (full match), Maximum length and Allowed values check an argument only when it is present. Add a Required rule to make the argument mandatory.
  • The argument path is a list of property names separated by dots, for example user.email. Array indexes and wildcards are not supported.
  • Environment variables (${var}) are resolved at run time.
  • The tool name must be the same as the name in tools/list. The match is case-sensitive. When two servers share a tool name, it shows as server__tool.

See Argument Constraints.

Try it​

Call get_order_status with an orderId that does not match the expression:

curl -s -X POST https://gateway.example.com/apigateway/order-desk \
-H "Authorization: Bearer <access token of jane.doe>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_order_status","arguments":{"orderId":"10421"}}}'
{"jsonrpc":"2.0","id":3,"error":{"code":-32602,"message":"Bad request. argument constraint violated: path='orderId' rule=REGEX constraint='^SO-[0-9]{5}$'"}}

Call cancel_order as sam.lee without reason:

curl -s -X POST https://gateway.example.com/apigateway/order-desk \
-H "Authorization: Bearer <access token of sam.lee>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"cancel_order","arguments":{"orderId":"SO-10421"}}}'
{"jsonrpc":"2.0","id":4,"error":{"code":-32602,"message":"Bad request. argument constraint violated: path='reason' rule=REQUIRED"}}

The message names the rule from your configuration. It never repeats the value that the client sent.

Tool Risk Policy and Pending Tool Approvals​

This card holds risky tools until a person approves the call.

Open the card

Select Configure on the Tool risk policy card.

Turn on the policy

Turn on Apply the tool risk policy. When the policy is off, every call is allowed. The exemptions and the tool risk tiers stay saved.

Set the action of each tier

Under Action per risk tier, set Low to Allow, Medium to Allow and High to Require approval.

Save and deploy

Select Save. Deploy the proxy.

Tool risk policy dialog with Low Allow, Medium Allow, High Require approval, the Exemptions list and the Tool risk tiers list

The actions of a tier are Allow, Require approval and Deny.

Tier of a Passthrough tool. The gateway uses the first rule that matches:

  1. A row in Tool risk tiers for the tool name.
  2. The annotations of the backend server: readOnlyHint gives Low and destructiveHint gives High.
  3. Medium.

In the example, get_order_status and list_open_orders are Low. cancel_order is High, so it needs approval.

Exemptions. For a matching identity (consumer, role or organization), Require approval becomes Allow. Deny does not change. The exemption checks only the identity, so it applies to every tool.

See Tool Risk Policy and Approval for the tiers and the annotation options.

Try it​

Call cancel_order as sam.lee with valid arguments:

curl -s -X POST https://gateway.example.com/apigateway/order-desk \
-H "Authorization: Bearer <access token of sam.lee>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"cancel_order","arguments":{"orderId":"SO-10421","reason":"Customer withdrew the order"}}}'

The gateway holds the call and returns:

{"jsonrpc":"2.0","id":6,"error":{"code":-32021,"message":"Tool call requires approval. Retry the same call after an administrator approves it.","data":{"approvalId":"…"}}}

If you send the same call again before the decision, the answer holds the same approvalId. The gateway does not create a second request.

Approve the call​

Open the list

On the Routing tab, open Pending tool approvals. Select Refresh if the request does not show.

Pending tool approvals list with the request of sam.lee for cancel_order, risk tier High

The list shows the environment, the tool, the risk tier, the caller, the client ID, the operation, the request time and the expiry time.

Approve the request

Select Approve. Read the question and confirm.

Approve tool call dialog: Approve the call of cancel_order by sam.lee in the test environment
Send the call again

The client sends the same call again. The gateway runs it one time and returns the tool result:

Order SO-10421 was cancelled: Customer withdrew the order

The approval is single use. The next identical call creates a new approval request. A client that supports MCP Tasks gets a task instead of the -32021 error, and it can read the result with tasks/get.

See Pending tool approvals for the permission that you need and for the audit log.

Results​

CallerCallCard that decidesAnswer
jane.doetools/listIdentity Access Rulesget_order_status, list_open_orders
sam.leetools/listIdentity Access Rulesget_order_status, list_open_orders, cancel_order
jane.doecancel_orderIdentity Access Rules-32603, generic refusal message
jane.doeget_order_status with orderId 10421Argument constraints-32602, rule=REGEX
sam.leecancel_order without reasonArgument constraints-32602, rule=REQUIRED
sam.leecancel_order with valid argumentsTool risk policy-32021 with approvalId
sam.leeThe same call after ApproveTool risk policyTool result

Limits and Behavior​

Check order. The gateway checks Identity Access Rules first, then argument constraints, then the tool risk policy. On an Expose Tools proxy, the Required Scopes check runs after the identity rules. A refusal at one step stops the call. The next steps do not run.

Error answers. The gateway returns each refusal as an HTTP 200 response with a JSON-RPC error. The body is JSON or an SSE data: frame. Read the error.code field, not the HTTP status.

Generic refusal. For a caller that the identity rules do not allow, the gateway uses one generic message on purpose. The message does not reveal why the call is refused. You can change the message text on the Error Messages screen.

Approval.

  • An approval is single use. The next identical call needs a new approval.
  • A request expires 15 minutes after it is created.
  • One caller can have at most 20 open approval requests on one proxy.
  • The approval is bound to the environment, the caller, the tool and the arguments.
  • The decisions that you make in the Manager are written to the audit log.

Troubleshooting​

ProblemCauseFix
A caller sees no tool in tools/listThe list of Identity Access Rules has rules, and no rule matches the caller.Add a rule for the consumer, the role or the organization of the caller.
A tool is missing for a caller that has the right roleA Deny entry of a matching rule lists the tool. A deny entry always wins.Remove the name from Deny.
-32603 with the message "Tool call rejected: the target API requires policies…"The identity rules do not allow the caller to call the tool.Check the rule of the role of the caller.
An argument rule has no effectThe tool name in the rule is not the same as the name in tools/list, or the argument path is wrong.Copy the name from tools/list. Use dot-separated property names.
A missing argument passes the checkRegex (full match), Maximum length and Allowed values check an argument only when it is present.Add a Required rule.
A destructive tool does not need approvalThe tool risk policy is off, an exemption matches the caller, or a Tool risk tiers row sets a lower tier.Check the switch, the Exemptions list and the Tool risk tiers list.
The request does not show in Pending tool approvalsThe request expired after 15 minutes, or it is in another environment.Select Refresh. Send the call again to create a new request.
The call after Approve returns -32021 againThe approval is single use, or the arguments of the call are not the same.Send the call with the same arguments, right after the approval.
The call returns an error about open approval requestsThe caller has 20 open approval requests.Approve or reject the open requests, or wait until they expire.

Checking the Setup​

  • When jane.doe sends tools/list, does the answer show only get_order_status and list_open_orders?
  • When sam.lee sends tools/list, does the answer show all three tools?
  • When jane.doe calls cancel_order, does the call return the generic refusal with code -32603?
  • When get_order_status gets an orderId that does not start with SO-, does the call return -32602 with rule=REGEX?
  • When cancel_order has no reason, does the call return -32602 with rule=REQUIRED?
  • When sam.lee calls cancel_order with valid arguments, does the call return -32021 with an approvalId?
  • When you send the same call again before the decision, does the answer hold the same approvalId?
  • Does the request show in Pending tool approvals with the risk tier High?
  • After Approve, does the same call return the tool result?
  • When you send the same call a second time after the approval, does the gateway create a new approval request?
  • When you turn off Apply the tool risk policy, does cancel_order run without approval?