Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
This repository was archived by the owner on May 13, 2025. It is now read-only.

Commit ed25167

Browse filesBrowse files
authored
Introduce a function to create a standard AsyncClient with options (modelcontextprotocol#655)
1 parent 72003d9 commit ed25167
Copy full SHA for ed25167

File tree

Expand file treeCollapse file tree

6 files changed

+95
-8
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

6 files changed

+95
-8
lines changed
Open diff view settings
Collapse file

‎examples/servers/simple-auth/mcp_simple_auth/server.py‎

Copy file name to clipboardExpand all lines: examples/servers/simple-auth/mcp_simple_auth/server.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from typing import Any
77

88
import click
9-
import httpx
109
from pydantic import AnyHttpUrl
1110
from pydantic_settings import BaseSettings, SettingsConfigDict
1211
from starlette.exceptions import HTTPException
@@ -24,6 +23,7 @@
2423
)
2524
from mcp.server.auth.settings import AuthSettings, ClientRegistrationOptions
2625
from mcp.server.fastmcp.server import FastMCP
26+
from mcp.shared._httpx_utils import create_mcp_http_client
2727
from mcp.shared.auth import OAuthClientInformationFull, OAuthToken
2828

2929
logger = logging.getLogger(__name__)
@@ -123,7 +123,7 @@ async def handle_github_callback(self, code: str, state: str) -> str:
123123
client_id = state_data["client_id"]
124124

