From a329c46489fff4e1dda9dee2d587f24633f1183b Mon Sep 17 00:00:00 2001 From: Romain Sestier Date: Mon, 6 Apr 2026 15:45:13 -0700 Subject: [PATCH] feat: add configurable HTTP timeout for tool execution httpx.request() was called without a timeout, defaulting to 5s. Slow providers (e.g. Workday) regularly exceed this, causing ReadTimeout errors. - Add `timeout` field to `ExecuteConfig` (default 30s) - Pass it through to `httpx.request()` - Add `timeout` to `ExecuteToolsConfig` so users can set it at init: `StackOneToolSet(execute={"account_ids": [...], "timeout": 60})` - Thread timeout from toolset config through `_StackOneRpcTool` Co-Authored-By: Claude Opus 4.6 (1M context) --- stackone_ai/models.py | 3 ++- stackone_ai/toolset.py | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/stackone_ai/models.py b/stackone_ai/models.py index f58d91b..885d0e0 100644 --- a/stackone_ai/models.py +++ b/stackone_ai/models.py @@ -65,6 +65,7 @@ class ExecuteConfig(BaseModel): parameter_locations: dict[str, ParameterLocation] = Field( default_factory=dict, description="Maps parameter names to their location in the request" ) + timeout: float = Field(default=30.0, description="HTTP request timeout in seconds") class ToolParameters(BaseModel): @@ -249,7 +250,7 @@ def execute( if query_params: request_kwargs["params"] = query_params - response = httpx.request(**request_kwargs) + response = httpx.request(**request_kwargs, timeout=self._execute_config.timeout) response_status = response.status_code response.raise_for_status() diff --git a/stackone_ai/toolset.py b/stackone_ai/toolset.py index c13fc73..9c227ba 100644 --- a/stackone_ai/toolset.py +++ b/stackone_ai/toolset.py @@ -69,6 +69,9 @@ class ExecuteToolsConfig(TypedDict, total=False): account_ids: list[str] """Account IDs to scope tool discovery and execution.""" + timeout: float + """HTTP request timeout in seconds for tool execution. Defaults to 30.""" + _SEARCH_DEFAULT: SearchConfig = {"method": "auto"} @@ -415,6 +418,7 @@ def __init__( api_key: str, base_url: str, account_id: str | None, + timeout: float = 30.0, ) -> None: execute_config = ExecuteConfig( method="POST", @@ -423,6 +427,7 @@ def __init__( headers={}, body_type="json", parameter_locations=dict(_RPC_PARAMETER_LOCATIONS), + timeout=timeout, ) super().__init__( description=description, @@ -1185,6 +1190,9 @@ def _create_rpc_tool(self, tool_def: _McpToolDefinition, account_id: str | None) type=str(schema.get("type") or "object"), properties=self._normalize_schema_properties(schema), ) + timeout = ( + self._execute_config.get("timeout", 30.0) if self._execute_config else 30.0 + ) return _StackOneRpcTool( name=tool_def.name, description=tool_def.description or "", @@ -1192,6 +1200,7 @@ def _create_rpc_tool(self, tool_def: _McpToolDefinition, account_id: str | None) api_key=self.api_key, base_url=self.base_url, account_id=account_id, + timeout=timeout, ) def _normalize_schema_properties(self, schema: dict[str, Any]) -> dict[str, Any]: