Skip to content

Commit 41ceacd

Browse files
authored
Use CLAUDE_CODE_STREAM_CLOSE_TIMEOUT (if present) to override initialize() timeout (#354)
1 parent f446e3e commit 41ceacd

File tree

2 files changed

+25
-4
lines changed

2 files changed

+25
-4
lines changed

src/claude_agent_sdk/_internal/query.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def __init__(
7272
| None = None,
7373
hooks: dict[str, list[dict[str, Any]]] | None = None,
7474
sdk_mcp_servers: dict[str, "McpServer"] | None = None,
75+
initialize_timeout: float = 60.0,
7576
):
7677
"""Initialize Query with transport and callbacks.
7778
@@ -81,7 +82,9 @@ def __init__(
8182
can_use_tool: Optional callback for tool permission requests
8283
hooks: Optional hook configurations
8384
sdk_mcp_servers: Optional SDK MCP server instances
85+
initialize_timeout: Timeout in seconds for the initialize request
8486
"""
87+
self._initialize_timeout = initialize_timeout
8588
self.transport = transport
8689
self.is_streaming_mode = is_streaming_mode
8790
self.can_use_tool = can_use_tool
@@ -140,7 +143,10 @@ async def initialize(self) -> dict[str, Any] | None:
140143
"hooks": hooks_config if hooks_config else None,
141144
}
142145

143-
response = await self._send_control_request(request)
146+
# Use longer timeout for initialize since MCP servers may take time to start
147+
response = await self._send_control_request(
148+
request, timeout=self._initialize_timeout
149+
)
144150
self._initialized = True
145151
self._initialization_result = response # Store for later access
146152
return response
@@ -315,8 +321,15 @@ async def _handle_control_request(self, request: SDKControlRequest) -> None:
315321
}
316322
await self.transport.write(json.dumps(error_response) + "\n")
317323

318-
async def _send_control_request(self, request: dict[str, Any]) -> dict[str, Any]:
319-
"""Send control request to CLI and wait for response."""
324+
async def _send_control_request(
325+
self, request: dict[str, Any], timeout: float = 60.0
326+
) -> dict[str, Any]:
327+
"""Send control request to CLI and wait for response.
328+
329+
Args:
330+
request: The control request to send
331+
timeout: Timeout in seconds to wait for response (default 60s)
332+
"""
320333
if not self.is_streaming_mode:
321334
raise Exception("Control requests require streaming mode")
322335

@@ -339,7 +352,7 @@ async def _send_control_request(self, request: dict[str, Any]) -> dict[str, Any]
339352

340353
# Wait for response
341354
try:
342-
with anyio.fail_after(60.0):
355+
with anyio.fail_after(timeout):
343356
await event.wait()
344357

345358
result = self.pending_control_results.pop(request_id)

src/claude_agent_sdk/client.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@ async def _empty_stream() -> AsyncIterator[dict[str, Any]]:
140140
if isinstance(config, dict) and config.get("type") == "sdk":
141141
sdk_mcp_servers[name] = config["instance"] # type: ignore[typeddict-item]
142142

143+
# Calculate initialize timeout from CLAUDE_CODE_STREAM_CLOSE_TIMEOUT env var if set
144+
# CLAUDE_CODE_STREAM_CLOSE_TIMEOUT is in milliseconds, convert to seconds
145+
initialize_timeout_ms = int(
146+
os.environ.get("CLAUDE_CODE_STREAM_CLOSE_TIMEOUT", "60000")
147+
)
148+
initialize_timeout = max(initialize_timeout_ms / 1000.0, 60.0)
149+
143150
# Create Query to handle control protocol
144151
self._query = Query(
145152
transport=self._transport,
@@ -149,6 +156,7 @@ async def _empty_stream() -> AsyncIterator[dict[str, Any]]:
149156
if self.options.hooks
150157
else None,
151158
sdk_mcp_servers=sdk_mcp_servers,
159+
initialize_timeout=initialize_timeout,
152160
)
153161

154162
# Start reading messages and initialize

0 commit comments

Comments
 (0)