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 by default the credential AISIX sends to an upstream MCP server is held gateway-side. A server configured to act on the caller's behalf receives the caller's own token or one exchanged for it, never the gateway key.
Four 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. |
| Interactive sign-in | A session token the gateway issues after a browser sign-in | Clients configured with only a URL, reaching a server where each user has their own upstream account. |
| Anonymous | Nothing | Clients on trusted networks that cannot present a credential. |
Whatever the mode, the caller resolves to an API key principal. Its tool grant determines what it can list and call. Configured rate limits and guardrails can govern tool calls, while usage remains attributed to the principal. In AISIX Enterprise and AISIX Cloud, matching budgets also apply. Anonymous callers therefore remain subject to the same controls as authenticated callers.
Prerequisites
Before starting, prepare the following:
- Complete Set Up MCP Gateway for AISIX Enterprise, AISIX Cloud, or an open-source AISIX gateway, and retain the
AISIX_PROXYandAISIX_MCP_KEYvalues used at the end of that guide. - For AISIX Enterprise, retain
AISIX_CP,AISIX_TOKEN, andENV_IDfrom the AISIX Enterprise getting-started guide. For AISIX Cloud, obtain those values from your organization. To request access, contact API7. - cURL for the request 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_PROXY/mcp" \
-H "Authorization: Bearer $AISIX_MCP_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.
A client that must also send its own token for an upstream server in Authorization, as a server with auth_type: oauth_delegate expects, sends the gateway key in x-aisix-api-key instead:
curl -sS -X POST "$AISIX_PROXY/mcp" \
-H "x-aisix-api-key: $AISIX_MCP_KEY" \
-H "Authorization: Bearer $UPSTREAM_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
When x-aisix-api-key is present, AISIX authenticates the caller with it alone and does not read Authorization or x-api-key. See Send the Gateway Key in x-aisix-api-key for the full precedence rules.
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 in JWKS mode, which is the authorization server tokens must come from. A shared-secret (HMAC) provider does not publish OAuth discovery metadata, but can still be used for JWT authentication.
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.
The gateway checks a token's aud claim against the audiences of the trust provider that issued it, and rejects the token when none of them match, even though sign-in succeeded. It never compares aud with the resource URL, which is used only for the discovery metadata. So set audiences to what your identity provider actually issues:
- An MCP client following the OAuth flow sends the resource URL as the
resourceparameter. With an identity provider that honors it, the token's audience is that URL, any path prefix included, andaudiencesmust list it as written. - An identity provider that issues a different audience needs that value in
audiencesinstead. For Microsoft Entra ID with OBO token exchange, that is the token'saudclaim, which for a v2.0 access token is the gateway's client ID.
If the provider also accepts other audiences, a token matching any of them can pass the audience check, so narrow the list to the resource isolation you intend. The token must still pass the other checks, such as signature, issuer, expiry, and scope.
You must also resolve the verified identity to a valid caller API key, either by binding the identity directly with the key's jwt_provider and jwt_subject, or through JWT claim mappings. The key must carry the required MCP tool grant. Signing in at the identity provider or setting the discovery metadata does not create an identity binding or grant tool access.
AISIX Enterprise or 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 managed control plane requires an absolute http or https URL whose path ends with /mcp, with no query or fragment. The path must be percent-encoded and cannot contain empty, . or .. segments. Each environment has one resource URL. If a gateway in front of AISIX routes by path prefix, include the prefix, as in /team-a/mcp; see Run Behind a Path-Prefix Gateway. The URL is published as written on an unauthenticated endpoint, user information (user:password@) included, so do not put credentials in it. Send null to clear it and turn discovery off.
For file-defined settings, OAuth discovery activates only when the URL parses as an absolute HTTP(S) URL with no query or fragment and a path ending in /mcp. Use a percent-encoded path without empty, . or .. segments, which is also what the managed control plane requires. The resources-file path accepts some non-canonical spellings that the managed API refuses, but a gateway in front of AISIX can normalize them differently from the public resource URL AISIX publishes.
In the dashboard, the same setting lives on the environment's MCP Access page, which also warns when an enabled provider's audiences do not include the URL. This is only a warning and does not block the save, so an identity provider that issues a different audience, such as Microsoft Entra ID, is expected to show it.
AISIX OSS
Set resource_url on the singleton mcp_auth_settings entry, creating it if absent, and keep any anonymous settings unchanged. Add corp-sso to oidc_providers:
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.
For a file-defined setting, OAuth discovery activates only when the URL parses as absolute HTTP(S), carries no query or fragment, and has a path ending in /mcp. Use a percent-encoded path without empty, . or .. segments, as the managed control plane requires. The resources-file path accepts some non-canonical spellings that the managed API refuses, but a front gateway can normalize them differently from the public resource URL that AISIX publishes.
Run Behind a Path-Prefix Gateway
If a gateway in front of AISIX shares one domain and routes by path prefix, configure the public URL, such as https://gateway.example.com/team-a/mcp, as the resource URL.
Anything before /mcp is a prefix the front gateway strips before forwarding. The routes on AISIX itself do not change, so the front gateway must forward:
| Public path | Path AISIX receives |
|---|---|
/team-a/mcp | /mcp |
/team-a/mcp/{server} | /mcp/{server} |
/team-a/.well-known/oauth-protected-resource/mcp | /.well-known/oauth-protected-resource/mcp |
It must also pass the WWW-Authenticate header on 401 responses back unchanged. The resource_metadata in that challenge becomes https://gateway.example.com/team-a/.well-known/oauth-protected-resource/mcp, and the resource in the metadata is the configured URL as written.
Without a front gateway that strips the prefix, requests to that path get 404 from AISIX, so do not configure a prefix in that case.
The client must follow resource_metadata from the 401. A client that builds the well-known URL from the origin instead will not reach this gateway on a shared domain.
A prefixed path requires a gateway released after 1.8.0. A plain /mcp URL has no version requirement. While a gateway at 1.8.0 or earlier, at or above the oldest supported version and with a heartbeat in the last 5 minutes, is registered in the environment, the AISIX Cloud Admin API and the dashboard refuse any save that writes this value, including a change to mcp_anonymous, with 422 and the error code DP_INCOMPATIBLE. To proceed, upgrade the gateway, or clear the URL in the same PATCH. A gateway at 1.8.0 or earlier that joins after the save leaves OAuth discovery off, with only a warning in that gateway's log.
Interactive Sign-In
For a server with auth_type: oauth2_authorization_code, the gateway can run the whole sign-in itself. The client is configured with /mcp/{server} and nothing else, the user signs in to your identity provider and authorizes the upstream once in a browser, and the client then holds a session token issued by the gateway. See Interactive MCP Sign-In.
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 resolves to an API key principal you choose. Its tool grants determine what it can list and call. Configured rate limits and guardrails can govern tool calls, while usage remains attributed to the principal. In AISIX Enterprise and AISIX Cloud, matching budgets also apply. Only the credential check changes.
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 no credential takes the anonymous path. A credential header that is present counts as a credential the caller offered, whatever its value. That covers x-aisix-api-key, Authorization, and x-api-key. Such a header is authenticated even when it is unusable, such as an empty or whitespace-only value, a wrong authentication scheme, or Bearer followed by no token, so a client that tries to authenticate and gets it wrong fails with 401 rather than quietly succeeding with a different identity. Only a request that carries none of the three headers presents no credential, and it takes the anonymous path only when the anonymous configuration, source network, entry, and principal grant all allow it.
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.
Each entry identifies one registered server, not a pattern. AISIX rejects server names containing *, so the ceiling cannot expand to other servers. A renamed server stays on the allowlist and reads back under its new name; deleting it leaves an inactive entry. See Rename or Delete an MCP Server for re-creation and cleanup behavior.
The Principal Needs Its Own Grant
In AISIX Enterprise and AISIX Cloud, the principal must carry its own mcp_access block. A key without one is rejected, because it would take whatever the environment and team layers leave — so a later policy change could widen anonymous access without anyone revisiting this setting. A key with its own block is bounded by its own allow list whatever those layers do.
In an open-source AISIX gateway configured with a resources file the same block is the principal's only layer, since the file has no policy collection. The servers list remains an additional ceiling on it.
Configure in AISIX Enterprise or AISIX Cloud
Export the ID of the API key that anonymous traffic should run as. The key must belong to the environment and carry the MCP grant described above:
export ANON_KEY_ID="YOUR_ANONYMOUS_PRINCIPAL_API_KEY_ID"
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": ["everything"],
"aggregate_entry": false
}
}'
❶ The API key principal that anonymous traffic runs as. It must belong to this environment and carry its own mcp_access block.
❷ 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/everything 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 MCP Access page, where enabling anonymous access requires an explicit risk acknowledgement.
Configure in AISIX OSS
Add anonymous-mcp to api_keys. In the singleton mcp_auth_settings entry, add anonymous; create the entry if it is absent. Keep resource_url, the everything server, and the other resources unchanged. The ID below is derived from anonymous-mcp:
api_keys:
- display_name: anonymous-mcp
key_env: ANONYMOUS_MCP_KEY
allowed_models: []
mcp_access:
allow:
- everything__*
mcp_auth_settings:
- anonymous:
api_key_id: d6869ae7-741a-598e-8213-16672e922546
source_cidrs:
- 10.0.0.0/8
servers:
- everything
aggregate_entry: false
Set ANONYMOUS_MCP_KEY in the gateway process environment before loading the file. If you use a different API key display name, replace api_key_id with that entry's deterministic derived ID. The resource_url field for OAuth discovery lives on the same mcp_auth_settings 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_PROXY/mcp/everything" \
-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_PROXY/mcp/everything" \
-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 gate and checks applicable budgets.
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: apply caller rate limits and use budgets that cover the caller API key.
- Guardrails: inspect MCP tool arguments and results.
- Observability: find MCP traffic in logs, metrics and usage.