Skip to main content

Model Context Protocol (MCP)

The Model Context Protocol (MCP) middleware enables secure, governed access to MCP servers by acting as an OAuth-compliant gateway. It provides centralized access control, resource metadata discovery, and fine-grained policy enforcement for MCP tools and resources.

Key Features and Benefits

  • OAuth 2.1 / 2.0 Compliant Access Control: Implements OAuth 2.0/2.1 Resource Server specification for MCP server protection
  • Resource Metadata Discovery: Automatically exposes /.well-known/oauth-protected-resource/<resource-path> endpoints
  • Task-Based Access Control (TBAC): Fine-grained authorization across three dimensions—tasks (business objectives), tools (system access), and transactions (parameter-level constraints). See Understanding TBAC for details.
  • JWT Integration: Seamless integration with existing JWT authentication middleware
  • Centralized Governance: Unified access control across multiple MCP servers and tools

Requirements

  • You must have MCP Gateway enabled:

    helm upgrade traefik traefik/traefik -n traefik --wait \
    --reset-then-reuse-values \
    --set hub.mcpgateway.enabled=true

    Optionally configure the maximum request body size (default: 1MB):

    helm upgrade traefik traefik/traefik -n traefik --wait \
    --reset-then-reuse-values \
    --set hub.mcpgateway.enabled=true \
    --set hub.mcpgateway.maxRequestBodySize=2097152 # 2MB in bytes
  • JWT authentication middleware must be configured for the route to provide authentication context

  • MCP servers must support the Streamable HTTP transport protocol

  • Session Affinity: For MCP sessions to work correctly, configure load balancing with Highest Random Weight (HRW) algorithm to ensure consistent routing to the same service instance

How It Works

The MCP middleware operates as an OAuth 2.1/2.0 Resource Server, intercepting requests to MCP servers and enforcing access control policies:

  1. Resource Metadata Exposure: Automatically creates /.well-known/oauth-protected-resource/<resource-path> endpoints for each configured MCP server, providing OAuth discovery information.

  2. Request Interception: Intercepts POST requests with JSON-RPC payloads destined for MCP servers.

  3. JWT Claims Extraction: Retrieves JWT claims from the authentication context provided by upstream JWT middleware.

  4. Policy Evaluation: Evaluates configured policies against the MCP request content and JWT claims using expression-based matching.

  5. Access Decision: Allows or denies requests based on policy matches and default actions.

The middleware automatically allows initialize and notifications/initialized methods which are essential for MCP protocol handshake.

OAuth 2.1 Compatibility

The MCP Gateway (JWT + MCP middleware) is fully compatible with OAuth 2.1 as it implements the Resource Server role. OAuth 2.1's security improvements (removal of implicit/password grant flows, mandatory PKCE) primarily affect authorization servers and clients, not resource servers. The MCP Gateway validates JWT access tokens issued by OAuth 2.1-compliant authorization servers without requiring any additional configuration.

Configuration Example

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: mcp-gateway
spec:
plugin:
mcp:
resourceMetadata:
resource: https://api.example.com/mcp-server
authorizationServers:
- https://auth.example.com
scopesSupported:
- mcp:tools
- mcp:resources
resourceDocumentation: https://docs.example.com/mcp-server
policies:
- match: Equals(`mcp.method`, `tools/call`) && Equals(`mcp.params.name`, `get_weather`) && Contains(`jwt.groups`, `weather-users`)
action: allow
- match: Equals(`mcp.method`, `resources/read`) && Prefix(`mcp.params.uri`, `file://safe/`)
action: allow
- match: Equals(`mcp.method`, `tools/call`) && Equals(`mcp.params.name`, `admin_tool`)
action: deny
defaultAction: deny

Configuration Options

FieldDescriptionRequiredDefault
resourceMetadataOAuth resource metadata configuration blockNo
resourceMetadata.resourceThe resource identifier URL for this MCP serverYes (if resourceMetadata specified)
resourceMetadata.authorizationServersArray of authorization server URLsYes (if resourceMetadata specified)
resourceMetadata.scopesSupportedArray of OAuth scopes supported by this resourceNo
resourceMetadata.resourceDocumentationURL to resource documentationNo
policiesArray of policy rules for access controlNo
policies[].matchExpression that must evaluate to true for the policy to applyYes
policies[].actionAction to take when the policy matches (allow or deny)Yes
defaultActionDefault action when no policies match (allow or deny)Nodeny

