chuk-tool-processor 0.6.27__tar.gz → 0.6.29__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of chuk-tool-processor might be problematic. Click here for more details.

Files changed (66) hide show
  1. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/PKG-INFO +74 -1
  2. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/README.md +73 -0
  3. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/pyproject.toml +1 -1
  4. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/core/processor.py +2 -2
  5. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/mcp_tool.py +5 -5
  6. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/stream_manager.py +37 -25
  7. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/transport/__init__.py +10 -0
  8. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/transport/http_streamable_transport.py +93 -89
  9. chuk_tool_processor-0.6.29/src/chuk_tool_processor/mcp/transport/models.py +100 -0
  10. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/transport/sse_transport.py +61 -61
  11. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/transport/stdio_transport.py +5 -5
  12. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/discovery.py +1 -1
  13. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor.egg-info/PKG-INFO +74 -1
  14. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor.egg-info/SOURCES.txt +1 -0
  15. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/setup.cfg +0 -0
  16. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/__init__.py +0 -0
  17. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/core/__init__.py +0 -0
  18. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/core/exceptions.py +0 -0
  19. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/__init__.py +0 -0
  20. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/strategies/__init__.py +0 -0
  21. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/strategies/inprocess_strategy.py +0 -0
  22. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/strategies/subprocess_strategy.py +0 -0
  23. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/tool_executor.py +0 -0
  24. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/wrappers/__init__.py +0 -0
  25. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/wrappers/caching.py +0 -0
  26. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/wrappers/rate_limiting.py +0 -0
  27. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/wrappers/retry.py +0 -0
  28. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/logging/__init__.py +0 -0
  29. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/logging/context.py +0 -0
  30. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/logging/formatter.py +0 -0
  31. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/logging/helpers.py +0 -0
  32. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/logging/metrics.py +0 -0
  33. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/__init__.py +0 -0
  34. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/register_mcp_tools.py +0 -0
  35. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/setup_mcp_http_streamable.py +0 -0
  36. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/setup_mcp_sse.py +0 -0
  37. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/setup_mcp_stdio.py +0 -0
  38. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/transport/base_transport.py +0 -0
  39. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/__init__.py +0 -0
  40. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/execution_strategy.py +0 -0
  41. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/streaming_tool.py +0 -0
  42. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/tool_call.py +0 -0
  43. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/tool_export_mixin.py +0 -0
  44. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/tool_result.py +0 -0
  45. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/validated_tool.py +0 -0
  46. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/__init__.py +0 -0
  47. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/__init__.py +0 -0
  48. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/base.py +0 -0
  49. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/function_call_tool.py +0 -0
  50. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/json_tool.py +0 -0
  51. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/openai_tool.py +0 -0
  52. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/xml_tool.py +0 -0
  53. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/__init__.py +0 -0
  54. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/auto_register.py +0 -0
  55. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/decorators.py +0 -0
  56. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/interface.py +0 -0
  57. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/metadata.py +0 -0
  58. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/provider.py +0 -0
  59. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/providers/__init__.py +0 -0
  60. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/providers/memory.py +0 -0
  61. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/tool_export.py +0 -0
  62. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/utils/__init__.py +0 -0
  63. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/utils/validation.py +0 -0
  64. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor.egg-info/dependency_links.txt +0 -0
  65. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor.egg-info/requires.txt +0 -0
  66. {chuk_tool_processor-0.6.27 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chuk-tool-processor
3
- Version: 0.6.27
3
+ Version: 0.6.29
4
4
  Summary: Async-native framework for registering, discovering, and executing tools referenced in LLM responses
5
5
  Author-email: CHUK Team <chrishayuk@somejunkmailbox.com>
6
6
  Maintainer-email: CHUK Team <chrishayuk@somejunkmailbox.com>
@@ -941,6 +941,79 @@ async def test_calculator():
941
941
 
942
942
  ## Configuration
943
943
 
944
+ ### Timeout Configuration
945
+
946
+ CHUK Tool Processor uses a unified timeout configuration system that applies to all MCP transports (HTTP Streamable, SSE, STDIO) and the StreamManager. Instead of managing dozens of individual timeout values, there are just **4 logical timeout categories**:
947
+
948
+ ```python
949
+ from chuk_tool_processor.mcp.transport import TimeoutConfig
950
+
951
+ # Create custom timeout configuration
952
+ timeout_config = TimeoutConfig(
953
+ connect=30.0, # Connection establishment, initialization, session discovery
954
+ operation=30.0, # Normal operations (tool calls, listing tools/resources/prompts)
955
+ quick=5.0, # Fast health checks and pings
956
+ shutdown=2.0 # Cleanup and shutdown operations
957
+ )
958
+ ```
959
+
960
+ **Using timeout configuration with StreamManager:**
961
+
962
+ ```python
963
+ from chuk_tool_processor.mcp.stream_manager import StreamManager
964
+ from chuk_tool_processor.mcp.transport import TimeoutConfig
965
+
966
+ # Create StreamManager with custom timeouts
967
+ timeout_config = TimeoutConfig(
968
+ connect=60.0, # Longer for slow initialization
969
+ operation=45.0, # Longer for heavy operations
970
+ quick=3.0, # Faster health checks
971
+ shutdown=5.0 # More time for cleanup
972
+ )
973
+
974
+ manager = StreamManager(timeout_config=timeout_config)
975
+ ```
976
+
977
+ **Timeout categories explained:**
978
+
979
+ | Category | Default | Used For | Examples |
980
+ |----------|---------|----------|----------|
981
+ | `connect` | 30.0s | Connection setup, initialization, discovery | HTTP connection, SSE session discovery, STDIO subprocess launch |
982
+ | `operation` | 30.0s | Normal tool operations | Tool calls, listing tools/resources/prompts, get_tools() |
983
+ | `quick` | 5.0s | Fast health/status checks | Ping operations, health checks |
984
+ | `shutdown` | 2.0s | Cleanup and teardown | Transport close, connection cleanup |
985
+
986
+ **Why this matters:**
987
+ - ✅ **Simple**: 4 timeout values instead of 20+
988
+ - ✅ **Consistent**: Same timeout behavior across all transports
989
+ - ✅ **Configurable**: Adjust timeouts based on your environment (slow networks, large datasets, etc.)
990
+ - ✅ **Type-safe**: Pydantic validation ensures correct values
991
+
992
+ **Example: Adjusting for slow environments**
993
+
994
+ ```python
995
+ from chuk_tool_processor.mcp import setup_mcp_stdio
996
+ from chuk_tool_processor.mcp.transport import TimeoutConfig
997
+
998
+ # For slow network or resource-constrained environments
999
+ slow_timeouts = TimeoutConfig(
1000
+ connect=120.0, # Allow more time for package downloads
1001
+ operation=60.0, # Allow more time for heavy operations
1002
+ quick=10.0, # Be patient with health checks
1003
+ shutdown=10.0 # Allow thorough cleanup
1004
+ )
1005
+
1006
+ processor, manager = await setup_mcp_stdio(
1007
+ config_file="mcp_config.json",
1008
+ servers=["sqlite"],
1009
+ namespace="db",
1010
+ initialization_timeout=120.0
1011
+ )
1012
+
1013
+ # Set custom timeouts on the manager
1014
+ manager.timeout_config = slow_timeouts
1015
+ ```
1016
+
944
1017
  ### Environment Variables
945
1018
 
946
1019
  | Variable | Default | Description |
@@ -913,6 +913,79 @@ async def test_calculator():
913
913
 
914
914
  ## Configuration
915
915
 
916
+ ### Timeout Configuration
917
+
918
+ CHUK Tool Processor uses a unified timeout configuration system that applies to all MCP transports (HTTP Streamable, SSE, STDIO) and the StreamManager. Instead of managing dozens of individual timeout values, there are just **4 logical timeout categories**:
919
+
920
+ ```python
921
+ from chuk_tool_processor.mcp.transport import TimeoutConfig
922
+
923
+ # Create custom timeout configuration
924
+ timeout_config = TimeoutConfig(
925
+ connect=30.0, # Connection establishment, initialization, session discovery
926
+ operation=30.0, # Normal operations (tool calls, listing tools/resources/prompts)
927
+ quick=5.0, # Fast health checks and pings
928
+ shutdown=2.0 # Cleanup and shutdown operations
929
+ )
930
+ ```
931
+
932
+ **Using timeout configuration with StreamManager:**
933
+
934
+ ```python
935
+ from chuk_tool_processor.mcp.stream_manager import StreamManager
936
+ from chuk_tool_processor.mcp.transport import TimeoutConfig
937
+
938
+ # Create StreamManager with custom timeouts
939
+ timeout_config = TimeoutConfig(
940
+ connect=60.0, # Longer for slow initialization
941
+ operation=45.0, # Longer for heavy operations
942
+ quick=3.0, # Faster health checks
943
+ shutdown=5.0 # More time for cleanup
944
+ )
945
+
946
+ manager = StreamManager(timeout_config=timeout_config)
947
+ ```
948
+
949
+ **Timeout categories explained:**
950
+
951
+ | Category | Default | Used For | Examples |
952
+ |----------|---------|----------|----------|
953
+ | `connect` | 30.0s | Connection setup, initialization, discovery | HTTP connection, SSE session discovery, STDIO subprocess launch |
954
+ | `operation` | 30.0s | Normal tool operations | Tool calls, listing tools/resources/prompts, get_tools() |
955
+ | `quick` | 5.0s | Fast health/status checks | Ping operations, health checks |
956
+ | `shutdown` | 2.0s | Cleanup and teardown | Transport close, connection cleanup |
957
+
958
+ **Why this matters:**
959
+ - ✅ **Simple**: 4 timeout values instead of 20+
960
+ - ✅ **Consistent**: Same timeout behavior across all transports
961
+ - ✅ **Configurable**: Adjust timeouts based on your environment (slow networks, large datasets, etc.)
962
+ - ✅ **Type-safe**: Pydantic validation ensures correct values
963
+
964
+ **Example: Adjusting for slow environments**
965
+
966
+ ```python
967
+ from chuk_tool_processor.mcp import setup_mcp_stdio
968
+ from chuk_tool_processor.mcp.transport import TimeoutConfig
969
+
970
+ # For slow network or resource-constrained environments
971
+ slow_timeouts = TimeoutConfig(
972
+ connect=120.0, # Allow more time for package downloads
973
+ operation=60.0, # Allow more time for heavy operations
974
+ quick=10.0, # Be patient with health checks
975
+ shutdown=10.0 # Allow thorough cleanup
976
+ )
977
+
978
+ processor, manager = await setup_mcp_stdio(
979
+ config_file="mcp_config.json",
980
+ servers=["sqlite"],
981
+ namespace="db",
982
+ initialization_timeout=120.0
983
+ )
984
+
985
+ # Set custom timeouts on the manager
986
+ manager.timeout_config = slow_timeouts
987
+ ```
988
+
916
989
  ### Environment Variables
917
990
 
918
991
  | Variable | Default | Description |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "chuk-tool-processor"
7
- version = "0.6.27"
7
+ version = "0.6.29"
8
8
  description = "Async-native framework for registering, discovering, and executing tools referenced in LLM responses"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -260,7 +260,7 @@ class ToolProcessor:
260
260
  unknown_tools.append(call.tool)
261
261
 
262
262
  if unknown_tools:
263
- self.logger.warning(f"Unknown tools: {unknown_tools}")
263
+ self.logger.debug(f"Unknown tools: {unknown_tools}")
264
264
 
265
265
  # Execute tools
266
266
  results = await self.executor.execute(calls, timeout=timeout)
@@ -412,7 +412,7 @@ class ToolProcessor:
412
412
  duration=duration,
413
413
  num_calls=0,
414
414
  )
