Skip to content

Commit 55dede7

Browse files
Optionize initialized notification tolerance
Co-authored-by: Codex <noreply@openai.com>
1 parent f97611d commit 55dede7

2 files changed

Lines changed: 60 additions & 3 deletions

File tree

src/agents/mcp/server.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,17 @@ class RequireApprovalObject(TypedDict, total=False):
7979

8080

8181
class _AgentsStreamableHTTPTransport(StreamableHTTPTransport):
82+
def __init__(
83+
self,
84+
url: str,
85+
*,
86+
ignore_initialized_notification_failure: bool = False,
87+
) -> None:
88+
super().__init__(url)
89+
self._ignore_initialized_notification_failure = (
90+
ignore_initialized_notification_failure
91+
)
92+
8293
async def post_writer(
8394
self,
8495
client: httpx.AsyncClient,
@@ -127,7 +138,10 @@ async def handle_request_async(
127138
try:
128139
await handle_request_async()
129140
except Exception:
130-
if self._is_initialized_notification(message):
141+
if (
142+
self._ignore_initialized_notification_failure
143+
and self._is_initialized_notification(message)
144+
):
131145
logger.warning(
132146
"Ignoring initialized notification failure in post_writer",
133147
exc_info=True,
@@ -150,6 +164,7 @@ async def streamablehttp_client(
150164
terminate_on_close: bool = True,
151165
httpx_client_factory: HttpClientFactory = create_mcp_http_client,
152166
auth: httpx.Auth | None = None,
167+
ignore_initialized_notification_failure: bool = False,
153168
):
154169
timeout_seconds = timeout.total_seconds() if isinstance(timeout, timedelta) else timeout
155170
sse_read_timeout_seconds = (
@@ -163,7 +178,10 @@ async def streamablehttp_client(
163178
timeout=httpx.Timeout(timeout_seconds, read=sse_read_timeout_seconds),
164179
auth=auth,
165180
)
166-
transport = _AgentsStreamableHTTPTransport(url)
181+
transport = _AgentsStreamableHTTPTransport(
182+
url,
183+
ignore_initialized_notification_failure=ignore_initialized_notification_failure,
184+
)
167185

168186
async with client:
169187
read_stream_writer, read_stream = anyio.create_memory_object_stream[
@@ -1286,6 +1304,14 @@ class MCPServerStreamableHttpParams(TypedDict):
12861304
transport.
12871305
"""
12881306

1307+
ignore_initialized_notification_failure: NotRequired[bool]
1308+
"""Whether to ignore failures when sending the best-effort
1309+
``notifications/initialized`` POST.
1310+
1311+
Defaults to ``False``. When set to ``True``, initialized-notification failures are
1312+
logged and ignored so subsequent requests on the same transport can continue.
1313+
"""
1314+
12891315

12901316
class MCPServerStreamableHttp(_MCPServerWithClientSession):
12911317
"""MCP server implementation that uses the Streamable HTTP transport. See the [spec]
@@ -1380,6 +1406,10 @@ def create_streams(
13801406
kwargs["httpx_client_factory"] = self.params["httpx_client_factory"]
13811407
if "auth" in self.params:
13821408
kwargs["auth"] = self.params["auth"]
1409+
if "ignore_initialized_notification_failure" in self.params:
1410+
kwargs["ignore_initialized_notification_failure"] = self.params[
1411+
"ignore_initialized_notification_failure"
1412+
]
13831413
return streamablehttp_client(**kwargs)
13841414

13851415
@asynccontextmanager

tests/mcp/test_streamable_http_client_factory.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,10 @@ def comprehensive_factory(
256256

257257
@pytest.mark.asyncio
258258
async def test_initialized_notification_failure_does_not_stop_following_requests():
259-
transport = _AgentsStreamableHTTPTransport("https://example.test/mcp")
259+
transport = _AgentsStreamableHTTPTransport(
260+
"https://example.test/mcp",
261+
ignore_initialized_notification_failure=True,
262+
)
260263
request_handled = asyncio.Event()
261264

262265
async def fake_handle_post_request(ctx):
@@ -299,3 +302,27 @@ async def fake_handle_post_request(ctx):
299302

300303
await write_stream.aclose()
301304
tg.cancel_scope.cancel()
305+
306+
307+
@pytest.mark.asyncio
308+
async def test_streamable_http_server_passes_ignore_initialized_notification_failure():
309+
with patch("agents.mcp.server.streamablehttp_client") as mock_client:
310+
mock_client.return_value = MagicMock()
311+
312+
server = MCPServerStreamableHttp(
313+
params={
314+
"url": "http://localhost:8000/mcp",
315+
"ignore_initialized_notification_failure": True,
316+
}
317+
)
318+
319+
server.create_streams()
320+
321+
mock_client.assert_called_once_with(
322+
url="http://localhost:8000/mcp",
323+
headers=None,
324+
timeout=5,
325+
sse_read_timeout=300,
326+
terminate_on_close=True,
327+
ignore_initialized_notification_failure=True,
328+
)

0 commit comments

Comments
 (0)