MCP Authentication
Agent Gateway supports the MCP Authorization specification, enabling OAuth-based authentication for MCP endpoints. This tutorial shows you how to secure MCP tools with token-based access.
What you’ll build
In this tutorial, you’ll:
- Configure MCP authentication with JWT token validation
- Expose OAuth protected resource metadata
- Test authenticated and unauthenticated requests
- Understand the MCP authentication flow
Prerequisites
- Node.js installed (for the MCP server)
Step 1: Install Agent Gateway
curl -sL https://raw.githubusercontent.com/agentgateway/agentgateway/refs/heads/main/common/scripts/get-agentgateway | bashStep 2: Download test keys
Create a directory for this tutorial:
mkdir mcp-auth-tutorial && cd mcp-auth-tutorialDownload the pre-generated test keys from the Agent Gateway repository:
# Download the JWKS public key
curl -sL https://raw.githubusercontent.com/agentgateway/agentgateway/main/manifests/jwt/pub-key -o pub-key
# Download a pre-generated test JWT token
curl -sL https://raw.githubusercontent.com/agentgateway/agentgateway/main/manifests/jwt/example1.key -o test-token.jwtStep 3: Create the config
Create a configuration file with MCP authentication enabled:
cat > config.yaml << 'EOF'
binds:
- port: 3000
listeners:
- routes:
- backends:
- mcp:
targets:
- name: everything
stdio:
cmd: npx
args: ["@modelcontextprotocol/server-everything"]
matches:
- path:
exact: /mcp
- path:
exact: /.well-known/oauth-protected-resource/mcp
policies:
cors:
allowOrigins: ["*"]
allowHeaders: ["*"]
exposeHeaders: ["Mcp-Session-Id"]
mcpAuthentication:
mode: strict
issuer: agentgateway.dev
audiences: [test.agentgateway.dev]
jwks:
file: ./pub-key
resourceMetadata:
resource: http://localhost:3000/mcp
scopesSupported:
- read:all
bearerMethodsSupported:
- header
EOFKey configuration:
mode: strict- Requires valid JWT for all requests (options:strict,optional,permissive)issuer- Expected JWT issuer claimaudiences- Expected JWT audience claimjwks.file- Path to public key for token validationresourceMetadata- OAuth protected resource metadata for MCP clients
Step 4: Start Agent Gateway
agentgateway -f config.yamlYou should see:
INFO agentgateway: Listening on 0.0.0.0:3000
INFO agentgateway: Admin UI available at http://localhost:15000/ui/Step 5: Test without authentication
Try to access the MCP endpoint without a token:
curl -s -i http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}'You should get a 401 Unauthorized response:
HTTP/1.1 401 Unauthorized
www-authenticate: Bearer resource_metadata="http://localhost:3000/.well-known/oauth-protected-resource/mcp"
content-type: application/json
{"error":"unauthorized","error_description":"JWT token required"}Notice the www-authenticate header points to the protected resource metadata endpoint.
Step 6: Check the protected resource metadata
MCP clients can discover authentication requirements from the metadata endpoint:
curl -s http://localhost:3000/.well-known/oauth-protected-resource/mcp | jq .Response:
{
"resource": "http://localhost:3000/mcp",
"authorization_servers": ["agentgateway.dev"],
"mcp_protocol_version": "2025-06-18",
"resource_type": "mcp-server",
"bearer_methods_supported": ["header"],
"scopes_supported": ["read:all"]
}This tells clients:
- Which authorization servers to use
- What scopes are required
- How to send the bearer token (header, body, or query)
Step 7: Test with a valid token
Now test with the pre-generated JWT token:
curl -s -i http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer $(cat test-token.jwt)" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}'You should see a successful 200 OK response with the MCP session initialized.
Authentication Modes
| Mode | Behavior |
|---|---|
strict |
Requires valid JWT for all requests. Returns 401 if missing or invalid. |
optional |
Validates JWT if present, but allows unauthenticated requests. |
permissive |
Attempts validation but doesn’t enforce it. Useful for debugging. |
MCP Authentication Flow
When an MCP client connects to a protected resource:
- Client requests resource - Sends initialize request to
/mcp - Gateway returns 401 - Includes
www-authenticateheader with metadata URL - Client fetches metadata - Gets
/.well-known/oauth-protected-resource/mcp - Client authenticates - Obtains token from authorization server
- Client retries with token - Includes
Authorization: Bearer <token>header
Keycloak Integration
For production with Keycloak:
binds:
- port: 3000
listeners:
- routes:
- backends:
- mcp:
targets:
- name: tools
stdio:
cmd: npx
args: ["@modelcontextprotocol/server-everything"]
matches:
- path:
exact: /mcp
- path:
exact: /.well-known/oauth-protected-resource/mcp
- path:
exact: /.well-known/oauth-authorization-server/mcp
- path:
exact: /.well-known/oauth-authorization-server/mcp/client-registration
policies:
cors:
allowOrigins: ["*"]
allowHeaders: ["*"]
exposeHeaders: ["Mcp-Session-Id"]
mcpAuthentication:
mode: strict
issuer: http://localhost:7080/realms/mcp
audiences: [mcp_proxy]
jwks:
url: http://localhost:7080/realms/mcp/protocol/openid-connect/certs
provider:
keycloak: {}
resourceMetadata:
resource: http://localhost:3000/mcp
scopesSupported:
- profile
- offline_access
- openid
bearerMethodsSupported:
- headerThe provider.keycloak setting enables special handling for Keycloak’s non-standard OAuth endpoints.
Auth0 Integration
For production with Auth0:
mcpAuthentication:
mode: strict
issuer: https://your-tenant.auth0.com/
audiences: [your-api-identifier]
jwks:
url: https://your-tenant.auth0.com/.well-known/jwks.json
provider:
auth0: {}
resourceMetadata:
resource: http://localhost:3000/mcp
scopesSupported:
- openid
- profile
bearerMethodsSupported:
- headerCleanup
Stop Agent Gateway with Ctrl+C, then remove the tutorial directory:
cd .. && rm -rf mcp-auth-tutorial