Policy Expression Language

Policies use an expression language that provides access to MCP request data and JWT claims:

Available Data Context

Policy expressions have access to two main data sources: the MCP JSON-RPC request and JWT claims from the authentication context.

MCP Request Data (mcp.*)

The entire JSON-RPC request body is available under the mcp namespace. You can access any field from the request structure:

FieldDescriptionExample Value
mcp.jsonrpcJSON-RPC version"2.0"
mcp.methodMCP method being invoked"tools/call", "resources/read", "prompts/get"
mcp.idRequest identifier (string or number)1, "req-123"
mcp.params.*Method parameters (nested access supported)See examples below

Common MCP Method Parameters:

MethodParameter PathDescriptionExample
tools/callmcp.params.nameTool name"get_weather", "search_database"
tools/callmcp.params.arguments.*Tool argumentsmcp.params.arguments.city"Paris"
resources/readmcp.params.uriResource URI"file:///project/data.json"
resources/subscribemcp.params.uriResource URI to subscribe to"file:///logs/app.log"
prompts/getmcp.params.namePrompt name"code_review", "summarize"
prompts/getmcp.params.arguments.*Prompt argumentsmcp.params.arguments.language"go"

Example JSON-RPC Request Structure:

{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"city": "Paris",
"units": "metric"
}
}
}

Access in policies:

  • mcp.method"tools/call"
  • mcp.id1
  • mcp.params.name"get_weather"
  • mcp.params.arguments.city"Paris"
  • mcp.params.arguments.units"metric"

JWT Claims Data (jwt.*)

All JWT claims from the authenticated user are available under the jwt namespace. You can access standard and custom claims using dot notation for nested fields.

Common JWT Claims:

FieldDescriptionExample Value
jwt.subSubject (user identifier)"user-123", "[email protected]"
jwt.issIssuer"https://auth.example.com"
jwt.audAudience"api.example.com"
jwt.expExpiration time (Unix timestamp)1735689600
jwt.iatIssued at time (Unix timestamp)1735686000
jwt.scopeOAuth scopes (space-separated string)"mcp:read mcp:write"
jwt.groupsUser groups (array)["developers", "admin"]
jwt.tenant_idTenant identifier (custom claim)"tenant-abc"
jwt.permissionsUser permissions (custom claim)["tool:weather", "resource:read"]
jwt.*Any custom claimAccess any field in your JWT

Example JWT Claims:

{
"sub": "user-123",
"groups": ["developers", "beta-testers"],
"scope": "mcp:read mcp:write",
"tenant_id": "acme-corp",
"email": "[email protected]"
}

Access in policies:

  • jwt.sub"user-123"
  • jwt.groups["developers", "beta-testers"]
  • jwt.scope"mcp:read mcp:write"
  • jwt.tenant_id"acme-corp"
  • jwt.email"[email protected]"

Variable Substitution

You can use JWT claims as variables in your expressions using the ${jwt.field} syntax:

# Allow users to access their own tenant resources
- match: Contains(`mcp.params.uri`, `tenant-${jwt.tenant_id}`)
action: allow

# Allow users to access their personal directory
- match: Prefix(`mcp.params.uri`, `file:///users/${jwt.sub}/`)
action: allow

Expression Functions

FunctionDescriptionExample
Equals(field, value)Exact string matchEquals('mcp.method', 'tools/call')
Contains(field, substring)Check if field contains substring (for strings) or contains value (for arrays)Contains('jwt.groups', 'admin')
Prefix(field, prefix)Check if field starts with prefixPrefix('mcp.params.uri', 'file://safe/')
Exists(field)Check if field exists in the data contextExists('jwt.tenant_id')
SplitContains(field, separator, value)Split field by separator and check if any part matches valueSplitContains('jwt.scope', ' ', 'mcp:read')
OneOf(field, value1, value2, ...)Check if field matches any of the provided valuesOneOf('mcp.method', 'tools/call', 'tools/list')
Lt(field, value)Numeric less than comparison (supports variable substitution)Lt('mcp.params.arguments.amount', '1000')
Lte(field, value)Numeric less than or equal comparison (supports variable substitution)Lte('mcp.params.arguments.amount', '${jwt.approval_limit}')
Gt(field, value)Numeric greater than comparison (supports variable substitution)Gt('jwt.rate_limit', '100')
Gte(field, value)Numeric greater than or equal comparison (supports variable substitution)Gte('mcp.params.arguments.priority', '${jwt.min_priority}')

