MCP Shield is an OAuth proxy for MCP servers on OpenShift. It provides OAuth 2.0 discovery, client registration, and token exchange for MCP servers with OpenShift-specific handling and a proxy token system for enhanced security.
Supported MCP servers:
Note: MCP Shield has been tested with Claude (Anthropic's AI assistant) as an MCP client. The implementation is designed to work with any MCP client that follows the OAuth 2.0 flow with Bearer token authentication.
- OAuth Discovery: Implements
/.well-known/oauth-authorization-serverendpoint for OAuth 2.0 discovery - Client Registration: Implements
/oauth/registerendpoint for static client configuration - OAuth Start Flow: Implements
/oauth2/startendpoint that redirects to OpenShift OAuth with callback proxy support - OAuth Callback: Implements
/oauth/callbackendpoint that handles OpenShift callbacks and redirects to original client redirect URIs - Token Exchange: Implements
/oauth/tokenendpoint with filtering ofresource=undefinedparameter and redirect_uri fixing - Proxy Token System: Generates proxy tokens for clients instead of exposing real user tokens, enhancing security
- MCP Proxy: Proxies
/mcpand root path (/) requests to the MCP server backend, exchanging proxy tokens for real tokens - CORS Support: Properly configured CORS headers for browser-based OAuth flows
- Dynamic Redirect URI Support: Handles dynamic localhost redirect URIs from MCP clients (e.g., Agentic CLI) via callback proxy mechanism
-
OAUTH_AUTHORIZATION_SERVERS(required): Comma-separated list of authorization server URLs (should be the public URL where MCP Shield is accessible)- Used to derive gateway URL and callback URLs
- Example:
https://prometheus-mcp-server.apps.example.com
-
INSPECTOR_ORIGIN(optional): Origin URL for the MCP Inspector (for CORS headers)- Defaults to
*(allow all origins) if not set - Example:
https://mcp-inspector.apps.example.com
- Defaults to
-
OAUTH_CLIENT_ID(optional): OAuth client ID that must match the OAuthClient name in OpenShift- Defaults to
prometheus-mcp-serverif not set - Must match the
metadata.nameof the OAuthClient resource
- Defaults to
-
OAUTH_REDIRECT_URIS(optional): Comma-separated list of additional redirect URIs- Default redirect URIs are automatically generated from
OAUTH_AUTHORIZATION_SERVERSandINSPECTOR_ORIGIN - Example:
https://custom-redirect.example.com/callback,https://another.example.com/callback
- Default redirect URIs are automatically generated from
-
OPENSHIFT_OAUTH_TOKEN_URL(optional): OpenShift OAuth token endpoint URL- Auto-derived from
OAUTH_AUTHORIZATION_SERVERSif not set - Format:
https://oauth-openshift.apps.<cluster-domain>/oauth/token - Usually doesn't need to be set manually
- Auto-derived from
-
MCP_BACKEND_URL(optional): URL of the MCP server backend container- Defaults to
http://localhost:8080if not set - Automatic Gateway Detection: MCP Shield automatically detects if this points to an mcp-gateway instance by calling its
/statusendpoint - If detected as a gateway: MCP Shield automatically discovers upstream servers and routes tool calls directly to them. Session management is handled automatically via
initializecalls with session IDs in both JSON body and HTTP header (mcp-session-id) - If NOT detected as a gateway: MCP Shield uses it as a regular fallback for all MCP requests (including tool calls). No direct routing is attempted, and requests are forwarded to the backend as-is
- Example:
http://localhost:8080(for sidecar in same pod) orhttp://mcp-gateway:8080(for gateway discovery) - See Direct Tool Routing for details
- Defaults to
-
MCP_BACKEND_PATH(optional): Backend path endpoint to forward requests to- Defaults to
/mcpif not set (for Prometheus MCP server) - For Loki MCP server, set to
/stream(Loki uses/streamfor HTTP,/mcpis for SSE) - Example:
MCP_BACKEND_PATH=/stream(for Loki MCP server)
- Defaults to
-
PROXY_TOKEN_TTL(optional): Time-to-live for proxy tokens- Defaults to
24hif not set - Format: Go duration string (e.g.,
24h,12h,1h30m) - Proxy tokens expire after this duration and must be refreshed
- Example:
PROXY_TOKEN_TTL=12h
- Defaults to
go build ./cmd/mcp-shield# Build the image
docker build -t quay.io/<MY_USER>/mcp-shield:dev .
# Or use the Makefile
make docker-build# Push the image
docker push quay.io/<MY_USER>/mcp-shield:dev
# Or use the Makefile
make docker-push./mcp-shield -listen :8080 -log-level infodocker run -p 8080:8080 \
-e OAUTH_AUTHORIZATION_SERVERS=https://your-mcp-server.apps.example.com \
-e INSPECTOR_ORIGIN=https://mcp-inspector.apps.example.com \
quay.io/<MY_USER>/mcp-shield:dev-
GET /.well-known/oauth-authorization-server- OAuth 2.0 discovery metadata- Returns OAuth authorization server metadata including issuer, endpoints, supported grant types, etc.
- Used by MCP clients to discover OAuth configuration
-
GET/POST /oauth/register- Client registration endpoint- Returns static client configuration (client_id, redirect_uris, etc.)
- Supports both GET and POST methods
- Used by MCP Inspector for dynamic client registration
-
GET /oauth2/start- OAuth authorization flow initiation- Accepts OAuth authorization request parameters
- Intercepts dynamic localhost redirect URIs and replaces with fixed callback URL
- Stores original redirect URI in state parameter
- Redirects to OpenShift OAuth authorization endpoint
-
GET /oauth/callback- OAuth callback handler- Receives authorization code from OpenShift OAuth server
- Extracts original redirect URI from state parameter
- Redirects client to original redirect URI with authorization code
-
POST /oauth/token- Token exchange endpoint- Accepts authorization code and exchanges for access token
- Filters out
resource=undefinedparameter (OpenShift rejects it) - Fixes redirect_uri to match the authorization step
- Adds Basic Auth header with client_id (required by OpenShift)
- Proxies request to OpenShift OAuth token endpoint
- Returns access token to client
-
POST /mcp- MCP protocol endpoint- Proxies MCP protocol requests to the MCP server backend
- Forwards
Authorizationheader with Bearer token (Agentic CLI sends token with each request) - Forwards all request headers and cookies
- Returns MCP server response
- Note: The MCP server is stateless - it extracts the token from each request's Authorization header
-
POST /mcp/- MCP protocol endpoint (trailing slash)- Same as
/mcpbut with trailing slash support
- Same as
-
POST /- Root path MCP proxy- Handles POST requests to root path (used by some MCP clients like agentic CLI)
- Forwards to MCP handler
- Returns 404 for non-POST requests
GET /healthz- Health check endpoint- Returns
200 OKwith bodyok - Used for liveness and readiness probes
- Returns
MCP Shield is designed to run as a sidecar container alongside your MCP server. Detailed deployment guides are available for each supported MCP server:
- Prometheus MCP Server - Complete guide for deploying with prometheus-mcp-server
- Loki MCP Server - Complete guide for deploying with loki-mcp-server
- Kubernetes MCP Server - Complete guide for deploying with kubernetes-mcp-server
- MCP Gateway - Complete guide for deploying with mcp-gateway
Different MCP servers use different endpoint paths for HTTP requests:
-
Prometheus MCP Server: Uses
/mcpendpoint for HTTP requests- Configure:
MCP_BACKEND_PATH=/mcp(this is the default)
- Configure:
-
Loki MCP Server: Uses
/streamendpoint for HTTP requests- Configure:
MCP_BACKEND_PATH=/stream - Note: Loki's
/mcpendpoint is for SSE (Server-Sent Events), not HTTP POST requests - Also requires
sessionIdinjection in JSON-RPC requests (handled automatically by MCP Shield)
- Configure:
-
Kubernetes MCP Server: Uses
/mcpendpoint for HTTP requests- Configure:
MCP_BACKEND_PATH=/mcp(this is the default) - Note: While kubernetes-mcp-server supports external OAuth (proxying well-known endpoints), it does not implement the complete OAuth flow. MCP Shield provides the full OAuth 2.0 flow implementation.
- Configure:
The MCP_BACKEND_PATH environment variable allows you to configure the correct endpoint for your MCP server type.
For local development and testing:
# Build the binary
go build ./cmd/mcp-shield
# Run with environment variables
export OAUTH_AUTHORIZATION_SERVERS="https://your-mcp-server.apps.example.com"
export INSPECTOR_ORIGIN="https://mcp-inspector.apps.example.com"
./mcp-shield -listen :8080 -log-level debugMCP Shield is designed to run as a sidecar service alongside the MCP server, handling OAuth-related endpoints that require special handling for OpenShift integration.
The following diagram illustrates the complete OAuth authentication flow:
sequenceDiagram
participant Client as MCP Client<br/>(Agentic CLI, Inspector)
participant Proxy as MCP Shield<br/>(Sidecar)
participant OpenShift as OpenShift<br/>OAuth Server
participant MCP as MCP Server<br/>(prometheus-mcp-server)
Note over Client,MCP: 1. Discovery Phase
Client->>Proxy: GET /.well-known/oauth-authorization-server
Proxy-->>Client: OAuth discovery metadata<br/>(issuer, endpoints, etc.)
Client->>Proxy: POST /oauth/register
Proxy-->>Client: Client registration info<br/>(client_id, redirect_uris)
Note over Client,MCP: 2. Authorization Phase
Client->>Proxy: GET /oauth2/start?<br/>response_type=code&<br/>client_id=...&<br/>redirect_uri=localhost:XXXX/callback
Note over Proxy: Intercepts localhost redirect_uri<br/>Replaces with fixed callback URL<br/>Stores original in state parameter
Proxy->>OpenShift: Redirect to /oauth/authorize<br/>with fixed redirect_uri
OpenShift->>Client: User login page
Client->>OpenShift: User credentials
OpenShift->>Proxy: Redirect to /oauth/callback<br/>with authorization code
Note over Proxy: Extracts original redirect_uri<br/>from state parameter
Proxy->>Client: Redirect to original<br/>localhost:XXXX/callback<br/>with authorization code
Note over Client,MCP: 3. Token Exchange Phase
Client->>Proxy: POST /oauth/token<br/>grant_type=authorization_code&<br/>code=...&<br/>redirect_uri=localhost:XXXX/callback
Note over Proxy: Fixes redirect_uri to<br/>match authorization step<br/>Filters resource=undefined<br/>Adds Basic Auth (client_id)
Proxy->>OpenShift: POST /oauth/token<br/>with fixed parameters
OpenShift-->>Proxy: Real user access token
Note over Proxy: Generates proxy token<br/>Stores mapping:<br/>proxy_token -> real_token
Proxy-->>Client: Proxy token<br/>(not real user token)
Note over Client,MCP: 4. MCP Requests Phase
Client->>Proxy: POST /mcp<br/>Authorization: Bearer <proxy_token>
Note over Proxy: Exchanges proxy token<br/>for real user token
Proxy->>MCP: Forward request<br/>Authorization: Bearer <real_token>
MCP->>MCP: Validate real token<br/>Extract user info
MCP->>Proxy: MCP response
Proxy-->>Client: MCP response
-
Discovery Phase:
- Client requests OAuth discovery metadata to learn available endpoints
- Client registers itself (or receives static client configuration)
-
Authorization Phase:
- Client initiates OAuth flow with a dynamic localhost redirect URI
- MCP Shield intercepts and replaces it with a fixed callback URL
- User authenticates with OpenShift
- OpenShift redirects to the fixed callback URL
- MCP Shield extracts the original redirect URI from the state parameter and redirects the client
-
Token Exchange Phase:
- Client exchanges authorization code for access token
- MCP Shield fixes the redirect_uri to match the authorization step
- MCP Shield filters out
resource=undefinedparameter (OpenShift rejects it) - MCP Shield adds Basic Auth with client_id (required by OpenShift)
- MCP Shield receives real user token from OpenShift
- MCP Shield generates a proxy token and stores the mapping (proxy_token -> real_token)
- Client receives proxy token (not the real user token)
- Proxy token is only valid for use with this MCP Shield instance
-
MCP Requests Phase:
- Agentic CLI clients cache the proxy token locally (client-side) after receiving it
- Client makes MCP requests with proxy token in
Authorization: Bearer <proxy_token>header - MCP Shield exchanges proxy token for real user token before forwarding to MCP server
- MCP Shield forwards requests with real user token in
Authorizationheader to MCP server - MCP server is stateless - it extracts the Bearer token from each request's Authorization header
- MCP server uses the real token to authenticate with Prometheus/Thanos on behalf of the user
- Security benefit: Agentic CLI clients never have access to the real user token - they can only use the proxy token with this MCP Shield instance
- No server-side sessions are maintained - each request is independent
The proxy token system provides an important security layer:
Problem: Without proxy tokens, agentic CLI clients would cache the real OpenShift OAuth token, which could be:
- Used to access any OpenShift resource the user has permissions for
- Extracted from the client's cache and used maliciously
- A security risk if the client is compromised
Solution: MCP Shield generates proxy tokens that:
- ✅ Cannot be used outside MCP Shield: Proxy tokens are only valid for this specific MCP Shield instance
- ✅ Scoped to MCP access: Proxy tokens can only be exchanged for real tokens when making MCP requests through MCP Shield
- ✅ Time-limited: Proxy tokens expire after a configurable TTL (default: 24 hours)
- ✅ No direct OpenShift access: Even if a proxy token is compromised, it cannot be used to directly access OpenShift resources - it must go through MCP Shield
How it works:
- User authenticates with OpenShift OAuth
- MCP Shield receives the real user token from OpenShift
- MCP Shield generates a secure random proxy token
- MCP Shield stores the mapping:
proxy_token -> real_token(in-memory, not persisted) - Agentic CLI clients receive and cache the proxy token
- When agentic CLI clients make MCP requests, MCP Shield exchanges the proxy token for the real token
- Real token is only used server-side and never exposed to the client
For detailed comparisons with alternative solutions, see Comparisons, which covers:
- Why not use the official OpenShift oauth-proxy? - Differences in authentication methods, OAuth discovery endpoints, and use cases
- Why not use mcp-gateway? - Architecture differences, OAuth implementation differences, and use case guidance