415
- self.logger.error(f"Parser {parser_name} failed: {str(e)}")
415
+ self.logger.debug(f"Parser {parser_name} failed: {str(e)}")
416
416
  return []
417
417
 
418
418
 
@@ -370,7 +370,7 @@ class MCPTool:
370
370
  self._circuit_open = False
371
371
  self._circuit_open_time = None
372
372
  self.connection_state = ConnectionState.HEALTHY
373
- logger.info(f"Circuit breaker closed for tool '{self.tool_name}' after successful execution")
373
+ logger.debug(f"Circuit breaker closed for tool '{self.tool_name}' after successful execution")
374
374
 
375
375
  async def _record_failure(self, is_connection_error: bool = False) -> None:
376
376
  """Record a failed execution."""
@@ -407,7 +407,7 @@ class MCPTool:
407
407
  self._circuit_open = False
408
408
  self._circuit_open_time = None
409
409
  self.connection_state = ConnectionState.HEALTHY
410
- logger.info(f"Circuit breaker reset for tool '{self.tool_name}' after timeout")
410
+ logger.debug(f"Circuit breaker reset for tool '{self.tool_name}' after timeout")
411
411
  return False
412
412
 
413
413
  return True
@@ -462,12 +462,12 @@ class MCPTool:
462
462
  self._circuit_open_time = None