Policy Examples

policies:
# Allow weather tool for weather-users group
- match: Equals(`mcp.method`, `tools/call`) && Equals(`mcp.params.name`, `get_weather`) && Contains(`jwt.groups`, `weather-users`)
action: allow

# Allow reading safe file resources
- match: Equals(`mcp.method`, `resources/read`) && Prefix(`mcp.params.uri`, `file://safe/`)
action: allow

# Deny admin tools for non-admin users
- match: Equals(`mcp.method`, `tools/call`) && Prefix(`mcp.params.name`, `admin_`) && !Contains(`jwt.groups`, `admin`)
action: deny

# Allow specific user to access personal resources
- match: Equals(`mcp.method`, `resources/read`) && Contains(`mcp.params.uri`, `user-${jwt.sub}`)
action: allow

# Numeric comparison: Allow expense approvals within user's limit
- match: Equals(`mcp.params.name`, `approve_expense`) && Lte(`mcp.params.arguments.amount`, `${jwt.approval_limit}`)
action: allow

# Numeric comparison: Allow high-priority tasks for users with sufficient clearance
- match: Equals(`mcp.method`, `tools/call`) && Gte(`jwt.clearance_level`, `${mcp.params.arguments.required_level}`)
action: allow

Client Behavior When Requests Are Blocked

When the MCP middleware denies a request, it returns an HTTP 403 Forbidden response. The client experience depends on the MCP client implementation:

What happens:

  1. The middleware returns 403 Forbidden with the plain text body "Forbidden"
  2. The MCP client receives this as a broken session error
  3. Most MCP clients (including Claude Desktop) report: calling "tools/call": broken session: 403 Forbidden

Client-side impact:

  • Claude Desktop: The tool call fails with an error message visible in the conversation. Claude will typically inform the user that the tool is unavailable or access was denied. The MCP session remain broken until Claude Desktop is restarted, as there is no automatic session recovery.
  • VSCode: Logs the error but recovers automatically for subsequent requests. The MCP context is preserved, allowing the agent to continue working.
  • Other MCP clients: Behavior varies by implementation. Some may attempt reconnection, while others will report the error and require manual intervention.

Best practices for policy design:

  • Use allow policies with defaultAction: deny to create explicit allow-lists rather than deny-lists
  • Test policies to avoid unintentionally blocking legitimate tool calls
  • Monitor access logs to identify and debug policy-related denials
  • Consider providing users with clear documentation about which tools/resources require specific permissions

Integration with JWT Middleware

The MCP middleware requires the JWT middleware to be configured upstream to provide authentication context. The JWT middleware should be configured with:

ConfigurationDescription
wwwAuthenticateURL to the auto-generated well-known OAuth resource endpoint (RFC 6750).
This should match the endpoint automatically created by the MCP Gateway based on your resourceMetadata.resource configuration.
Format: https://<host>/.well-known/oauth-protected-resource/<resource-path> (for example, https://api.example.com/.well-known/oauth-protected-resource/mcp-server)
forwardAuthorizationSet to true to forward the Authorization header to the MCP server, enabling On-Behalf-Of (OBO) authentication where the MCP server can act with the client's identity and permissions
forwardHeadersConfigure headers to pass user information if needed

Example JWT configuration for MCP integration:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: mcp-jwt
spec:
plugin:
jwt:
jwksUrl: https://auth.example.com/.well-known/jwks.json
wwwAuthenticate: https://api.example.com/.well-known/oauth-protected-resource/mcp-server
forwardAuthorization: true
forwardHeaders:
X-User-ID: sub
X-Tenant-ID: tenant_id
X-User-Groups: groups

OAuth Resource Discovery

When resourceMetadata is configured, the middleware automatically creates a well-known endpoint that returns OAuth resource metadata according to RFC 8414:

