Skip to content

Commit 17249c3

Browse files
author
Alberto Farah
committed
fix(streamable_http): allow Content-Type header override
Allow StreamableHTTPTransport and streamable_http_client to accept a custom content_type parameter instead of hardcoding 'application/json'. This enables clients to override the Content-Type header to include custom charsets (e.g. 'application/json; charset=utf-8') or other attributes required by specific server implementations. Fixes: #2375
1 parent b33c811 commit 17249c3

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

src/mcp/client/streamable_http.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,17 @@ class RequestContext:
7272
class StreamableHTTPTransport:
7373
"""StreamableHTTP client transport implementation."""
7474

75-
def __init__(self, url: str) -> None:
75+
def __init__(self, url: str, content_type: str = "application/json") -> None:
7676
"""Initialize the StreamableHTTP transport.
7777
7878
Args:
7979
url: The endpoint URL.
80+
content_type: The Content-Type header value for POST requests.
81+
Defaults to "application/json". Can be overridden to include
82+
custom charsets (e.g. "application/json; charset=utf-8").
8083
"""
8184
self.url = url
85+
self.content_type = content_type
8286
self.session_id: str | None = None
8387
self.protocol_version: str | None = None
8488

@@ -90,7 +94,7 @@ def _prepare_headers(self) -> dict[str, str]:
9094
"""
9195
headers: dict[str, str] = {
9296
"accept": "application/json, text/event-stream",
93-
"content-type": "application/json",
97+
"content-type": self.content_type,
9498
}
9599
# Add session headers if available
96100
if self.session_id:
@@ -515,6 +519,7 @@ async def streamable_http_client(
515519
*,
516520
http_client: httpx.AsyncClient | None = None,
517521
terminate_on_close: bool = True,
522+
content_type: str = "application/json",
518523
) -> AsyncGenerator[TransportStreams, None]:
519524
"""Client transport for StreamableHTTP.
520525
@@ -524,6 +529,9 @@ async def streamable_http_client(
524529
client with recommended MCP timeouts will be created. To configure headers,
525530
authentication, or other HTTP settings, create an httpx.AsyncClient and pass it here.
526531
terminate_on_close: If True, send a DELETE request to terminate the session when the context exits.
532+
content_type: The Content-Type header value for POST requests.
533+
Defaults to "application/json". Can be overridden to include custom charsets
534+
(e.g. "application/json; charset=utf-8").
527535
528536
Yields:
529537
Tuple containing:
@@ -544,7 +552,7 @@ async def streamable_http_client(
544552
# Create default client with recommended MCP timeouts
545553
client = create_mcp_http_client()
546554

547-
transport = StreamableHTTPTransport(url)
555+
transport = StreamableHTTPTransport(url, content_type=content_type)
548556

549557
async with anyio.create_task_group() as tg:
550558
try:
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Tests for Content-Type override in streamable HTTP transport.
2+
3+
Verifies that the content_type parameter allows overriding the Content-Type header
4+
to include custom charsets or other attributes.
5+
"""
6+
7+
import pytest
8+
from mcp.client.streamable_http import StreamableHTTPTransport
9+
10+
11+
def test_streamable_http_transport_default_content_type() -> None:
12+
"""Test that the default Content-Type is 'application/json'."""
13+
transport = StreamableHTTPTransport("http://example.com/mcp")
14+
headers = transport._prepare_headers()
15+
assert headers["content-type"] == "application/json"
16+
17+
18+
def test_streamable_http_transport_custom_content_type() -> None:
19+
"""Test that a custom Content-Type with charset can be specified."""
20+
transport = StreamableHTTPTransport(
21+
"http://example.com/mcp",
22+
content_type="application/json; charset=utf-8",
23+
)
24+
headers = transport._prepare_headers()
25+
assert headers["content-type"] == "application/json; charset=utf-8"
26+
27+
28+
def test_streamable_http_transport_content_type_preserved_in_headers() -> None:
29+
"""Test that Content-Type is correctly placed in prepared headers."""
30+
custom_type = "application/json; charset=utf-8; boundary=npm"
31+
transport = StreamableHTTPTransport("http://example.com/mcp", content_type=custom_type)
32+
headers = transport._prepare_headers()
33+
# The accept header should also be present
34+
assert "accept" in headers
35+
assert headers["content-type"] == custom_type

0 commit comments

Comments
 (0)