463
463
  self._consecutive_failures = 0
464
464
  self.connection_state = ConnectionState.HEALTHY
465
- logger.info(f"Circuit breaker manually reset for tool '{self.tool_name}'")
465
+ logger.debug(f"Circuit breaker manually reset for tool '{self.tool_name}'")
466
466
 
467
467
  def disable_resilience(self) -> None:
468
468
  """Disable resilience features for this tool instance."""
469
469
  self.enable_resilience = False
470
- logger.info(f"Resilience features disabled for tool '{self.tool_name}'")
470
+ logger.debug(f"Resilience features disabled for tool '{self.tool_name}'")
471
471
 
472
472
  def set_stream_manager(self, stream_manager: StreamManager | None) -> None:
473
473
  """
@@ -482,7 +482,7 @@ class MCPTool:
482
482
  if self._circuit_open:
483
483
  self._circuit_open = False
484
484
  self._circuit_open_time = None
485
- logger.info(f"Circuit breaker closed for tool '{self.tool_name}' due to new stream manager")
485
+ logger.debug(f"Circuit breaker closed for tool '{self.tool_name}' due to new stream manager")
486
486
  else:
487
487
  self.connection_state = ConnectionState.DISCONNECTED
488
488
 
@@ -21,6 +21,7 @@ from chuk_tool_processor.mcp.transport import (
21
21
  MCPBaseTransport,
22
22
  SSETransport,
23
23
  StdioTransport,
24
+ TimeoutConfig,
24
25
  )
25
26
 
26
27
  logger = get_logger("chuk_tool_processor.mcp.stream_manager")
@@ -38,7 +39,7 @@ class StreamManager:
38
39
  - HTTP Streamable (modern replacement for SSE, spec 2025-03-26) with graceful headers handling
39
40
  """