Endpoint Pattern: /.well-known/oauth-protected-resource/<resource-path>

For example, if resourceMetadata.resource is https://api.example.com/mcp-server, the auto-generated endpoint will be:

  • URL: https://api.example.com/.well-known/oauth-protected-resource/mcp-server

Response Example:

{
"resource": "https://api.example.com/mcp-server",
"authorization_servers": ["https://auth.example.com"],
"bearer_methods_supported": ["header"],
"scopes_supported": ["mcp:tools", "mcp:resources"],
"resource_documentation": "https://docs.example.com/mcp-server"
}

This enables MCP clients to discover authorization requirements automatically.

Session Handling and Load Balancing

MCP (Model Context Protocol) requires session affinity to maintain proper communication between clients and servers. MCP sessions are stateful, meaning that once a client establishes a connection with a specific MCP server instance, subsequent requests from that client should be routed to the same server instance.

Configuring Consistent Server-Side Session Affinity

To ensure session affinity for MCP traffic, configure your services to use the Highest Random Weight (HRW) load balancing algorithm. HRW provides consistent routing based on the client's IP address without requiring sticky cookies.

apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
name: mcp-server-lb
spec:
highestRandomWeight:
services:
- name: mcp-server-1
port: 80
weight: 10
- name: mcp-server-2
port: 80
weight: 10
- name: mcp-server-3
port: 80
weight: 10

Benefits of HRW for MCP

  • Session Affinity: Clients consistently reach the same MCP server instance
  • No Client State: Unlike sticky cookies, no client-side configuration is required
  • Automatic Failover: If a server becomes unavailable, requests are redistributed consistently among remaining servers
  • Weighted Distribution: Different server instances can handle different loads based on their capacity

Common Deployment Patterns

Basic MCP Server Protection

Protect an MCP server with JWT authentication and allow all operations for authenticated users:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: basic-mcp
spec:
plugin:
mcp:
resourceMetadata:
resource: https://api.example.com/mcp
authorizationServers:
- https://auth.example.com
defaultAction: allow

Task-Based Access Control

Implement Task-Based Access Control (TBAC) to authorize AI agents based on the tasks they perform, the tools they access, and transaction-level constraints. This approach is more appropriate for AI agents than traditional role-based access control because agents complete tasks across multiple domains. See Understanding TBAC for the complete conceptual framework.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: tbac-mcp
spec:
plugin:
mcp:
resourceMetadata:
resource: https://api.example.com/mcp
authorizationServers:
- https://auth.example.com
scopesSupported:
- mcp:read
- mcp:write
- mcp:admin
policies:
# Tool authorization: Developers can access development tools
- match: Contains(`jwt.groups`, `developers`) && Prefix(`mcp.params.name`, `dev_`)
action: allow

# Task authorization: Admins can perform all tasks
- match: Contains(`jwt.groups`, `admin`)
action: allow

# Transaction authorization: Regular users can only read resources
- match: Equals(`mcp.method`, `resources/read`) && Contains(`jwt.scope`, `mcp:read`)
action: allow

# Deny everything else
defaultAction: deny

Multi-Tenant Resource Access

Control access to resources based on tenant information in JWT claims:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: multitenant-mcp
spec:
plugin:
mcp:
policies:
# Users can only access resources in their tenant
- match: Equals(`mcp.method`, `resources/read`) && Contains(`mcp.params.uri`, `tenant-${jwt.tenant_id}`)
action: allow

# Tools are restricted by tenant permissions
- match: Equals(`mcp.method`, `tools/call`) && Contains(`jwt.permissions`, `tool:${mcp.params.name}`)
action: allow

defaultAction: deny

Transaction-Level Authorization with Numeric Limits

Implement fine-grained transaction controls using numeric comparison functions to enforce per-user limits on operations:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: transaction-control-mcp
spec:
plugin:
mcp:
resourceMetadata:
resource: https://api.example.com/financial-mcp
authorizationServers:
- https://auth.example.com
scopesSupported:
- financial:approve
- financial:transfer
policies:
# Allow expense approvals within user's approval limit
- match: Equals(`mcp.params.name`, `approve_expense`) && Lte(`mcp.params.arguments.amount`, `${jwt.approval_limit}`)
action: allow

