chuk-tool-processor 0.6.4__py3-none-any.whl → 0.9.7__py3-none-any.whl

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/core/__init__.py +32 -1
  2. chuk_tool_processor/core/exceptions.py +225 -13
  3. chuk_tool_processor/core/processor.py +135 -104
  4. chuk_tool_processor/execution/strategies/__init__.py +6 -0
  5. chuk_tool_processor/execution/strategies/inprocess_strategy.py +142 -150
  6. chuk_tool_processor/execution/strategies/subprocess_strategy.py +202 -206
  7. chuk_tool_processor/execution/tool_executor.py +82 -84
  8. chuk_tool_processor/execution/wrappers/__init__.py +42 -0
  9. chuk_tool_processor/execution/wrappers/caching.py +150 -116
  10. chuk_tool_processor/execution/wrappers/circuit_breaker.py +370 -0
  11. chuk_tool_processor/execution/wrappers/rate_limiting.py +76 -43
  12. chuk_tool_processor/execution/wrappers/retry.py +116 -78
  13. chuk_tool_processor/logging/__init__.py +23 -17
  14. chuk_tool_processor/logging/context.py +40 -45
  15. chuk_tool_processor/logging/formatter.py +22 -21
  16. chuk_tool_processor/logging/helpers.py +28 -42
  17. chuk_tool_processor/logging/metrics.py +13 -15
  18. chuk_tool_processor/mcp/__init__.py +8 -12
  19. chuk_tool_processor/mcp/mcp_tool.py +158 -114
  20. chuk_tool_processor/mcp/register_mcp_tools.py +22 -22
  21. chuk_tool_processor/mcp/setup_mcp_http_streamable.py +57 -17
  22. chuk_tool_processor/mcp/setup_mcp_sse.py +57 -17
  23. chuk_tool_processor/mcp/setup_mcp_stdio.py +11 -11
  24. chuk_tool_processor/mcp/stream_manager.py +333 -276
  25. chuk_tool_processor/mcp/transport/__init__.py +22 -29
  26. chuk_tool_processor/mcp/transport/base_transport.py +180 -44
  27. chuk_tool_processor/mcp/transport/http_streamable_transport.py +505 -325
  28. chuk_tool_processor/mcp/transport/models.py +100 -0
  29. chuk_tool_processor/mcp/transport/sse_transport.py +607 -276
  30. chuk_tool_processor/mcp/transport/stdio_transport.py +597 -116
  31. chuk_tool_processor/models/__init__.py +21 -1
  32. chuk_tool_processor/models/execution_strategy.py +16 -21
  33. chuk_tool_processor/models/streaming_tool.py +28 -25
  34. chuk_tool_processor/models/tool_call.py +49 -31
  35. chuk_tool_processor/models/tool_export_mixin.py +22 -8
  36. chuk_tool_processor/models/tool_result.py +40 -77
  37. chuk_tool_processor/models/tool_spec.py +350 -0
  38. chuk_tool_processor/models/validated_tool.py +36 -18
  39. chuk_tool_processor/observability/__init__.py +30 -0
  40. chuk_tool_processor/observability/metrics.py +312 -0
  41. chuk_tool_processor/observability/setup.py +105 -0
  42. chuk_tool_processor/observability/tracing.py +345 -0
  43. chuk_tool_processor/plugins/__init__.py +1 -1
  44. chuk_tool_processor/plugins/discovery.py +11 -11
  45. chuk_tool_processor/plugins/parsers/__init__.py +1 -1
  46. chuk_tool_processor/plugins/parsers/base.py +1 -2
  47. chuk_tool_processor/plugins/parsers/function_call_tool.py +13 -8
  48. chuk_tool_processor/plugins/parsers/json_tool.py +4 -3
  49. chuk_tool_processor/plugins/parsers/openai_tool.py +12 -7
  50. chuk_tool_processor/plugins/parsers/xml_tool.py +4 -4
  51. chuk_tool_processor/registry/__init__.py +12 -12
  52. chuk_tool_processor/registry/auto_register.py +22 -30
  53. chuk_tool_processor/registry/decorators.py +127 -129
  54. chuk_tool_processor/registry/interface.py +26 -23
  55. chuk_tool_processor/registry/metadata.py +27 -22
  56. chuk_tool_processor/registry/provider.py +17 -18
  57. chuk_tool_processor/registry/providers/__init__.py +16 -19
  58. chuk_tool_processor/registry/providers/memory.py +18 -25
  59. chuk_tool_processor/registry/tool_export.py +42 -51
  60. chuk_tool_processor/utils/validation.py +15 -16
  61. chuk_tool_processor-0.9.7.dist-info/METADATA +1813 -0
  62. chuk_tool_processor-0.9.7.dist-info/RECORD +67 -0
  63. chuk_tool_processor-0.6.4.dist-info/METADATA +0 -697
  64. chuk_tool_processor-0.6.4.dist-info/RECORD +0 -60
  65. {chuk_tool_processor-0.6.4.dist-info → chuk_tool_processor-0.9.7.dist-info}/WHEEL +0 -0
  66. {chuk_tool_processor-0.6.4.dist-info → chuk_tool_processor-0.9.7.dist-info}/top_level.txt +0 -0