125125
# Exchange code for token with GitHub
126-
async with httpx.AsyncClient() as client:
126+
async with create_mcp_http_client() as client:
127127
response = await client.post(
128128
self.settings.github_token_url,
129129
data={
@@ -325,7 +325,7 @@ async def get_user_profile() -> dict[str, Any]:
325325
"""
326326
github_token = get_github_token()
327327

328-
async with httpx.AsyncClient() as client:
328+
async with create_mcp_http_client() as client:
329329
response = await client.get(
330330
"https://api.github.com/user",
331331
headers={
Collapse file

‎examples/servers/simple-tool/mcp_simple_tool/server.py‎

Copy file name to clipboardExpand all lines: examples/servers/simple-tool/mcp_simple_tool/server.py
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import anyio
22
import click
3-
import httpx
43
import mcp.types as types
54
from mcp.server.lowlevel import Server
5+
from mcp.shared._httpx_utils import create_mcp_http_client
66

77

88
async def fetch_website(
@@ -11,7 +11,7 @@ async def fetch_website(
1111
headers = {
1212
"User-Agent": "MCP Test Server (github.com/modelcontextprotocol/python-sdk)"
1313
}
14-
async with httpx.AsyncClient(follow_redirects=True, headers=headers) as client:
14+
async with create_mcp_http_client(headers=headers) as client:
1515
response = await client.get(url)
1616
response.raise_for_status()
1717
return [types.TextContent(type="text", text=response.text)]
Collapse file

‎src/mcp/client/sse.py‎

Copy file name to clipboardExpand all lines: src/mcp/client/sse.py
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from httpx_sse import aconnect_sse
1111

1212
import mcp.types as types
13+
from mcp.shared._httpx_utils import create_mcp_http_client
1314
from mcp.shared.message import SessionMessage
1415

1516
logger = logging.getLogger(__name__)
@@ -44,7 +45,7 @@ async def sse_client(
4445
async with anyio.create_task_group() as tg:
4546
try:
4647
logger.info(f"Connecting to SSE endpoint: {remove_request_params(url)}")
47-
async with httpx.AsyncClient(headers=headers) as client:
48+
async with create_mcp_http_client(headers=headers) as client:
4849
async with aconnect_sse(
4950
client,
5051
"GET",
Collapse file

‎src/mcp/client/streamable_http.py‎

Copy file name to clipboardExpand all lines: src/mcp/client/streamable_http.py
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
1919
from httpx_sse import EventSource, ServerSentEvent, aconnect_sse
2020

21+
from mcp.shared._httpx_utils import create_mcp_http_client
2122
from mcp.shared.message import ClientMessageMetadata, SessionMessage
2223
from mcp.types import (
2324
ErrorData,
@@ -446,12 +447,11 @@ async def streamablehttp_client(
446447
try:
447448
logger.info(f"Connecting to StreamableHTTP endpoint: {url}")
448449

449-
async with httpx.AsyncClient(
450+
async with create_mcp_http_client(
450451
headers=transport.request_headers,
451452
timeout=httpx.Timeout(
452453
transport.timeout.seconds, read=transport.sse_read_timeout.seconds
453454
),
454-
follow_redirects=True,
455455
) as client:
456456
# Define callbacks that need access to tg
457457
def start_get_stream() -> None:
Collapse file

‎src/mcp/shared/_httpx_utils.py‎

Copy file name to clipboard
+62Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""Utilities for creating standardized httpx AsyncClient instances."""
2+
3+
from typing import Any
4+
5+
import httpx
6+
7+
__all__ = ["create_mcp_http_client"]
8+
9+
10+
def create_mcp_http_client(
11+
headers: dict[str, str] | None = None,
12+
timeout: httpx.Timeout | None = None,
13+
) -> httpx.AsyncClient:
14+
"""Create a standardized httpx AsyncClient with MCP defaults.
15+
16+
This function provides common defaults used throughout the MCP codebase:
17+
- follow_redirects=True (always enabled)
18+
- Default timeout of 30 seconds if not specified
19+
20+
Args:
21+
headers: Optional headers to include with all requests.
22+
timeout: Request timeout as httpx.Timeout object.
23+
Defaults to 30 seconds if not specified.
24+
25+
Returns:
26+
Configured httpx.AsyncClient instance with MCP defaults.
27+
28+
Note:
29+
The returned AsyncClient must be used as a context manager to ensure
30+
proper cleanup of connections.
31+
32+
Examples:
33+
# Basic usage with MCP defaults
34+
async with create_mcp_http_client() as client:
35+
response = await client.get("https://api.example.com")
36+
37+
# With custom headers
38+
headers = {"Authorization": "Bearer token"}
39+
async with create_mcp_http_client(headers) as client:
40+
response = await client.get("/endpoint")
41+
42+
# With both custom headers and timeout
43+
timeout = httpx.Timeout(60.0, read=300.0)
44+
async with create_mcp_http_client(headers, timeout) as client:
45+
response = await client.get("/long-request")
46+
"""
47+
# Set MCP defaults
48+
kwargs: dict[str, Any] = {
49+
"follow_redirects": True,
50+
}
51+
52+
# Handle timeout
53+
if timeout is None:
54+
kwargs["timeout"] = httpx.Timeout(30.0)
55+
else:
56+
kwargs["timeout"] = timeout
57+
58+
# Handle headers
59+
if headers is not None:
60+
kwargs["headers"] = headers
61+
62+
return httpx.AsyncClient(**kwargs)
Collapse file

‎tests/shared/test_httpx_utils.py‎

Copy file name to clipboard
+24Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""Tests for httpx utility functions."""
2+
3+
import httpx
4+
5+
from mcp.shared._httpx_utils import create_mcp_http_client
6+
7+
8+
def test_default_settings():
9+
"""Test that default settings are applied correctly."""
10+
client = create_mcp_http_client()
11+
12+
assert client.follow_redirects is True
13+
assert client.timeout.connect == 30.0
14+
15+
16+
def test_custom_parameters():
17+
"""Test custom headers and timeout are set correctly."""
18+
headers = {"Authorization": "Bearer token"}
19+
timeout = httpx.Timeout(60.0)
20+
21+
client = create_mcp_http_client(headers, timeout)
22+
23+
assert client.headers["Authorization"] == "Bearer token"
24+
assert client.timeout.connect == 60.0

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.