chuk-tool-processor 0.6.28__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.
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/PKG-INFO +74 -1
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/README.md +73 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/pyproject.toml +1 -1
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/mcp_tool.py +5 -5
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/stream_manager.py +30 -16
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/transport/__init__.py +10 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/transport/http_streamable_transport.py +90 -86
- chuk_tool_processor-0.6.29/src/chuk_tool_processor/mcp/transport/models.py +100 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/transport/sse_transport.py +56 -56
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/transport/stdio_transport.py +2 -2
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor.egg-info/PKG-INFO +74 -1
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor.egg-info/SOURCES.txt +1 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/setup.cfg +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/core/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/core/exceptions.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/core/processor.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/strategies/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/strategies/inprocess_strategy.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/strategies/subprocess_strategy.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/tool_executor.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/wrappers/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/wrappers/caching.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/wrappers/rate_limiting.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/execution/wrappers/retry.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/logging/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/logging/context.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/logging/formatter.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/logging/helpers.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/logging/metrics.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/register_mcp_tools.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/setup_mcp_http_streamable.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/setup_mcp_sse.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/setup_mcp_stdio.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/transport/base_transport.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/execution_strategy.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/streaming_tool.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/tool_call.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/tool_export_mixin.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/tool_result.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/models/validated_tool.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/discovery.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/base.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/function_call_tool.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/json_tool.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/openai_tool.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/plugins/parsers/xml_tool.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/auto_register.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/decorators.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/interface.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/metadata.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/provider.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/providers/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/providers/memory.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/registry/tool_export.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/utils/__init__.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/utils/validation.py +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor.egg-info/dependency_links.txt +0 -0
- {chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor.egg-info/requires.txt +0 -0
- {chuk_tool_processor-0.6.28 → 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.
|
|
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.
|
|
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"
|
{chuk_tool_processor-0.6.28 → chuk_tool_processor-0.6.29}/src/chuk_tool_processor/mcp/mcp_tool.py
RENAMED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
50
|
+
self.timeout_config = timeout_config or TimeoutConfig()
|
|
50
51
|
|
|
51
52
|
# ------------------------------------------------------------------ #
|
|
52
53
|
# factory helpers with enhanced error handling #
|
|
@@ -251,8 +252,12 @@ class StreamManager:
|
|
|
251
252
|
self.transports[server_name] = transport
|
|
252
253
|
|
|
253
254
|
# Ping and get tools with timeout protection (use longer timeouts for slow servers)
|
|
254
|
-
status =
|
|
255
|
-
|
|
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)
|
|
256
261
|
|
|
257
262
|
for t in tools:
|
|
258
263
|
name = t.get("name")
|
|
@@ -333,8 +338,12 @@ class StreamManager:
|
|
|
333
338
|
|
|
334
339
|
self.transports[name] = transport
|
|
335
340
|
# Use longer timeouts for slow servers (ping can take time after initialization)
|
|
336
|
-
status =
|
|
337
|
-
|
|
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)
|
|
338
347
|
|
|
339
348
|
for t in tools:
|
|
340
349
|
tname = t.get("name")
|
|
@@ -415,8 +424,12 @@ class StreamManager:
|
|
|
415
424
|
|
|
416
425
|
self.transports[name] = transport
|
|
417
426
|
# Use longer timeouts for slow servers (ping can take time after initialization)
|
|
418
|
-
status =
|
|
419
|
-
|
|
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)
|
|
420
433
|
|
|
421
434
|
for t in tools:
|
|
422
435
|
tname = t.get("name")
|
|
@@ -462,7 +475,7 @@ class StreamManager:
|
|
|
462
475
|
transport = self.transports[server_name]
|
|
463
476
|
|
|
464
477
|
try:
|
|
465
|
-
tools = await asyncio.wait_for(transport.get_tools(), timeout=
|
|
478
|
+
tools = await asyncio.wait_for(transport.get_tools(), timeout=self.timeout_config.operation)
|
|
466
479
|
logger.debug("Found %d tools for server %s", len(tools), server_name)
|
|
467
480
|
return tools
|
|
468
481
|
except TimeoutError:
|
|
@@ -481,7 +494,7 @@ class StreamManager:
|
|
|
481
494
|
|
|
482
495
|
async def _ping_one(name: str, tr: MCPBaseTransport):
|
|
483
496
|
try:
|
|
484
|
-
ok = await asyncio.wait_for(tr.send_ping(), timeout=
|
|
497
|
+
ok = await asyncio.wait_for(tr.send_ping(), timeout=self.timeout_config.quick)
|
|
485
498
|
except Exception:
|
|
486
499
|
ok = False
|
|
487
500
|
return {"server": name, "ok": ok}
|
|
@@ -496,7 +509,7 @@ class StreamManager:
|
|
|
496
509
|
|
|
497
510
|
async def _one(name: str, tr: MCPBaseTransport):
|
|
498
511
|
try:
|
|
499
|
-
res = await asyncio.wait_for(tr.list_resources(), timeout=
|
|
512
|
+
res = await asyncio.wait_for(tr.list_resources(), timeout=self.timeout_config.operation)
|
|
500
513
|
resources = res.get("resources", []) if isinstance(res, dict) else res
|
|
501
514
|
for item in resources:
|
|
502
515
|
item = dict(item)
|
|
@@ -516,7 +529,7 @@ class StreamManager:
|
|
|
516
529
|
|
|
517
530
|
async def _one(name: str, tr: MCPBaseTransport):
|
|
518
531
|
try:
|
|
519
|
-
res = await asyncio.wait_for(tr.list_prompts(), timeout=
|
|
532
|
+
res = await asyncio.wait_for(tr.list_prompts(), timeout=self.timeout_config.operation)
|
|
520
533
|
prompts = res.get("prompts", []) if isinstance(res, dict) else res
|
|
521
534
|
for item in prompts:
|
|
522
535
|
item = dict(item)
|
|
@@ -643,7 +656,7 @@ class StreamManager:
|
|
|
643
656
|
try:
|
|
644
657
|
results = await asyncio.wait_for(
|
|
645
658
|
asyncio.gather(*[task for _, task in close_tasks], return_exceptions=True),
|
|
646
|
-
timeout=self.
|
|
659
|
+
timeout=self.timeout_config.shutdown,
|
|
647
660
|
)
|
|
648
661
|
|
|
649
662
|
# Process results
|
|
@@ -666,7 +679,8 @@ class StreamManager:
|
|
|
666
679
|
# Brief wait for cancellations to complete
|
|
667
680
|
with contextlib.suppress(TimeoutError):
|
|
668
681
|
await asyncio.wait_for(
|
|
669
|
-
asyncio.gather(*[task for _, task in close_tasks], return_exceptions=True),
|
|
682
|
+
asyncio.gather(*[task for _, task in close_tasks], return_exceptions=True),
|
|
683
|
+
timeout=self.timeout_config.shutdown,
|
|
670
684
|
)
|
|
671
685
|
|
|
672
686
|
async def _sequential_close(self, transport_items: list[tuple[str, MCPBaseTransport]], close_results: list) -> None:
|
|
@@ -675,7 +689,7 @@ class StreamManager:
|
|
|
675
689
|
try:
|
|
676
690
|
await asyncio.wait_for(
|
|
677
691
|
self._close_single_transport(name, transport),
|
|
678
|
-
timeout=
|
|
692
|
+
timeout=self.timeout_config.shutdown,
|
|
679
693
|
)
|
|
680
694
|
logger.debug("Closed transport: %s", name)
|
|
681
695
|
close_results.append((name, True, None))
|
|
@@ -767,7 +781,7 @@ class StreamManager:
|
|
|
767
781
|
|
|
768
782
|
for name, transport in self.transports.items():
|
|
769
783
|
try:
|
|
770
|
-
ping_ok = await asyncio.wait_for(transport.send_ping(), timeout=
|
|
784
|
+
ping_ok = await asyncio.wait_for(transport.send_ping(), timeout=self.timeout_config.quick)
|
|
771
785
|
health_info["transports"][name] = {
|
|
772
786
|
"status": "healthy" if ping_ok else "unhealthy",
|
|
773
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
|
]
|