# Allow transfers below daily limit
- match: Equals(`mcp.params.name`, `transfer_funds`) && Lte(`mcp.params.arguments.amount`, `${jwt.daily_transfer_limit}`)
action: allow

# Allow budget adjustments only for amounts within authorized range
- match: Equals(`mcp.params.name`, `adjust_budget`) && Lte(`mcp.params.arguments.amount`, `${jwt.max_budget_change}`)
action: allow

# Deny any transaction exceeding user limits
defaultAction: deny

This pattern uses JWT claims like approval_limit, daily_transfer_limit, and max_budget_change to dynamically enforce transaction limits. The IdP issues these limits based on the user's role, department, or other attributes, enabling centralized policy management.

Observability

The MCP middleware provides OpenTelemetry-compatible metrics and traces for monitoring AI agent tool invocations.

Metrics

The MCP Gateway records detailed metrics including:

  • Operation duration: mcp.client.operation.duration histogram tracking MCP operation latency
  • Method-level tracking: Metrics tagged with MCP method names (tools/call, resources/read, etc.)
  • Tool-specific metrics: Automatic tagging by tool name, prompt name, and resource URI
  • Argument tracking: Request arguments captured as metric attributes for detailed analysis

Metric Attributes

The following attributes are automatically attached to metrics when available:

AttributeDescriptionExample
mcp.session.idMCP session identifier from the Mcp-Session-Id headersession-abc123
mcp.method.nameThe MCP method being invokedtools/call, resources/read
mcp.request.idJSON-RPC request ID (string or number)1, "req-123"
mcp.tool.nameTool name (for tools/call methods)get_weather, search_database
mcp.prompt.namePrompt name (for prompts/get methods)code_review, summarize
mcp.resource.uriResource URI (for resource methods)file://safe/data.json
mcp.request.argument.*Tool arguments (for tools/call with arguments)mcp.request.argument.city=Paris

Example Metrics Query

Using Prometheus, you can query MCP operation metrics:

# Average MCP operation duration by method
rate(mcp_client_operation_duration_sum[5m]) / rate(mcp_client_operation_duration_count[5m])

# MCP operations per second by tool name
sum(rate(mcp_client_operation_duration_count{mcp_tool_name!=""}[5m])) by (mcp_tool_name)

# 95th percentile MCP operation latency
histogram_quantile(0.95, sum(rate(mcp_client_operation_duration_bucket[5m])) by (le))

Tracing Integration

MCP operations are automatically included in OpenTelemetry traces when tracing is enabled in Traefik Hub. The middleware creates spans with the same attributes as metrics, allowing you to correlate performance issues with specific MCP methods and tools.

Troubleshooting

403 Forbidden Responses

When requests are denied with HTTP 403, check the following:

  • JWT middleware configuration: Verify JWT middleware is configured and placed before MCP middleware in the middleware chain
  • JWT claims: Check that JWT contains required claims referenced in policies (use kubectl logs to inspect JWT content)
  • Policy expressions: Review policy expressions for syntax errors (field names are case-sensitive)
  • Default action: Ensure defaultAction is set appropriately for your use case (deny by default)

Testing tip: Start with defaultAction: allow to verify JWT authentication works, then add policies incrementally.

Well-Known Endpoint Not Found

If the OAuth resource discovery endpoint returns 404:

  • Resource metadata: Confirm resourceMetadata is configured in the MCP middleware
  • URL format: Verify the resource URL format matches https://<host>/<path> pattern
  • Route application: Check that the middleware is applied to the correct IngressRoute
  • Path verification: The well-known endpoint is automatically generated at /.well-known/oauth-protected-resource/<resource-path>

Example: If resource: https://api.example.com/mcp-server, the endpoint will be at https://api.example.com/.well-known/oauth-protected-resource/mcp-server

Policy Not Matching

When policies don't match expected requests:

  • Field names: Use exact field names in expressions (case-sensitive). Use mcp.method not mcp.Method
  • JWT structure: Verify JWT claims structure matches policy expectations using jwt.io or similar tools
  • Nested fields: Access nested fields with dot notation: mcp.params.arguments.city
  • Variable substitution: Only use ${...} syntax in the second parameter (the value), not the first parameter (the field name)
    • ✓ Correct: Equals('jwt.department', '${mcp.params.arguments.department}')
    • ✗ Wrong: Equals('${jwt.department}', 'sales')
  • Testing: Start with simple conditions and add complexity gradually

