# MCP gateway security

Require authentication on every gateway with API keys, Basic Auth or bearer tokens.

Every [MCP gateway](https://zato.io/docs/ai/mcp/index.html) requires authentication - a gateway cannot be left open. An AI agent presents a credential with each request, the credential resolves to a security definition, and that definition is the agent's identity everywhere else: in sessions, in the [audit log](https://zato.io/docs/ai/mcp/audit-log.html) and in [rate limiting](https://zato.io/docs/ai/mcp/rate-limits.html).

## Credential types {#credential-types}

| Type | How the agent sends it |
| --- | --- |
| [API key](https://zato.io/docs/dev/rest/authentication.html) | The `X-API-Key` header - the header name can be redefined through an environment variable |
| [Basic Auth](https://zato.io/docs/dev/rest/authentication.html) | The standard `Authorization` header, e.g. `curl -u user:password` |
| [Bearer token](https://zato.io/docs/security/bearer-tokens/index.html) | `Authorization: Bearer <token>` - a static token or a JWT verified against a JWKS endpoint, with audience and claim filters |
| [OAuth](https://zato.io/docs/ai/mcp/oauth.html) | `Authorization: Bearer <token>` too, with the token obtained by the person signing in through your identity provider from inside their AI client - the gateway's OAuth field on, and a bearer token definition with an issuer and an audience in its group |

## Assign definitions to a gateway {#assign-definitions-to-a-gateway}

The **Security** picker on the wizard's step 01, **How do agents connect?**, lists the available [API key](https://zato.io/docs/security/api-keys.html) and [Basic Auth](https://zato.io/docs/security/basic-auth.html) definitions. Each gateway has a [security group](https://zato.io/docs/security/groups.html) named `mcp.<gateway-name>` and the picker manages that group's members - the same group is visible under **Security** > **Groups**, which is also where bearer token definitions are added to a gateway.

A request is accepted when any member of the group matches the presented credentials. Membership changes are live - adding or removing a member is reflected in enforcement immediately, without restarts.

When a JWT matches a claim-filtered bearer definition, the resolved identity is that specific definition - two definitions with different claim filters on one gateway resolve to different identities, so a session initialized with one token cannot be reused by a caller whose token resolves to the other. A definition with an identity claim goes one step further and resolves each person to an identity of their own, `<definition>/<claim value>`, so that two people let in by one definition are two callers. The details are on the [bearer tokens](https://zato.io/docs/security/bearer-tokens/index.html) page.

## Rejections {#rejections}

A request without valid credentials never reaches any tool, and how it is refused depends on the gateway's [OAuth](https://zato.io/docs/ai/mcp/oauth.html) field:

- With OAuth off, the request is refused with HTTP 403 and an empty body.
- With OAuth on, the request is refused with HTTP 401 and a `WWW-Authenticate` header naming the gateway's metadata document, from which an MCP client learns where to send the person to sign in. A request with a token the gateway did not accept also gets `error="invalid_token"` in the header:

```text
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource/mcp/billing", error="invalid_token"
```

Each rejection is logged in the server log with the gateway, the security definition and the username involved, e.g.:

```text
MCP gateway `billing` rejected unauthenticated request (sec name=`partner.key` username=`partner1`)
```

With the [audit log](https://zato.io/docs/ai/mcp/audit-log.html) on, rejections are recorded too, as `auth-failed` events that say why the token was refused and, when it could be read, who presented it.

## Origin validation {#origin-validation}

For gateways reachable from browsers, the `Zato_MCP_Check_Origin` environment variable, set to `true`, additionally validates the `Origin` header of incoming requests against the gateway's list of allowed origins - a request whose origin is not on the list is refused with HTTP 403 and logged:

```text
MCP gateway `billing` rejected origin `https://evil.example` (sec name=`partner.key` username=`partner1`)
```

With validation off, which is the default, the header is ignored. Requests without the header, e.g. from command-line clients, are never affected either way.

> **Note:** The same security definitions and groups also protect [REST channels](https://zato.io/docs/dev/rest/authentication.html) - one credential can serve both an agent-facing gateway and the REST APIs behind it, with one shared identity. What the shared identity means for quotas is under [rate limits](https://zato.io/docs/ai/mcp/rate-limits.html).

## See also {#see-also}

- [Rate limits](https://zato.io/docs/ai/mcp/rate-limits.html) - Traffic caps configured on the same security definitions
- [Audit log](https://zato.io/docs/ai/mcp/audit-log.html) - How accepted and rejected requests are recorded
- [Bearer tokens](https://zato.io/docs/security/bearer-tokens/index.html) - Static tokens and JWTs with JWKS, audience and claim filters
- [OAuth](https://zato.io/docs/ai/mcp/oauth.html) - Sign-in through your identity provider in place of a key
- [REST authentication](https://zato.io/docs/dev/rest/authentication.html) - The same definitions and groups protecting REST channels

## Learn more {#learn-more}

- [MCP tutorial](https://zato.io/tutorials/mcp/01.html) - Expose Python services as AI tools and connect Claude Code in minutes
- [AI Integrations overview](https://zato.io/docs/ai/) - Both directions of AI traffic in one platform, and where to start
- [MCP gateway reference](https://zato.io/docs/ai/mcp/) - Configuration, endpoint behavior and the governance controls
- [Tool schemas](https://zato.io/docs/ai/mcp/tool-schemas.html) - How a service's docstring and declared I/O become its tool definition
- [MCP response controls](https://zato.io/docs/ai/mcp/response-controls.html) - PII removal, prompt-injection safeguards and token-denominated size caps
- [MCP audit log](https://zato.io/docs/ai/mcp/audit-log.html) - What each agent did, when and with what outcome - payloads never recorded
- [MCP alerts](https://zato.io/docs/ai/mcp/alerts.html) - Failing backends, agents stuck in a loop and gateways with too many tools
- [Cost and limits](https://zato.io/docs/ai/cost-and-limits.html) - Every setting that caps AI cost and traffic, with worked examples
- [GitOps for AI integrations](https://zato.io/docs/ai/enmasse.html) - LLM connections and MCP gateways as YAML in version control
- [AI examples](https://zato.io/docs/ai/examples/) - Complete, runnable scenarios - one real-world need per file