40
41
 
41
- def __init__(self) -> None:
42
+ def __init__(self, timeout_config: TimeoutConfig | None = None) -> None:
42
43
  self.transports: dict[str, MCPBaseTransport] = {}
43
44
  self.server_info: list[dict[str, Any]] = []
44
45
  self.tool_to_server_map: dict[str, str] = {}
@@ -46,7 +47,7 @@ class StreamManager:
46
47
  self.all_tools: list[dict[str, Any]] = []
47
48
  self._lock = asyncio.Lock()
48
49
  self._closed = False # Track if we've been closed
49
- self._shutdown_timeout = 2.0 # Maximum time to spend on shutdown
50
+ self.timeout_config = timeout_config or TimeoutConfig()
50
51
 
51
52
  # ------------------------------------------------------------------ #
52
53
  # factory helpers with enhanced error handling #
@@ -182,7 +183,7 @@ class StreamManager:
182
183
  params, connection_timeout=initialization_timeout, default_timeout=default_timeout
183
184
  )
184
185
  elif transport_type == "sse":
185
- logger.warning(
186
+ logger.debug(
186
187
  "Using SSE transport in initialize() - consider using initialize_with_sse() instead"
187
188
  )
188
189
  params = await load_config(config_file, server_name)
@@ -195,7 +196,7 @@ class StreamManager:
195
196
  sse_url = "http://localhost:8000"
196
197
  api_key = None
197
198
  headers = {}
198
- logger.warning("No URL configured for SSE transport, using default: %s", sse_url)
199
+ logger.debug("No URL configured for SSE transport, using default: %s", sse_url)
199
200
 
200
201
  # Build SSE transport with optional headers
201
202
  transport_params = {"url": sse_url, "api_key": api_key, "default_timeout": default_timeout}
@@ -205,7 +206,7 @@ class StreamManager:
205
206
  transport = SSETransport(**transport_params)
206
207
 
207
208
  elif transport_type == "http_streamable":
208
- logger.warning(
209
+ logger.debug(
209
210
  "Using HTTP Streamable transport in initialize() - consider using initialize_with_http_streamable() instead"
210
211
  )
211
212
  params = await load_config(config_file, server_name)
@@ -220,9 +221,7 @@ class StreamManager:
220
221
  api_key = None
221
222
  headers = {}
222
223
  session_id = None
223
- logger.warning(
224
- "No URL configured for HTTP Streamable transport, using default: %s", http_url
225
- )
224
+ logger.debug("No URL configured for HTTP Streamable transport, using default: %s", http_url)
226
225
 
227
226
  # Build HTTP transport (headers not supported yet)
228
227
  transport_params = {
@@ -244,7 +243,7 @@ class StreamManager:
244
243
  # Initialize with timeout protection
245
244
  try:
246
245
  if not await asyncio.wait_for(transport.initialize(), timeout=initialization_timeout):
247
- logger.error("Failed to init %s", server_name)
246
+ logger.warning("Failed to init %s", server_name)
248
247
  continue
249
248
  except TimeoutError:
250
249
  logger.error("Timeout initialising %s (timeout=%ss)", server_name, initialization_timeout)
@@ -253,8 +252,12 @@ class StreamManager:
253
252
  self.transports[server_name] = transport
254
253
 
255
254
  # Ping and get tools with timeout protection (use longer timeouts for slow servers)
256
- status = "Up" if await asyncio.wait_for(transport.send_ping(), timeout=30.0) else "Down"
257
- tools = await asyncio.wait_for(transport.get_tools(), timeout=30.0)
255
+ status = (
256
+ "Up"
257
+ if await asyncio.wait_for(transport.send_ping(), timeout=self.timeout_config.operation)
258
+ else "Down"
259
+ )
260
+ tools = await asyncio.wait_for(transport.get_tools(), timeout=self.timeout_config.operation)
258
261
 
259
262
  for t in tools:
260
263
  name = t.get("name")
@@ -327,7 +330,7 @@ class StreamManager:
327
330
 
328
331
  try:
329
332
  if not await asyncio.wait_for(transport.initialize(), timeout=initialization_timeout):
330
- logger.error("Failed to init SSE %s", name)
333
+ logger.warning("Failed to init SSE %s", name)
331
334
  continue
332
335
  except TimeoutError:
333
336
  logger.error("Timeout initialising SSE %s (timeout=%ss)", name, initialization_timeout)
@@ -335,8 +338,12 @@ class StreamManager:
335
338
 
336
339
  self.transports[name] = transport
337
340
  # Use longer timeouts for slow servers (ping can take time after initialization)
338
- status = "Up" if await asyncio.wait_for(transport.send_ping(), timeout=30.0) else "Down"
339
- tools = await asyncio.wait_for(transport.get_tools(), timeout=30.0)
341
+ status = (
342
+ "Up"
343
+ if await asyncio.wait_for(transport.send_ping(), timeout=self.timeout_config.operation)
344
+ else "Down"
345
+ )
346
+ tools = await asyncio.wait_for(transport.get_tools(), timeout=self.timeout_config.operation)
340
347
 
341
348
  for t in tools:
342
349
  tname = t.get("name")
@@ -406,7 +413,7 @@ class StreamManager:
406
413
  logger.debug(f"Calling transport.initialize() for {name} with timeout={initialization_timeout}s")
407
414
  try:
408
415
  if not await asyncio.wait_for(transport.initialize(), timeout=initialization_timeout):
409
- logger.error("Failed to init HTTP Streamable %s", name)
416
+ logger.warning("Failed to init HTTP Streamable %s", name)
410
417
  continue
411
418
  except TimeoutError:
412
419
  logger.error(
@@ -417,8 +424,12 @@ class StreamManager:
417
424
 
418
425
  self.transports[name] = transport
419
426
  # Use longer timeouts for slow servers (ping can take time after initialization)
420
- status = "Up" if await asyncio.wait_for(transport.send_ping(), timeout=30.0) else "Down"
421
- tools = await asyncio.wait_for(transport.get_tools(), timeout=30.0)
427
+ status = (
428
+ "Up"
429
+ if await asyncio.wait_for(transport.send_ping(), timeout=self.timeout_config.operation)
430
+ else "Down"
431
+ )
432
+ tools = await asyncio.wait_for(transport.get_tools(), timeout=self.timeout_config.operation)
422
433
 
423
434
  for t in tools:
424
435
  tname = t.get("name")
@@ -464,7 +475,7 @@ class StreamManager:
464
475
  transport = self.transports[server_name]
465
476
 
466
477
  try:
467
- tools = await asyncio.wait_for(transport.get_tools(), timeout=10.0)
478
+ tools = await asyncio.wait_for(transport.get_tools(), timeout=self.timeout_config.operation)
468
479
  logger.debug("Found %d tools for server %s", len(tools), server_name)
469
480
  return tools
470
481
  except TimeoutError:
@@ -483,7 +494,7 @@ class StreamManager:
483
494
 
484
495
  async def _ping_one(name: str, tr: MCPBaseTransport):
485
496
  try:
486
- ok = await asyncio.wait_for(tr.send_ping(), timeout=5.0)
497
+ ok = await asyncio.wait_for(tr.send_ping(), timeout=self.timeout_config.quick)
487
498
  except Exception:
488
499
  ok = False
489
500
  return {"server": name, "ok": ok}
@@ -498,7 +509,7 @@ class StreamManager:
498
509
 
499
510
  async def _one(name: str, tr: MCPBaseTransport):
500
511
  try:
501
- res = await asyncio.wait_for(tr.list_resources(), timeout=10.0)
512
+ res = await asyncio.wait_for(tr.list_resources(), timeout=self.timeout_config.operation)
502
513
  resources = res.get("resources", []) if isinstance(res, dict) else res
503
514
  for item in resources:
504
515
  item = dict(item)
@@ -518,7 +529,7 @@ class StreamManager:
518
529
 
519
530
  async def _one(name: str, tr: MCPBaseTransport):
520
531
  try:
521
- res = await asyncio.wait_for(tr.list_prompts(), timeout=10.0)
532
+ res = await asyncio.wait_for(tr.list_prompts(), timeout=self.timeout_config.operation)
522
533
  prompts = res.get("prompts", []) if isinstance(res, dict) else res
523
534
  for item in prompts:
524
535
  item = dict(item)
@@ -645,7 +656,7 @@ class StreamManager:
645
656
  try:
646
657
  results = await asyncio.wait_for(
647
658
  asyncio.gather(*[task for _, task in close_tasks], return_exceptions=True),
648
- timeout=self._shutdown_timeout,
659
+ timeout=self.timeout_config.shutdown,
649
660
  )
650
661
 
651
662
  # Process results
@@ -668,7 +679,8 @@ class StreamManager:
668
679
  # Brief wait for cancellations to complete
669
680
  with contextlib.suppress(TimeoutError):
670
681
  await asyncio.wait_for(
671
- asyncio.gather(*[task for _, task in close_tasks], return_exceptions=True), timeout=0.5
682
+ asyncio.gather(*[task for _, task in close_tasks], return_exceptions=True),
683
+ timeout=self.timeout_config.shutdown,
672
684
  )
673
685
 
674
686
  async def _sequential_close(self, transport_items: list[tuple[str, MCPBaseTransport]], close_results: list) -> None:
@@ -677,7 +689,7 @@ class StreamManager:
677
689
  try:
678
690
  await asyncio.wait_for(
679
691
  self._close_single_transport(name, transport),
680
- timeout=0.5, # Short timeout per transport
692
+ timeout=self.timeout_config.shutdown,
681
693
  )
682
694
  logger.debug("Closed transport: %s", name)
683
695
  close_results.append((name, True, None))
@@ -769,7 +781,7 @@ class StreamManager:
769
781
 
770
782
  for name, transport in self.transports.items():
771
783
  try:
772
- ping_ok = await asyncio.wait_for(transport.send_ping(), timeout=5.0)
784
+ ping_ok = await asyncio.wait_for(transport.send_ping(), timeout=self.timeout_config.quick)
773
785
  health_info["transports"][name] = {
774
786
  "status": "healthy" if ping_ok else "unhealthy",
775
787
  "ping_success": ping_ok,
@@ -11,6 +11,12 @@ All transports now follow the same interface and provide consistent behavior:
11
11
 
12
12
  from .base_transport import MCPBaseTransport
13
13
  from .http_streamable_transport import HTTPStreamableTransport
14
+ from .models import (
15
+ HeadersConfig,
16
+ ServerInfo,
17
+ TimeoutConfig,
18
+ TransportMetrics,
19
+ )
14
20
  from .sse_transport import SSETransport
15
21
  from .stdio_transport import StdioTransport
16
22
 
@@ -19,4 +25,8 @@ __all__ = [
19
25
  "StdioTransport",
20
26
  "SSETransport",
21
27
  "HTTPStreamableTransport",
28
+ "TimeoutConfig",
29
+ "TransportMetrics",
30
+ "ServerInfo",
31
+ "HeadersConfig",
22
32
  ]