Debug approach: Enable Traefik logs and check for policy evaluation messages to see which policies are being evaluated.

MCP Session Issues

MCP sessions require consistent routing to the same server instance:

Symptoms:

  • Connection errors after initial handshake
  • Unexpected session termination
  • State loss between requests

Solutions:

  • Load balancing: Configure HRW (Highest Random Weight) algorithm for session affinity (see Session Handling)
  • Server verification: Verify that MCP clients are connecting to the same server instance
  • Stateful connections: Check that MCP server instances can handle stateful connections properly
  • Alternative: Consider using sticky sessions if HRW is not available in your setup

Testing: Use kubectl logs on MCP server pods to verify the same pod handles requests from a given client.

JWT Claims Not Available in Policies

If JWT claims aren't accessible in policy expressions:

Common causes:

  • JWT middleware not configured with forwardAuthorization: true
  • JWT middleware placed after MCP middleware in the chain (wrong order)
  • JWT validation failing silently
  • Claims not present in the JWT token

Solutions:

  1. Verify middleware order in IngressRoute:

    middlewares:
    - name: jwt-auth # Must be first
    - name: mcp-gateway # Must be second
  2. Check JWT middleware configuration:

    forwardAuthorization: true  # Required for OBO
  3. Inspect JWT token claims using a tool like https://jwt.io

Expression Syntax Errors

Common expression syntax errors and fixes:

Backticks required: Field names and string values must use backticks:

  • ✓ Correct: Equals(`mcp.method`, `tools/call`)
  • ✗ Wrong: Equals("mcp.method", "tools/call")

Logical operators: Use &&, ||, ! for boolean logic:

  • ✓ Correct: Equals(`mcp.method`, `tools/call`) && Contains(`jwt.groups`, `admin`)
  • ✗ Wrong: Equals(`mcp.method`, `tools/call`) AND Contains(`jwt.groups`, `admin`)

Array checking: Use Contains() for arrays, not Equals():

  • ✓ Correct: Contains(`jwt.groups`, `admin`) (checks if "admin" is in the groups array)
  • ✗ Wrong: Equals(`jwt.groups`, `admin`) (tries exact match of entire array)

Variable substitution placement: Only in second parameter:

  • ✓ Correct: Equals(`jwt.tenant_id`, `${mcp.params.arguments.tenant}`)
  • ✗ Wrong: Equals(`${jwt.tenant_id}`, `tenant-123`)
Debugging Tips

1. Check JWT Claims

Ensure the JWT middleware is forwarding claims correctly:

# Check JWT middleware logs
kubectl logs -n traefik deployment/traefik | grep jwt

# Test JWT decoding manually
echo "eyJhbGc..." | base64 -d | jq .

2. Simplify Policies

Start with basic policies and add complexity gradually:

# Start simple
policies:
- match: "Equals(`mcp.method`, `tools/call`)"
action: allow

# Add complexity incrementally
policies:
- match: "Equals(`mcp.method`, `tools/call`) && Equals(`mcp.params.name`, `get_weather`)"
action: allow

3. Review Logs

Check Traefik Hub logs for policy evaluation messages:

kubectl logs -n traefik deployment/traefik --tail=100 -f

4. Test Expressions

Verify expression syntax matches the available data context by testing with curl:

# Send test request
curl -X POST https://api.example.com/mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"test"}}'

# Check response and logs

5. Validate Policy Syntax

Test policies with minimal configuration first, then expand:

# Minimal test - should allow all
defaultAction: allow

# Then add deny-by-default
defaultAction: deny
policies:
- match: "Exists(`jwt.sub`)" # Allow any authenticated user
action: allow

Security Considerations

  • JWT Validation: Ensure JWT middleware validates tokens before MCP middleware
  • Scope Verification: Use OAuth scopes to limit access to specific MCP capabilities
  • Resource Isolation: Design policies to prevent cross-tenant data access
  • Audit Logging: Enable access logs to track MCP server interactions
  • Token Expiry: Configure appropriate JWT expiration times for security