@@ -17,8 +17,6 @@ It:
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- from typing import Dict, List, Optional, Tuple
21
-
22
20
  from chuk_tool_processor.core.processor import ToolProcessor
23
21
  from chuk_tool_processor.logging import get_logger
24
22
  from chuk_tool_processor.mcp.register_mcp_tools import register_mcp_tools
@@ -32,34 +30,37 @@ logger = get_logger("chuk_tool_processor.mcp.setup_http_streamable")
32
30
  # --------------------------------------------------------------------------- #
33
31
  async def setup_mcp_http_streamable(
34
32
  *,
35
- servers: List[Dict[str, str]],
36
- server_names: Optional[Dict[int, str]] = None,
33
+ servers: list[dict[str, str]],
34
+ server_names: dict[int, str] | None = None,
37
35
  connection_timeout: float = 30.0,
38
36
  default_timeout: float = 30.0,
39
- max_concurrency: Optional[int] = None,
37
+ initialization_timeout: float = 60.0,
38
+ max_concurrency: int | None = None,
40
39
  enable_caching: bool = True,
41
40
  cache_ttl: int = 300,
42
41
  enable_rate_limiting: bool = False,
43
- global_rate_limit: Optional[int] = None,
44
- tool_rate_limits: Optional[Dict[str, tuple]] = None,
45
- enable_retries: bool = True,
46
- max_retries: int = 3,
42
+ global_rate_limit: int | None = None,
43
+ tool_rate_limits: dict[str, tuple] | None = None,
44
+ enable_retries: bool = True, # CHANGED: Enabled with OAuth errors excluded
45
+ max_retries: int = 2, # Retry non-OAuth errors (OAuth handled at transport level)
47
46
  namespace: str = "http",
48
- ) -> Tuple[ToolProcessor, StreamManager]:
47
+ oauth_refresh_callback: any | None = None, # NEW: OAuth token refresh callback
48
+ ) -> tuple[ToolProcessor, StreamManager]:
49
49
  """
50
50
  Initialize HTTP Streamable transport MCP + a :class:`ToolProcessor`.
51
51
 
52
52
  This uses the modern HTTP Streamable transport (spec 2025-03-26) which
53
53
  provides better infrastructure compatibility and more flexible response
54
54
  handling compared to the deprecated SSE transport.
55
-
55
+
56
56
  Call with ``await`` from your async context.
57
-
57
+
58
58
  Args:
59
59
  servers: List of server configurations with 'name', 'url', and optionally 'api_key' keys
60
60
  server_names: Optional mapping of server indices to names
61
61
  connection_timeout: Timeout for initial HTTP connection setup
62
62
  default_timeout: Default timeout for tool execution
63
+ initialization_timeout: Timeout for complete initialization (default 60s, increase to 120s+ for slow servers like Notion)
63
64
  max_concurrency: Maximum concurrent operations
64
65
  enable_caching: Whether to enable response caching
65
66
  cache_ttl: Cache time-to-live in seconds
@@ -69,10 +70,11 @@ async def setup_mcp_http_streamable(
69
70
  enable_retries: Whether to enable automatic retries
70
71
  max_retries: Maximum retry attempts
71
72
  namespace: Namespace for registered tools
72
-
73
+ oauth_refresh_callback: Optional async callback to refresh OAuth tokens (NEW)
74
+
73
75
  Returns:
74
76
  Tuple of (ToolProcessor, StreamManager)
75
-
77
+
76
78
  Example:
77
79
  >>> servers = [
78
80
  ... {
@@ -92,12 +94,49 @@ async def setup_mcp_http_streamable(
92
94
  server_names=server_names,
93
95
  connection_timeout=connection_timeout,
94
96
  default_timeout=default_timeout,
97
+ initialization_timeout=initialization_timeout,
98
+ oauth_refresh_callback=oauth_refresh_callback, # NEW: Pass OAuth callback
95
99
  )
96
100
 
97
101
  # 2️⃣ pull the remote tool list and register each one locally
98
102
  registered = await register_mcp_tools(stream_manager, namespace=namespace)
99
103
 
100
104
  # 3️⃣ build a processor instance configured to your taste
105
+ # IMPORTANT: Retries are enabled but OAuth errors are excluded
106
+ # OAuth refresh happens at transport level with automatic retry
107
+
108
+ # Import RetryConfig to configure OAuth error exclusion
109
+ from chuk_tool_processor.execution.wrappers.retry import RetryConfig
110
+
111
+ # Define OAuth error patterns that should NOT be retried at this level
112
+ # These will be handled by the transport layer's OAuth refresh mechanism
113
+ # Based on RFC 6750 (Bearer Token Usage) and MCP OAuth spec
114
+ oauth_error_patterns = [
115
+ # RFC 6750 Section 3.1 - Standard Bearer token errors
116
+ "invalid_token", # Token expired, revoked, malformed, or invalid
117
+ "insufficient_scope", # Request requires higher privileges (403 Forbidden)
118
+ # OAuth 2.1 token refresh errors
119
+ "invalid_grant", # Refresh token errors
120
+ # MCP spec - OAuth validation failures (401 Unauthorized)
121
+ "oauth validation",
122
+ "unauthorized",
123
+ # Common OAuth error descriptions
124
+ "expired token",
125
+ "token expired",
126
+ "authentication failed",
127
+ "invalid access token",
128
+ ]
129
+
130
+ # Create retry config that skips OAuth errors
131
+ retry_config = (
132
+ RetryConfig(
133
+ max_retries=max_retries,
134
+ skip_retry_on_error_substrings=oauth_error_patterns,
135
+ )
136
+ if enable_retries
137
+ else None
138
+ )
139
+
101
140
  processor = ToolProcessor(
102
141
  default_timeout=default_timeout,
103
142
  max_concurrency=max_concurrency,
@@ -108,12 +147,13 @@ async def setup_mcp_http_streamable(
108
147
  tool_rate_limits=tool_rate_limits,
109
148
  enable_retries=enable_retries,
110
149
  max_retries=max_retries,
150
+ retry_config=retry_config, # NEW: Pass OAuth-aware retry config
111
151
  )
112
152
 
113
- logger.info(
114
- "MCP (HTTP Streamable) initialised - %s tool%s registered into namespace '%s'",
153
+ logger.debug(
154
+ "MCP (HTTP Streamable) initialised - %d tool%s registered into namespace '%s'",
115
155
  len(registered),
116
156
  "" if len(registered) == 1 else "s",
117
157
  namespace,
118
158
  )
119
- return processor, stream_manager
159
+ return processor, stream_manager
@@ -13,8 +13,6 @@ It:
13
13
 
14
14
  from __future__ import annotations
15
15
 
16
- from typing import Dict, List, Optional, Tuple
17
-
18
16
  from chuk_tool_processor.core.processor import ToolProcessor
19
17
  from chuk_tool_processor.logging import get_logger
20
18
  from chuk_tool_processor.mcp.register_mcp_tools import register_mcp_tools
@@ -28,30 +26,33 @@ logger = get_logger("chuk_tool_processor.mcp.setup_sse")
28
26
  # --------------------------------------------------------------------------- #
29
27
  async def setup_mcp_sse( # noqa: C901 - long but just a config facade
30
28
  *,
31
- servers: List[Dict[str, str]],
32
- server_names: Optional[Dict[int, str]] = None,
29
+ servers: list[dict[str, str]],
30
+ server_names: dict[int, str] | None = None,
33
31
  connection_timeout: float = 30.0, # 🔧 INCREASED DEFAULT: was 10.0
34
- default_timeout: float = 30.0, # 🔧 INCREASED DEFAULT: was 10.0
35
- max_concurrency: Optional[int] = None,
32
+ default_timeout: float = 30.0, # 🔧 INCREASED DEFAULT: was 10.0
33
+ initialization_timeout: float = 60.0,
34
+ max_concurrency: int | None = None,
36
35
  enable_caching: bool = True,
37
36
  cache_ttl: int = 300,
38
37
  enable_rate_limiting: bool = False,
39
- global_rate_limit: Optional[int] = None,
40
- tool_rate_limits: Optional[Dict[str, tuple]] = None,
41
- enable_retries: bool = True,
42
- max_retries: int = 3,
38
+ global_rate_limit: int | None = None,
39
+ tool_rate_limits: dict[str, tuple] | None = None,
40
+ enable_retries: bool = True, # CHANGED: Enabled with OAuth errors excluded
41
+ max_retries: int = 2, # Retry non-OAuth errors (OAuth handled at transport level)
43
42
  namespace: str = "sse",
44
- ) -> Tuple[ToolProcessor, StreamManager]:
43
+ oauth_refresh_callback: any | None = None, # NEW: OAuth token refresh callback
44
+ ) -> tuple[ToolProcessor, StreamManager]:
45
45
  """
