What Custom Headers Are Automatically Added to MCP Requests in Dify Plugins
The Dify MCP SSE plugin automatically injects Content-Type: application/json, Accept: application/json, text/event-stream, and Mcp-Session-Id headers into MCP requests to ensure JSON-RPC 2.0 compliance, content negotiation, and session affinity.
When integrating with the junjiem/dify-plugin-tools-mcp_sse repository, understanding what custom headers are automatically added to MCP requests is critical for debugging authentication flows and managing stateful connections. The plugin supports two transport mechanisms—Server-Sent Events (SSE) and streamable HTTP—each automatically injecting specific headers to maintain protocol compliance and session state.
Automatically Injected MCP Headers
The transport clients in utils/mcp_client.py automatically append specific headers based on the transport type and connection state.
Content-Type: application/json
Both the SSE-based client (McpSseClient.send_message) and the streamable-HTTP client (McpStreamableHttpClient.send_message) automatically inject Content-Type: application/json when sending POST requests to the MCP server.
This header informs the MCP server that the request body follows JSON-RPC 2.0 encoding, which is required for all MCP method calls according to the protocol specification.
Accept: application/json, text/event-stream
The Accept: application/json, text/event-stream header is automatically added by McpStreamableHttpClient.send_message during HTTP transport initialization.
This content-negotiation header tells the MCP server that the client can process either standard JSON responses or Server-Sent Events (SSE) streams. The client handles both formats dynamically based on the server's response type, enabling flexible transport mechanisms.
Mcp-Session-Id: Session Affinity
The Mcp-Session-Id header provides session affinity for stateful MCP connections and is managed dynamically by McpStreamableHttpClient:
- After the first server response containing the
mcp-session-idheader, the client extracts and stores the UUID - The client capitalizes the header name as
Mcp-Session-Idwhen storing it (headers["Mcp-Session-Id"] = self.session_id) - Every subsequent request automatically includes this header without manual intervention
This mechanism ensures the server can associate all calls with the same logical session for state preservation, rate-limiting, or maintaining authentication context across multiple requests.
User-Defined Custom Headers
Beyond automatically injected protocol headers, the plugin supports user-defined headers through server configuration in provider/mcp_tool.yaml.
When configuring an MCP server, developers can specify a headers dictionary in the configuration. These headers are retrieved via config.get("headers", None) and passed directly to the underlying httpx.Client initialization.
This mechanism allows injection of deployment-specific headers such as API keys, tenant identifiers, or custom authentication tokens that are not part of the MCP protocol but are required by specific server implementations.
Implementation Details in the Source Code
The header injection logic resides primarily in utils/mcp_client.py, which contains both client implementations:
McpSseClient.send_message: Handles SSE transport and injectsContent-Type: application/jsonfor POST requestsMcpStreamableHttpClient.send_message: Handles streamable HTTP transport and managesContent-Type,Accept, and dynamicMcp-Session-Idheaders
The session ID persistence mechanism checks for the mcp-session-id response header after each request and updates the client's session_id attribute, which is then referenced in subsequent request headers.
Practical Code Examples
Streamable HTTP Client with Automatic Headers
# Streamable‑HTTP client – automatic headers when sending a request
client = McpStreamableHttpClient(
name="my_mcp",
url="https://example.com/mcp",
headers={"Authorization": "Bearer <token>"}, # user‑provided, forwarded automatically
)
# First request – only the JSON headers are sent
client.send_message({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {...}
})
# => Headers sent:
# Content-Type: application/json
# Accept: application/json, text/event-stream
# Authorization: Bearer <token>
# Server answers with a session header
# mcp-session-id: 9b3e1fca‑d4a7‑4f2b‑a0f4‑c1e2d5e7f9a0
# Subsequent requests automatically include the session header
client.send_message({...})
# => Headers sent now also include:
# Mcp-Session-Id: 9b3e1fca‑d4a7‑4f2b‑a0f4‑c1e2d5e7f9a0
SSE Client with User-Defined Headers
# SSE client – only the JSON header is needed for POSTs
sse_client = McpSseClient(
name="my_mcp",
url="https://example.com/mcp/sse",
headers={"X-Custom": "value"} # user‑provided, forwarded automatically
)
sse_client.send_message({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
})
# => Headers sent:
# Content-Type: application/json
# X-Custom: value
Summary
Content-Type: application/jsonis automatically added by bothMcpSseClientandMcpStreamableHttpClientto ensure JSON-RPC 2.0 compliance.Accept: application/json, text/event-streamis injected by the streamable HTTP client to negotiate response formats.Mcp-Session-Idis dynamically managed byMcpStreamableHttpClientafter the server returns a session ID, ensuring stateful session affinity.- User-defined headers from the YAML configuration in
provider/mcp_tool.yamlare propagated throughhttpx.Clientfor deployment-specific requirements like authentication tokens.
Frequently Asked Questions
What is the purpose of the Mcp-Session-Id header?
The Mcp-Session-Id header provides session affinity for stateful MCP connections. After the initial handshake, the server returns a unique session identifier that the client stores and resends with every subsequent request. This allows the server to associate all calls with the same logical session for state preservation, rate-limiting, or maintaining authentication context across multiple API calls.
Can I add custom authentication headers to MCP requests?
Yes, you can add custom authentication headers through the server configuration in provider/mcp_tool.yaml. When defining an MCP server, include a headers dictionary containing your required headers such as Authorization: Bearer <token> or API keys. These headers are retrieved via config.get("headers", None) and automatically passed to the underlying httpx.Client, ensuring they are included in every request alongside the automatically injected protocol headers.
Does the SSE client support the same headers as the HTTP client?
No, the SSE client (McpSseClient) and the streamable HTTP client (McpStreamableHttpClient) handle headers differently. Both automatically inject Content-Type: application/json, but only the HTTP client adds Accept: application/json, text/event-stream and manages the Mcp-Session-Id header for session affinity. The SSE transport maintains connection state through the SSE protocol itself rather than HTTP session headers, making the session ID header unnecessary for that transport mechanism.
Where are these headers configured in the Dify plugin?
The automatic headers are hardcoded in utils/mcp_client.py within the send_message methods of both client classes. User-defined custom headers are configured in provider/mcp_tool.yaml under the server configuration's headers field. These custom headers are then propagated through the httpx.Client initialization, allowing you to inject deployment-specific requirements like tenant identifiers or custom authentication tokens alongside the automatically managed protocol headers.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →