Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 235 additions & 0 deletions docs/investigation-generalized-payload-to-header-mapping-mcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
# Investigation: Generalized Payload-to-Header Mapping for MCP

**Contributors:** @kurtisvg
**Status:** 🚧 WIP
**Last Updated: Mar 3, 2026**

## Problem Statement

The current draft of the HTTP Header Standardization SEP introduces a mechanism
to expose critical routing information to network intermediaries (like load
balancers and API gateways) without requiring deep packet inspection.

Currently, this design takes a **schema-bound approach**. It allows server
authors to elevate specific tool parameters into HTTP headers by adding an
`x-mcp-header` annotation directly within a tool's `inputSchema`. This solution
is highly targeted, leveraging the existing extensibility of JSON Schema to
surface specific tool execution arguments.

The Working Group has requested an investigation into a **payload-bound
approach**: creating a generalized, protocol-wide mechanism that would allow
*any* arbitrary field from the JSON-RPC payload to be elevated into an HTTP
header. This would expand the capability beyond tool arguments to include:

* Arguments for Prompts.
* Variables for Resource Templates.
* Arbitrary properties such as within the top-level `_meta` object (e.g.,
standardizing `_meta.tenant_id` to `Mcp-Header-Tenant-Id`).

The purpose of this document is to evaluate whether this generalized approach
can be standardized without introducing unreasonable complexity, and to outline
the design constraints and alternative implementations.

## Design Challenges & Constraints

Shifting from a targeted schema annotation to a generalized payload-to-header
mapping engine introduces several structural and lifecycle challenges for the
MCP protocol.

### Limited to Primitives

Applying header mapping to arbitrary JSON-RPC requests necessitates a stateful
protocol design, which our current goal is to eliminate.

* To elevate an arbitrary field (like `_meta.tenant_id`) across any request, the
client needs a global set of mapping rules. If the server dictates these rules
(e.g., during an `initialize` phase), the client is forced to maintain this
global state and evaluate it before every subsequent outbound request.
* Consequently, we are practically constrained to acting on specific primitives
with a concrete schema where we can specify the mapping syntax. By attaching
mapping rules directly to a primitive's definition (like a Tool's
`inputSchema` returned from `tools/list`), the client predictably knows how to
format the specific execution request (`tools/call`) for that item without
relying on global protocol state.

### Defining a Generalized Mapping Syntax

If we abandon schemas entirely to support arbitrary fields (like `_meta`), the
specification must standardize a path-evaluation syntax.

* The protocol would need to adopt and enforce support for a querying language
like JSON Pointer or a restricted JSONPath subset (e.g., mapping
`$._meta.routing.tenantId` to an `MCP-Header-Tenant-Id`).
* This significantly increases the implementation complexity for all MCP client
SDKs, which would now need to include a universally consistent path-evaluation
engine to process outgoing requests.

### Security Considerations

We considered the security implications of a generalized payload-to-header
mapping engine. Ultimately, we concluded that this approach does not introduce
fundamentally new security vulnerabilities.

Because the custom headers simply mirror data that is already present within the
encrypted JSON-RPC request body, no new secrets are transmitted over the wire.
Furthermore, network infrastructure and compliance standards generally treat
HTTP headers as sensitive or PII.

## Proposed Alternative Designs & Trade-offs

### Option A: Tool-Only Schema Annotations (Current Approach)

*This is the current SEP draft approach.* Header mapping is strictly limited to
`tools/call` parameters using the `x-mcp-header` extension within a tool's
`inputSchema`.

**Example Tool Definition:**

```json
{
"name": "execute_query",
"description": "Executes a query in a specific region.",
"inputSchema": {
"type": "object",
"properties": {
"region": {
"type": "string",
"description": "The AWS region",
"x-mcp-header": "Region"
},
"query": {
"type": "string"
}
},
"required": ["region", "query"]
}
}
```

*Client Output:* When calling this tool with `{"region": "us-east-1"}`, the
client automatically appends the header: `MCP-Header-Region: us-east-1`.

* **Pros:**
* Minimal changes to core MCP specification (JSON Schema natively supports
`x-` extensions).
* Low implementation complexity for client and server SDKs.
* **Cons:**
* Asymmetric scope. It does not support Prompts, Resources, or top-level
JSON-RPC fields like `_meta`.

### Option B: Updating Prompts & Resources

Achieve parity across specific primitives by updating the core protocol to
support explicit header mapping fields.

**Example 1: Prompt Definition**

Currently, Prompts use a simple `arguments` array. We would need to update the
`PromptArgument` interface to include an optional `mcpHeader` field.

```json
{
"name": "code_review",
"description": "Review code for a specific tenant.",
"arguments": [
{
"name": "tenant_id",
"description": "The customer tenant ID",
"required": true,
"mcpHeader": "Tenant-Id"
},
{
"name": "source_code",
"description": "The code to review",
"required": true
}
]
}
```

*Client Output:* When fetching this prompt with `{"tenant_id": "acme-corp"}`,
the client appends the header: `MCP-Param-Tenant-Id: acme-corp`.

**Example 2: Templated Resource Definition**

Currently, Resource Templates only define a `uriTemplate` string (e.g.,
`file:///{tenant}/config.json`). They do not have an arguments array. To map a
URI variable to a header, we would need to redesign the core Resource Template
object to include a structured `variables` list.

```json
{
"name": "Tenant Configuration",
"uriTemplate": "file:///{tenant}/config.json",
"variables": [
{
"name": "tenant",
"description": "The isolated tenant environment",
"mcpHeader": "Resource-Tenant"
}
]
}

```

*Client Output:* When a client reads `file:///acme-corp/config.json`, the client
parses the URI against the template, extracts the `tenant` variable, and appends
the header: `MCP-Header-Resource-Tenant: acme-corp`.

**Pros:**

* Achieves conceptual consistency across all executable MCP primitives (Tools,
Prompts, Resources).

**Cons:**

* Requires inventing new data structures (like the `variables` array for
Resource Templates) solely to support a transport-layer feature.
* Still fails to provide a generalized solution for arbitrary, top-level
JSON-RPC fields like `_meta`.

### Option C: Generalized Path Mapping

Instead of modifying parameter schemas, generalized mapping rules are attached
as sidecar metadata to specific Tool, Prompt, or Resource definitions. This uses
the JSON Pointer (RFC 6901) standard to safely extract arbitrary fields.

**Example Tool Definition:**

```json
{
"name": "execute_query",
"inputSchema": {
"type": "object",
"properties": {
"region": { "type": "string" }
}
},
"httpHeaders": [
{
"path": "/_meta/tenantId",
"header": "MCP-Header-Tenant"
},
{
"path": "/params/arguments/region",
"header": "MCP-Header-Region"
}
]
}

```

*Client Output:* When the client calls `execute_query`, it evaluates the JSON
Pointers against the outgoing request payload and appends the resulting headers.

* **Pros:**
* **Solves the Discovery Paradox:** Clients discover the rules during
`tools/list` or `prompts/list`.
* **Highly Flexible:** Can extract arbitrary fields like `_meta.tenantId`,
which Option A and B cannot do.
* **Separation of Concerns:** Keeps the core `inputSchema` pure, avoiding
mixing transport routing details into semantic schemas.
* **Cons:**
* **Ecosystem Parity:** Requires standardizing JSON Pointer across all language SDKs to ensure parsing consistency.
* **Breaks Co-location:** The definition of a parameter and its routing
behavior are separated, creating a synchronization burden for developers.