Your agent can connect to an MCP server. It can discover tools. It can even create a refund. None of that proves your MCP gateway security is correct: the important question is whether this user, through this client, is allowed to refund this order.
A gateway can authenticate a connection while the application still exposes the wrong operation or another tenant’s data. That gap matters more than a successful demo. Once a tool can change a business system, authorization becomes part of the product’s behavior.
The short answer: Authenticate the caller, authorize the tool, then authorize the specific business resource. A gateway helps enforce policy, but it does not replace domain-level checks or make model output trustworthy.
This is an architecture guide, not a report of an MDA Assessment deployment. The example is illustrative. Protocol requirements below refer to the dated MCP authorization specification from November 25, 2025; sources were checked on September 9, 2026.
Why one MCP endpoint needs multiple policies
An HTTP API often exposes visibly different paths for reads and writes. An MCP endpoint can carry multiple operations through the same transport endpoint, including tool discovery and tool execution.
That makes “allow access to /mcp” an incomplete policy. A client that may call get_invoice should not automatically receive permission to call refund_payment or delete_customer.
There are at least three questions to answer:
| Boundary | Question | Example control |
|---|---|---|
| Connection | Who is making this request? | Validate a token for the intended MCP resource |
| Tool | Which capability may they invoke? | Default-deny grants for named tools |
| Business resource | Which records may that capability touch? | Tenant, account, ownership, and state checks |
The same separation appears in my architecture walkthrough: the API edge controls entry, while a service still owns its business invariants. Adding an AI client does not eliminate those responsibilities.
A practical review starts by naming the capabilities. “Finance access” is too broad. Reading invoice totals, downloading invoice attachments, initiating refunds, and changing payout destinations need different policies and different audit expectations.
Separate the gateway from the business service
A useful starting architecture is:
User + MCP client
|
| Token intended for the MCP resource
v
MCP-aware gateway / protected resource
- authentication enforcement
- tool-level policy
- request limits and audit context
|
| Controlled identity delegation
v
MCP tool adapter
- input schema validation
- maps tool calls to application use cases
|
v
Business service
- tenant and record authorization
- domain rules and transaction boundaries
- idempotency for retryable writes
The identity provider and authorization server are deliberately outside this request-path sketch. They issue credentials; the resource boundary validates and uses those credentials under a defined trust relationship.
The exact split depends on the platform. Some gateways expose REST operations as MCP tools. Other deployments proxy an existing MCP server. In either case, document which component validates the token, where authorization is evaluated, and how the downstream service receives a trustworthy caller identity.
Do not place an unprotected service behind a gateway and assume the network will remain private forever. Restrict upstream access, use authenticated service-to-service communication where appropriate, and prevent callers from injecting trusted identity headers.
MCP authorization requires the right token audience
For HTTP-based MCP authorization, the dated authorization specification defines requirements around OAuth, resource discovery, and access tokens. Local stdio processes have a different operating model; putting a remote OAuth diagram over every local tool process would be misleading.
For a protected HTTP resource, “the token has a valid signature” is not enough. You also need to establish that the token was issued by the expected authority, is valid now, and is intended for this resource. With JWT access tokens, audience validation is a critical part of that check.
The specification requires clients to use the OAuth resource parameter and requires MCP servers to accept only access tokens intended for them. Follow the discovery flow supported by your chosen protocol version and authorization server rather than hard-coding guessed endpoints.
A particularly dangerous shortcut is forwarding the incoming MCP token unchanged to every downstream API. The specification explicitly prohibits this token passthrough. If the MCP server calls another API, that API needs an appropriately issued token or another deliberately designed credential relationship.
This does not mean every deployment must use one specific token-exchange product. It means the trust model must be explicit. A token accepted by resource A does not become a credential for resource B merely because an agent requested the call.
Authorize tool discovery and tool execution
Filtering tools/list is useful. It reduces irrelevant capabilities in the client’s view and helps avoid presenting a tool the caller cannot use.
It is not sufficient enforcement. A caller may already know the name of a hidden tool and send tools/call directly. Check authorization again at execution time, using the current authenticated identity and current policy.
A basic grant model could look like this:
| Capability | Support reader | Finance operator | Human approval |
|---|---|---|---|
get_invoice | Allowed for assigned accounts | Allowed within authorized scope | Usually not required |
create_refund_request | Denied | Allowed within authorized scope | Required before execution |
execute_refund | Denied | Not granted to the general agent | Separate approved workflow |
These are illustrative business policies, not MCP defaults. They deliberately distinguish asking for an action from executing it.
Kong’s MCP Tool ACL announcement describes fine-grained tool authorization in AI Gateway 3.13, including identity-based filtering, consumer groups, and default-deny policies. That is evidence of a product capability, not proof that it is enabled in an existing gateway.
Check the current plugin documentation, supported deployment mode, version, and licensing before planning an implementation. A generic Kong route with authentication is not automatically an MCP-aware tool policy.
Keep tenant checks in the application
A tool allowlist answers “may this principal use get_invoice?” It cannot, by itself, answer “may this principal read invoice 742 from this account?”
The latter decision depends on application data. Put it next to the use case that owns that data, not in the model prompt.
Here is a deliberately small, pure TypeScript policy function:
type Principal = {
tenantId: string;
tools: readonly string[];
accountIds: readonly string[];
};
type Invoice = {
tenantId: string;
accountId: string;
};
function canReadInvoice(
principal: Principal,
invoice: Invoice,
): boolean {
// A grant to the tool is not a grant to every tenant's records.
return (
principal.tools.includes("get_invoice") &&
principal.tenantId === invoice.tenantId &&
principal.accountIds.includes(invoice.accountId)
);
}
This is not authentication middleware or a complete MCP server. The principal must come from verified identity and trusted authorization data—not from tool arguments, an unsigned header, or a field invented by the model.
Likewise, obtain the invoice’s tenant and account from trusted storage. Scope the lookup to the caller’s tenant where possible, then apply record-level policy. Do not fetch a global invoice, serialize it, and only afterward decide whether the caller should have seen it.
For a larger application, permissions may depend on teams, delegated authority, ownership, or policy-service decisions rather than an in-memory account list. The important boundary remains the same: the service decides, using authoritative context.
Return only the fields the tool needs. Correct record authorization does not justify exposing internal notes, payment details, or unrelated personal information in the response.
Design approval as an executable boundary
A prompt that says “ask before refunding” describes desired behavior. It does not enforce that behavior.
For a high-impact action, separate proposal from execution. The agent can prepare a refund request. A server-side workflow can then require approval from an authorized human before a separate execution capability becomes available.
Bind that approval to the exact action: tenant, resource, amount, currency, and an expiration. If the proposed amount changes after approval, require a new decision. Re-check authorization and relevant business state when the action executes.
Retries also need a policy. Network timeouts do not tell you whether a refund happened. Use an idempotency mechanism tied to the intended operation, and distinguish a repeated request from a new request with different parameters.
The gateway may limit traffic, but it cannot infer those domain rules. Ten calls per minute is not a financial authorization model.
Test the denial paths before the demo path
A happy-path tool call proves connectivity. Production confidence comes from verifying that the wrong calls are denied without leaking data or causing side effects.
Start with a small, explicit matrix:
| Test | Expected result |
|---|---|
| Missing or invalid access token | Rejected before tool execution |
| Valid token for a different resource | Rejected at the resource boundary |
| Hidden tool called directly | Denied even if its name is known |
| Allowed read tool, different tenant | No record data returned |
| Allowed tenant, unassigned account | No record data returned |
| Revoked permission after discovery | Execution uses the new policy |
| Refund parameters changed after approval | Existing approval is insufficient |
| Retried approved write | No duplicate business operation |
Test bypass paths too. Can someone reach the MCP server without the gateway? Can a caller supply the identity header that the service trusts? Does a cached decision survive longer than the authorization policy permits?
Use your actual transport and chosen SDK to verify error behavior. Avoid forcing every denial into one guessed HTTP status: the appropriate response can depend on whether the failure occurs at authentication, protocol handling, or tool execution.
Audit decisions without logging secrets
A useful audit record connects the user, client, tool, target, policy decision, and resulting operation. It should help an operator explain why an action was permitted—not just show that an HTTP request returned successfully.
Capture a correlation ID, trusted principal reference, tenant, tool name, decision, and policy version where available. For writes, record the approval reference and idempotency reference under your retention policy.
Do not log bearer tokens. Avoid recording full prompts and tool arguments by default: they can contain credentials, personal data, or business-sensitive content. Redaction and controlled retention are part of the design, not cleanup work after launch.
The MCP security best-practices guide also covers risks beyond authorization, including confused-deputy problems and local-server compromise. Tool allowlists do not solve prompt injection, unsafe execution, or unrestricted outbound requests by themselves.
Key takeaways
- Treat connection, tool, and business-resource authorization as separate decisions.
- Validate credentials for the intended MCP resource; do not pass incoming tokens through to unrelated APIs.
- Filter discovery for usability, but enforce permissions again during execution.
- Keep tenant boundaries and high-impact approval rules in trusted application code.
- Test denials, bypasses, and retries—not only the successful demo.
If you are organizing the coding agents themselves, read the Herdr CLI walkthrough. If you are planning the business-system boundary they will access, let’s discuss the architecture.