46
46
  Initialise SSE-transport MCP + a :class:`ToolProcessor`.
47
47
 
48
48
  Call with ``await`` from your async context.
49
-
49
+
50
50
  Args:
51
51
  servers: List of server configurations with 'name' and 'url' keys
52
52
  server_names: Optional mapping of server indices to names
53
53
  connection_timeout: Timeout for initial SSE connection setup
54
54
  default_timeout: Default timeout for tool execution
55
+ initialization_timeout: Timeout for complete initialization (default 60s, increase for slow servers)
55
56
  max_concurrency: Maximum concurrent operations
56
57
  enable_caching: Whether to enable response caching
57
58
  cache_ttl: Cache time-to-live in seconds
@@ -61,7 +62,8 @@ async def setup_mcp_sse( # noqa: C901 - long but just a config facade
61
62
  enable_retries: Whether to enable automatic retries
62
63
  max_retries: Maximum retry attempts
63
64
  namespace: Namespace for registered tools
64
-
65
+ oauth_refresh_callback: Optional async callback to refresh OAuth tokens (NEW)
66
+
65
67
  Returns:
66
68
  Tuple of (ToolProcessor, StreamManager)
67
69
  """
@@ -70,13 +72,50 @@ async def setup_mcp_sse( # noqa: C901 - long but just a config facade
70
72
  servers=servers,
71
73
  server_names=server_names,
72
74
  connection_timeout=connection_timeout, # 🔧 ADD THIS LINE
73
- default_timeout=default_timeout, # 🔧 ADD THIS LINE
75
+ default_timeout=default_timeout, # 🔧 ADD THIS LINE
76
+ initialization_timeout=initialization_timeout,
77
+ oauth_refresh_callback=oauth_refresh_callback, # NEW: Pass OAuth callback
74
78
  )
75
79
 
76
80
  # 2️⃣ pull the remote tool list and register each one locally
77
81
  registered = await register_mcp_tools(stream_manager, namespace=namespace)
78
82
 
79
83
  # 3️⃣ build a processor instance configured to your taste
84
+ # IMPORTANT: Retries are enabled but OAuth errors are excluded
85
+ # OAuth refresh happens at transport level with automatic retry
86
+
87
+ # Import RetryConfig to configure OAuth error exclusion
88
+ from chuk_tool_processor.execution.wrappers.retry import RetryConfig
89
+
90
+ # Define OAuth error patterns that should NOT be retried at this level
91
+ # These will be handled by the transport layer's OAuth refresh mechanism
92
+ # Based on RFC 6750 (Bearer Token Usage) and MCP OAuth spec
93
+ oauth_error_patterns = [
94
+ # RFC 6750 Section 3.1 - Standard Bearer token errors
95
+ "invalid_token", # Token expired, revoked, malformed, or invalid
96
+ "insufficient_scope", # Request requires higher privileges (403 Forbidden)
97
+ # OAuth 2.1 token refresh errors
98
+ "invalid_grant", # Refresh token errors
99
+ # MCP spec - OAuth validation failures (401 Unauthorized)
100
+ "oauth validation",
101
+ "unauthorized",
102
+ # Common OAuth error descriptions
103
+ "expired token",
104
+ "token expired",
105
+ "authentication failed",
106
+ "invalid access token",
107
+ ]
108
+
109
+ # Create retry config that skips OAuth errors
110
+ retry_config = (
111
+ RetryConfig(
112
+ max_retries=max_retries,
113
+ skip_retry_on_error_substrings=oauth_error_patterns,
114
+ )
115
+ if enable_retries
116
+ else None
117
+ )
118
+
80
119
  processor = ToolProcessor(
81
120
  default_timeout=default_timeout,
82
121
  max_concurrency=max_concurrency,
@@ -87,12 +126,13 @@ async def setup_mcp_sse( # noqa: C901 - long but just a config facade
87
126
  tool_rate_limits=tool_rate_limits,
88
127
  enable_retries=enable_retries,
89
128
  max_retries=max_retries,
129
+ retry_config=retry_config, # NEW: Pass OAuth-aware retry config
90
130
  )
91
131
 
92
- logger.info(
93
- "MCP (SSE) initialised - %s tool%s registered into namespace '%s'",
132
+ logger.debug(
133
+ "MCP (SSE) initialised - %d tool%s registered into namespace '%s'",
94
134
  len(registered),
95
135
  "" if len(registered) == 1 else "s",
96
136
  namespace,
97
137
  )
98
- return processor, stream_manager
138
+ return processor, stream_manager
@@ -13,8 +13,6 @@ It:
13
13
 
14
14
  from __future__ import annotations
15
15
 
16
- from typing import Dict, List, Optional, Tuple
17
-
18
16
  from chuk_tool_processor.core.processor import ToolProcessor
19
17
  from chuk_tool_processor.logging import get_logger
20
18
  from chuk_tool_processor.mcp.register_mcp_tools import register_mcp_tools
@@ -29,19 +27,20 @@ logger = get_logger("chuk_tool_processor.mcp.setup_stdio")
29
27
  async def setup_mcp_stdio( # noqa: C901 - long but just a config facade
30
28
  *,
31
29
  config_file: str,
32
- servers: List[str],
33
- server_names: Optional[Dict[int, str]] = None,
30
+ servers: list[str],
31
+ server_names: dict[int, str] | None = None,
34
32
  default_timeout: float = 10.0,
35
- max_concurrency: Optional[int] = None,
33
+ initialization_timeout: float = 60.0,
34
+ max_concurrency: int | None = None,
36
35
  enable_caching: bool = True,
37
36
  cache_ttl: int = 300,
38
37
  enable_rate_limiting: bool = False,
39
- global_rate_limit: Optional[int] = None,
40
- tool_rate_limits: Optional[Dict[str, tuple]] = None,
38
+ global_rate_limit: int | None = None,
39
+ tool_rate_limits: dict[str, tuple] | None = None,
41
40
  enable_retries: bool = True,
42
41
  max_retries: int = 3,
43
42
  namespace: str = "mcp",
44
- ) -> Tuple[ToolProcessor, StreamManager]:
43
+ ) -> tuple[ToolProcessor, StreamManager]:
45
44
  """
46
45
  Initialise stdio-transport MCP + a :class:`ToolProcessor`.
47
46
 
@@ -55,6 +54,7 @@ async def setup_mcp_stdio( # noqa: C901 - long but just a config facade
55
54
  server_names=server_names,
56
55
  transport_type="stdio",
57
56
  default_timeout=default_timeout, # 🔧 ADD THIS LINE
57
+ initialization_timeout=initialization_timeout,
58
58
  )
59
59
 
60
60
  # 2️⃣ pull the remote tool list and register each one locally
@@ -73,10 +73,10 @@ async def setup_mcp_stdio( # noqa: C901 - long but just a config facade
73
73
  max_retries=max_retries,
74
74
  )
75
75
 
76
- logger.info(
77
- "MCP (stdio) initialised - %s tool%s registered into namespace '%s'",
76
+ logger.debug(
77
+ "MCP (stdio) initialised - %d tool%s registered into namespace '%s'",
78
78
  len(registered),
79
79
  "" if len(registered) == 1 else "s",
80
80
  namespace,
81
81
  )
82
- return processor, stream_manager
82
+ return processor, stream_manager