Client Authentication
An MCP client reaches AISIX at one of two entries: the aggregated /mcp endpoint, which serves every registered server's tools under <server>__<tool> names, and /mcp/{server}, which serves one server's tools under their original names. This guide covers how a caller proves who it is at those entries.
Client authentication is independent of upstream authentication: the credential a client sends to AISIX identifies the caller to AISIX, and the credential AISIX sends to an upstream MCP server is held gateway-side and never forwarded.
Three modes are available, and an environment can combine them:
| Mode | The client sends | Use it for |
|---|---|---|
| Gateway API key | Authorization: Bearer <api key> | The default. Machine-to-machine callers and agents you issue keys to. |
| OAuth sign-in | An access token from your identity provider | Standard MCP clients that discover the sign-in flow themselves. |
| Anonymous | Nothing | Clients on trusted networks that cannot present a credential. |
Whatever the mode, the caller ends up as an API key principal: its tool grant, rate limits, budget, guardrails and usage attribution all apply. That is why anonymous callers stay as governable as authenticated ones.
Prerequisites
Before starting, prepare the following:
- For AISIX Cloud, an environment and a write-scoped admin token. For On-Premises, follow the AISIX Cloud Quickstart. To request Hybrid Cloud access, contact API7.
- For an open-source AISIX gateway, a gateway that loads a declarative resources file. Set Up MCP Gateway provides a working MCP server entry and the validation and reload workflow.
- cURL for the AISIX Cloud examples.
Gateway API Key
This is the default and needs no configuration. A client sends its key on every request:
curl -sS -X POST "$AISIX_GATEWAY/mcp" \
-H "Authorization: Bearer $AISIX_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
The key's grant decides which tools the caller sees and can call. See Control Tool Access for scoping a key to specific tools, whole servers, or every tool, and MCP Access Policies for granting at the environment or team level.
x-api-key: <api key> is accepted as an alternative header.
OAuth Sign-In
Standard MCP clients such as desktop assistants can sign a user in instead of asking them to paste a key. They do it by reading the WWW-Authenticate header on a 401, fetching the protected resource metadata it points at, and running the OAuth flow against the authorization server named there.
AISIX publishes that metadata once an environment has both:
- a canonical MCP resource URL — the public URL clients use to reach this environment's
/mcpendpoint; and - at least one enabled OIDC trust provider, which is the authorization server tokens must come from.
With both configured, GET /.well-known/oauth-protected-resource (and its /.well-known/oauth-protected-resource/mcp sibling) returns the resource identity, the issuers tokens may come from, and the scopes they must carry. Without them the routes return 404 and 401 responses carry no challenge, exactly as before the feature existed.
Access tokens must include the resource URL in their audience claim. This is the most common configuration mistake: a token minted for a different audience is rejected at the gateway even though sign-in succeeded.
AISIX Cloud
Set the resource URL on the environment:
curl -sS -X PATCH "$AISIX_CP/environments/$ENV_ID" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mcp_resource_url": "https://gateway.example.com/mcp"
}'
The URL must be an absolute http or https URL whose path is exactly /mcp, with no query or fragment, and no embedded credentials — it is published on an unauthenticated endpoint. Send null to clear it and turn discovery off.
In the dashboard, the same setting lives on the environment's OIDC Providers page, which also warns when an enabled provider's audiences do not include the URL.
Open-Source AISIX Gateway
Add the settings row and a trust provider to resources.yaml:
_format_version: "1"
mcp_auth_settings:
- resource_url: https://gateway.example.com/mcp
oidc_providers:
- name: corp-sso
issuer: https://sso.example.com/realms/agents
audiences:
- https://gateway.example.com/mcp
required_scopes:
- mcp:tools
At most one mcp_auth_settings entry may exist. A second one is rejected at load, and if a duplicate ever reaches a running gateway the discovery surface stays off rather than picking one.
Anonymous Access
Anonymous access lets clients that present no credential reach entries you open for them. It exists for fleets migrating from a gateway that never required a credential, where changing every client is not practical.
An anonymous request still runs as a principal you choose — an API key in the environment — so tool grants, rate limits, budgets, guardrails and usage attribution keep applying. Nothing is skipped; the credential check is what is replaced.
Anyone who can reach the gateway from the allowed networks can call the permitted tools without a credential, and the usage counts against the environment. Treat the source network allowlist as the access control it is, and keep the principal's tool grant as narrow as the clients actually need.
What Anonymous Is Not
It is not a downgrade path. A request that presents a credential is authenticated normally, and an invalid, expired, disabled or malformed one is rejected with 401. Only a request that presents nothing at all takes the anonymous path. A wrong authentication scheme or an empty header value counts as presenting something, so a client that tries to authenticate and gets it wrong fails rather than quietly succeeding with a different identity.
It is not visible to callers who are not allowed in. Every refusal — source outside the allowlist, a server not offered anonymously, the principal deleted or disabled, anonymous access off — answers the same 401 the entry gives without anonymous access at all. Callers cannot tell those apart, nor tell a registered MCP server from one that does not exist. Operators see the reason on the gateway's aisix_auth_decisions_total metric.
Configuration
Anonymous access is configured per environment:
| Field | Meaning |
|---|---|
api_key_id | The API key anonymous traffic runs as. |
source_cidrs | Client source networks allowed in. Required and non-empty. |
servers | MCP servers anonymous callers may reach. Required and non-empty. |
aggregate_entry | Whether the aggregated /mcp endpoint also serves anonymous callers. Off by default. |
enabled | Set to false to close anonymous access while keeping the configuration. Defaults to true. |
Two of these deserve more than a one-line description.
The Server List Is a Ceiling
servers is not only the list of /mcp/{server} entries to open — it is the limit on what the principal can reach anywhere, including through the aggregated endpoint. Without that, a principal whose own tool grant is wider than the list could name <server>__<tool> on the aggregated endpoint and reach a server whose per-server entry is closed.
So the effective grant of an anonymous caller is the listed servers' tools intersected with the principal's own grant. Both tools/list and tools/call follow it, which is why an anonymous caller never sees a tool it could not call.
A newly registered MCP server is never anonymous by default. Reaching anonymous callers is always a name added to this list.
The Principal Needs Its Own Grant
The principal must carry an explicit MCP grant. A key set to inherit the environment-default access policy is rejected: its tools would then follow that policy wherever it goes, so setting the policy to grant everything would hand every registered tool to anonymous callers without anyone revisiting this setting. A key that narrows what it inherits is accepted, as is a key with an explicit tool allowlist.
Configure in AISIX Cloud
Set the block on the environment:
curl -sS -X PATCH "$AISIX_CP/environments/$ENV_ID" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mcp_anonymous": {
"api_key_id": "'"$ANON_KEY_ID"'",
"source_cidrs": ["10.0.0.0/8"],
"servers": ["docs"],
"aggregate_entry": false
}
}'
❶ The principal anonymous traffic runs as. It must belong to this environment and carry its own MCP grant.
❷ Matched against the source address AISIX resolves through its real-IP configuration, never a header the client supplies. Use 10.0.0.1/32 for a single address.
❸ Approved servers exposed to this environment. Anonymous callers reach /mcp/docs and nothing else.
❹ Leave the aggregated endpoint on gateway credentials. See Anonymous Access and OAuth Sign-In before turning it on.
Send "mcp_anonymous": null to turn anonymous access off. The change reaches running gateways without a restart.
In the dashboard, the same settings live on the environment's OIDC Providers page, where enabling anonymous access requires an explicit risk acknowledgement.
Configure in an Open-Source Gateway
Add the block to the mcp_auth_settings entry in resources.yaml:
_format_version: "1"
mcp_auth_settings:
- anonymous:
api_key_id: 6fbea7f2-88a7-4cbb-8dca-a0ad785d07c5
source_cidrs:
- 10.0.0.0/8
servers:
- docs
aggregate_entry: false
api_key_id is the id of an api_keys entry in the same configuration. The resource_url field for OAuth discovery lives on this same entry; the two settings are independent, and either can be configured without the other.
Anonymous Access and OAuth Sign-In
An environment can run both, and for most deployments the natural split is: existing clients that cannot present a credential use the per-server entries anonymously, while standard MCP clients sign in through the aggregated /mcp.
Turning on aggregate_entry in an environment that also publishes OAuth discovery changes that. A no-credential request to /mcp then succeeds instead of returning the 401 that carries the discovery hint, so OAuth-capable clients never start the sign-in flow and stay on the anonymous grant. The per-server entries are unaffected.
Verify
Confirm the mode you configured behaves as intended.
An anonymous call to a listed entry succeeds with no credential:
curl -sS -X POST "$AISIX_GATEWAY/mcp/docs" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
The response lists the tools the principal's grant allows on that server, under their original names.
A bad credential is still rejected, rather than served anonymously:
curl -sS -o /dev/null -w '%{http_code}\n' -X POST "$AISIX_GATEWAY/mcp/docs" \
-H "Authorization: Bearer not-a-real-key" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
The gateway returns 401.
Observability
Anonymous traffic is attributable. Usage events carry the principal's API key id like any other request, plus auth_type: anonymous, which distinguishes traffic that inherited the principal from an entry from traffic that presented that key's own credential. Authentication decisions, including refusals and their reasons, are counted on aisix_auth_decisions_total.
Limitations
Anonymous access is designed for trusted networks. Two capacity protections that authenticated deployments can rely on are not yet in place for it:
- Per-source-IP rate limiting is not available. A single anonymous client can consume the principal's whole quota, since all anonymous traffic shares one principal.
- The
initialize,pingandtools/listmethods are not metered. Onlytools/callpasses the rate-limit and budget gate.
The mandatory source network allowlist is what keeps this bounded. Do not expose anonymous entries to untrusted networks.
Next Steps
- Control tool access: scope a key — including an anonymous principal — to specific tools or whole servers.
- Rate limits and budgets: bound what a principal can spend.
- Guardrails: inspect MCP tool arguments and results.
- Observability: find MCP traffic in logs, metrics and usage.