Model Context Protocol (MCP) traffic is increasingly moving from local developer environments into enterprise HTTP infrastructure.
A production MCP deployment may look like:
MCP Client
↓
API Gateway
↓
WAF
↓
Load Balancer
↓
MCP Server
↓
Enterprise APIs
This creates a practical security problem.
Traditional API gateways already understand HTTP methods, paths, authentication headers, and common API metadata. MCP requests, however, traditionally carried much of their semantic information inside the JSON-RPC body.
MCP 2.0 changes that model.
The July 28, 2026 MCP specification revision standardizes HTTP headers that mirror important MCP request information. A tools/call request can expose values such as:
Mcp-Method: tools/call
Mcp-Name: get_order_status
Mcp-Param-Region: eastus2
This allows gateways, proxies, WAFs, load balancers, and observability systems to make decisions without parsing the JSON-RPC body.
That capability is useful, but it also introduces a new security boundary.
The key question is:
How should enterprise gateways use MCP parameter headers without trusting attacker-controlled metadata incorrectly?
Why MCP Headers Matter
Consider a tool:
[McpServerTool(Name = "get_order_status")]
public static async Task<string> GetOrderStatus(
string region,
string orderId)
{
// Call regional order service.
return await GetStatusAsync(region, orderId);
}
The MCP 2.0 HTTP model can expose selected parameters as HTTP headers.
Conceptually:
POST /mcp
Mcp-Method: tools/call
Mcp-Name: get_order_status
Mcp-Param-Region: eastus2
The advantage is that infrastructure can make decisions without understanding the complete JSON-RPC payload.
For example:
Gateway
↓
Read Mcp-Param-Region
↓
Route to eastus2 backend
This is particularly useful for geographically distributed deployments. Microsoft describes this header design specifically as enabling ordinary HTTP infrastructure to route MCP traffic without deep inspection of request bodies.
Headers Are Not a Security Boundary by Themselves
The first security principle is simple:
Do not assume an MCP header is trustworthy merely because it uses a standardized name.
HTTP headers ultimately arrive as part of a client request.
An attacker may attempt to send:
Mcp-Param-Region: internal
or:
Mcp-Name: privileged_tool
The gateway must therefore distinguish between:
Client-supplied metadata
and:
Validated MCP request metadata
This distinction becomes especially important when a gateway uses the header to make an authorization or routing decision.
The JSON-RPC Body Remains Authoritative
MCP 2.0 deliberately avoids creating two independent sources of truth.
The standardized headers mirror information already represented in the MCP request.
For example:
Header:
Mcp-Name: get_order_status
Body:
tool = get_order_status
If they disagree, the request should not be interpreted by choosing whichever value is convenient.
The MCP 2.0 design makes the JSON-RPC body authoritative and rejects mismatched headers rather than guessing. Microsoft documents this behavior as a HeaderMismatch error condition.
This is an important security property.
Without this rule, an attacker could potentially attempt:
Gateway sees:
Mcp-Name: public_tool
Server receives:
tool: privileged_tool
or the reverse.
The infrastructure and application could make different security decisions.
The Confused-Deputy Problem
Consider an enterprise gateway:
Client
↓
Gateway
↓
MCP Server
The gateway reads:
Mcp-Name: get_customer_profile
and applies a policy:
Allowed
But the actual request body contains a different tool.
If the gateway and MCP server make decisions using different representations, the system can become vulnerable to a confused-deputy-style authorization problem.
The secure model is:
HTTP Metadata
↓
Routing / preliminary policy
↓
MCP validation
↓
Authoritative request
↓
Authorization
↓
Tool execution
A gateway should not become the sole authority for MCP authorization simply because it can see the tool name.
Separate Routing From Authorization
This distinction is important.
A gateway can use headers for:
Routing
Rate limiting
Traffic classification
Observability
Basic policy enforcement
But authorization should ultimately be based on validated identity, claims, scopes, resource ownership, and the authoritative MCP operation.
For example:
Mcp-Name: get_order_status
can help route the request.
It should not automatically imply:
User is authorized to view every order.
Authentication and authorization remain separate security decisions.
Validate Authentication Before Sensitive Routing
An enterprise deployment may use OAuth 2.0 or OpenID Connect.
A simplified request path is:
Client
↓
Authorization Header
↓
Gateway
↓
MCP Server
↓
Token Validation
↓
Authorization
↓
Tool
Validate at the appropriate trust boundary:
Issuer
Audience
Lifetime
Signature
Scopes
Claims
The MCP C# SDK has built-in support for OAuth-related authorization patterns, including incremental scope consent. Microsoft recommends validating issuer, audience, lifetime, and signing keys when configuring JWT bearer authentication.
Do Not Authorize Based Only on Tool Name
A weak policy might look like:
if Mcp-Name == "delete_customer"
allow
This is not authorization.
A production policy should consider:
Authenticated principal
+
Required scope
+
Tool
+
Resource
+
Tenant
+
Business policy
For example:
User
↓
Scope = customer.delete
↓
Tool = delete_customer
↓
Customer belongs to user's tenant
↓
Allow
The exact authorization model depends on the application.
Parameter Headers Can Become Security-Sensitive
A parameter that looks harmless can affect authorization or routing.
For example:
Mcp-Param-TenantId: tenant-a
If the gateway uses this value to select a tenant-specific backend, the parameter becomes security-sensitive.
An attacker might attempt:
Authenticated user:
tenant-a
Header:
tenant-b
The gateway must not assume that the header value establishes tenant identity.
Instead:
Authenticated identity
↓
Authorized tenant
↓
Validated MCP parameter
↓
Resource access check
Tenant identity should come from trusted authentication and authorization context wherever possible.
Do Not Trust Tenant IDs From Tool Arguments
Consider:
public static Task<string> GetCustomer(
string tenantId,
string customerId)
The presence of:
tenantId = tenant-b
does not prove the caller belongs to tenant B.
The server should derive or validate tenant context from the authenticated principal.
For example:
var userTenant =
User.FindFirst("tenant_id")?.Value;
if (userTenant != requestedTenantId)
{
throw new UnauthorizedAccessException();
}
The exact claim name and authorization mechanism depend on the identity provider.
The principle remains the same:
A request parameter identifies a resource; it does not establish authorization to access it.
Protect Region-Based Routing
Regional routing is one of the useful applications for Mcp-Param-*.
For example:
Mcp-Param-Region: eu-west
The gateway can route:
eu-west → EU backend
us-east → US backend
But routing must not bypass security controls.
A safer model is:
Authenticated request
↓
Validate requested region
↓
Check tenant/resource policy
↓
Route
Do not allow arbitrary region values to become arbitrary internal network destinations.
Otherwise, a parameter that was intended for routing can become an SSRF-like infrastructure control.
Use an Allowlist for Routing Values
Avoid:
var backend =
$"https://{region}.internal.example.com";
where region is directly controlled by the request.
Prefer a fixed mapping:
var endpoints =
new Dictionary<string, Uri>
{
["us-east"] =
new("https://orders-us.internal"),
["eu-west"] =
new("https://orders-eu.internal")
};
if (!endpoints.TryGetValue(
region,
out var endpoint))
{
throw new ArgumentException(
"Unsupported region.");
}
This prevents arbitrary input from becoming an internal destination.
Treat Mcp-Param-* as Untrusted Input
A useful security classification is:
Mcp-Method
↓
Untrusted until validated
Mcp-Name
↓
Untrusted until validated
Mcp-Param-*
↓
Untrusted until validated
After MCP validation and application authorization:
Validated request
↓
Trusted application context
This prevents infrastructure metadata from accidentally becoming an implicit trust mechanism.
Protect Against Header Injection
Header values can create problems if they are copied into other HTTP requests without validation.
For example:
Incoming:
Mcp-Param-Region: value
↓
Outgoing:
X-Backend-Region: value
If the value is not validated, an application can accidentally propagate malformed or dangerous data.
Use strict validation:
if (!AllowedRegions.Contains(region))
{
throw new InvalidOperationException(
"Invalid region.");
}
For structured values, use strongly typed parsing rather than string manipulation.
Avoid Reflection-Based Policy Names
A gateway policy such as:
Mcp-Name = "delete_customer"
should map to a known policy.
Avoid dynamically constructing policy names from untrusted values without validation:
var policy =
"Mcp_" + toolName;
Instead, use explicit mappings:
var policies =
new Dictionary<string, string>
{
["get_order_status"] =
"Orders.Read",
["create_order"] =
"Orders.Write",
["delete_order"] =
"Orders.Delete"
};
This makes security behavior auditable.
Use Least Privilege
MCP tools should not automatically receive broad permissions.
For example:
get_order_status
→ orders.read
create_order
→ orders.write
delete_order
→ orders.delete
This aligns the tool surface with the principle of least privilege.
The MCP C# SDK's authorization capabilities also support incremental scope consent, allowing clients to request additional permissions when an operation requires them instead of requesting every possible scope upfront.
Validate Scope at the Correct Layer
A gateway can perform coarse-grained scope checks:
/orders/*
The MCP server may still need fine-grained authorization:
Tool
+
Resource
+
Tenant
+
User
For example:
Gateway:
scope = mcp:tools
MCP Server:
scope = orders.read
+
tenant ownership
+
order access
This layered model avoids putting all authorization logic into the gateway.
Beware of Header Rewriting
Enterprise proxies sometimes normalize, remove, or rewrite headers.
For MCP traffic, this can create unexpected behavior.
For example:
Client
↓
Mcp-Name: get_order_status
↓
Gateway
↓
Mcp-Name: GetOrderStatus
If the server compares values strictly, the request could fail.
More importantly, a security policy may interpret the values differently.
Document gateway behavior for:
Case sensitivity
Header normalization
Header removal
Duplicate headers
Header size limits
Proxy forwarding
Do not assume that every intermediary preserves the exact wire representation.
Reject Duplicate or Ambiguous Metadata
Security-sensitive HTTP metadata should not have multiple competing interpretations.
A request containing ambiguous values such as:
Mcp-Name: tool-a
Mcp-Name: tool-b
should not be silently resolved by selecting one.
Configure the gateway and application stack to reject malformed or ambiguous requests.
The exact behavior depends on the HTTP server and proxy configuration.
Enforce Header Size Limits
Enterprise gateways should have sensible limits for:
Total header size
Individual header size
Number of headers
This is important because MCP parameter headers can expose selected tool arguments.
Do not allow arbitrary tool parameters to produce unbounded HTTP header sizes.
A tool argument containing a very large payload should generally remain in the request body rather than being promoted into a header.
Only Promote Appropriate Parameters
The [McpHeader] mechanism exists specifically to identify parameters that clients can mirror into headers. Microsoft describes it as a way to expose selected tool parameters for infrastructure routing and inspection.
Not every parameter should be promoted.
Good candidates:
Region
Routing key
Request classification
Low-risk category
Poor candidates:
Password
Access token
Payment information
Large document
Personal data
Secret
A simple rule is:
If you would not want a reverse proxy to inspect the value, do not promote it into a header.
Do Not Put Secrets in MCP Headers
Never use:
Mcp-Param-ApiKey
Mcp-Param-Password
Mcp-Param-AccessToken
for sensitive credentials.
HTTP headers commonly appear in:
Gateway logs
Proxy logs
Tracing systems
Debugging tools
Network captures
Secrets should use appropriate authentication mechanisms instead.
Protect Gateway Logs
Once MCP headers become observable by infrastructure, they can appear in gateway access logs.
For example:
Mcp-Param-Region: eastus2
may be harmless.
But:
Mcp-Param-Email: [email protected]
could be personal information.
And:
Mcp-Param-Token: ...
could be a credential.
Configure logging policies to:
Allow safe metadata
Redact sensitive headers
Avoid full request logging
Do not assume the gateway automatically knows which MCP parameters are sensitive.
Use Explicit Redaction
For example, define:
Safe:
Mcp-Method
Mcp-Name
Mcp-Param-Region
Sensitive:
Mcp-Param-Email
Mcp-Param-AccountNumber
Then configure infrastructure accordingly.
The exact redaction mechanism depends on your gateway or WAF.
Join the conversation! Your thoughts help the community grow.