chuk-tool-processor 0.2__py3-none-any.whl → 0.3__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.

@@ -36,9 +36,11 @@ class MCPTool:
36
36
  servers: Optional[List[str]] = None,
37
37
  server_names: Optional[Dict[int, str]] = None,
38
38
  namespace: str = "stdio",
39
+ default_timeout: Optional[float] = None, # Add default timeout support
39
40
  ) -> None:
40
41
  self.tool_name = tool_name
41
42
  self._sm: Optional[StreamManager] = stream_manager
43
+ self.default_timeout = default_timeout or 30.0 # Default to 30s if not specified
42
44
 
43
45
  # Boot-strap parameters (only needed if _sm is None)
44
46
  self._cfg_file = cfg_file
@@ -78,21 +80,56 @@ class MCPTool:
78
80
  return self._sm # type: ignore[return-value]
79
81
 
80
82
  # ------------------------------------------------------------------ #
81
- async def execute(self, **kwargs: Any) -> Any:
83
+ async def execute(self, timeout: Optional[float] = None, **kwargs: Any) -> Any:
82
84
  """
83
- Forward the call to the remote MCP tool.
85
+ Forward the call to the remote MCP tool with timeout support.
86
+
87
+ Args:
88
+ timeout: Optional timeout for this specific call. If not provided,
89
+ uses the instance's default_timeout.
90
+ **kwargs: Arguments to pass to the MCP tool.
91
+
92
+ Returns:
93
+ The result from the MCP tool call.
84
94
 
85
95
  Raises
86
96
  ------
87
97
  RuntimeError
88
98
  If the server returns an error payload.
99
+ asyncio.TimeoutError
100
+ If the call times out.
89
101
  """
90
102
  sm = await self._ensure_stream_manager()
91
- result = await sm.call_tool(tool_name=self.tool_name, arguments=kwargs)
103
+
104
+ # Use provided timeout, fall back to instance default, then global default
105
+ effective_timeout = timeout if timeout is not None else self.default_timeout
106
+
107
+ logger.debug("Calling MCP tool '%s' with timeout: %ss", self.tool_name, effective_timeout)
108
+
109
+ try:
110
+ # Pass timeout directly to StreamManager instead of wrapping with wait_for
111
+ result = await sm.call_tool(
112
+ tool_name=self.tool_name,
113
+ arguments=kwargs,
114
+ timeout=effective_timeout
115
+ )
116
+
117
+ if result.get("isError"):
118
+ err = result.get("error", "Unknown error")
119
+ logger.error("Remote MCP error from '%s': %s", self.tool_name, err)
120
+ raise RuntimeError(err)
121
+
122
+ return result.get("content")
123
+
124
+ except asyncio.TimeoutError:
125
+ logger.warning("MCP tool '%s' timed out after %ss", self.tool_name, effective_timeout)
126
+ raise
127
+ except Exception as e:
128
+ logger.error("Error calling MCP tool '%s': %s", self.tool_name, e)
129
+ raise
92
130
 
93
- if result.get("isError"):
94
- err = result.get("error", "Unknown error")
95
- logger.error("Remote MCP error from '%s': %s", self.tool_name, err)
96
- raise RuntimeError(err)
97
-
98
- return result.get("content")
131
+ # ------------------------------------------------------------------ #
132
+ # Legacy method name support
133
+ async def _aexecute(self, timeout: Optional[float] = None, **kwargs: Any) -> Any:
134
+ """Legacy alias for execute() method."""
135
+ return await self.execute(timeout=timeout, **kwargs)
@@ -14,6 +14,7 @@ Utility that wires up:
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ import os
17
18
  from typing import Dict, List, Optional, Tuple
18
19
 
19
20
  from chuk_tool_processor.core.processor import ToolProcessor
@@ -47,7 +48,26 @@ async def setup_mcp_sse( # noqa: C901 – long, but just a config wrapper
47
48
  and return a ready-to-go :class:`ToolProcessor`.
48
49
 
49
50
  Everything is **async-native** – call with ``await``.
51
+
52
+ NEW: Automatically detects and adds bearer token from MCP_BEARER_TOKEN
53
+ environment variable if not explicitly provided in server config.
50
54
  """
55
+
56
+ # NEW: Auto-detect and add bearer token to servers if available
57
+ bearer_token = os.getenv("MCP_BEARER_TOKEN")
58
+ if bearer_token:
59
+ logger.info("Found MCP_BEARER_TOKEN environment variable, adding to server configs")
60
+
61
+ # Add api_key to servers that don't already have it
62
+ enhanced_servers = []
63
+ for server in servers:
64
+ enhanced_server = dict(server) # Make a copy
65
+ if "api_key" not in enhanced_server and bearer_token:
66
+ enhanced_server["api_key"] = bearer_token
67
+ logger.info("Added bearer token to server: %s", enhanced_server.get("name", "unnamed"))
68
+ enhanced_servers.append(enhanced_server)
69
+ servers = enhanced_servers
70
+
51
71
  # 1️⃣ connect to the remote MCP servers
52
72
  stream_manager = await StreamManager.create_with_sse(
53
73
  servers=servers,
@@ -76,4 +96,4 @@ async def setup_mcp_sse( # noqa: C901 – long, but just a config wrapper
76
96
  "" if len(registered) == 1 else "s",
77
97
  namespace,
78
98
  )
79
- return processor, stream_manager
99
+ return processor, stream_manager
@@ -265,7 +265,20 @@ class StreamManager:
265
265
  tool_name: str,
266
266
  arguments: Dict[str, Any],
267
267
  server_name: Optional[str] = None,
268
+ timeout: Optional[float] = None, # Add timeout parameter
268
269
  ) -> Dict[str, Any]:
270
+ """
271
+ Call a tool on the appropriate server with timeout support.
272
+
273
+ Args:
274
+ tool_name: Name of the tool to call
275
+ arguments: Arguments to pass to the tool
276
+ server_name: Optional server name (auto-detected if not provided)
277
+ timeout: Optional timeout for the call
278
+
279
+ Returns:
280
+ Dictionary containing the tool result or error
281
+ """
269
282
  server_name = server_name or self.get_server_for_tool(tool_name)
270
283
  if not server_name or server_name not in self.transports:
271
284
  # wording kept exactly for unit-test expectation
@@ -273,8 +286,27 @@ class StreamManager:
273
286
  "isError": True,
274
287
  "error": f"No server found for tool: {tool_name}",
275
288
  }
276
- return await self.transports[server_name].call_tool(tool_name, arguments)
277
-
289
+
290
+ transport = self.transports[server_name]
291
+
292
+ # Apply timeout if specified
293
+ if timeout is not None:
294
+ logger.debug("Calling tool '%s' with %ss timeout", tool_name, timeout)
295
+ try:
296
+ return await asyncio.wait_for(
297
+ transport.call_tool(tool_name, arguments),
298
+ timeout=timeout
299
+ )
300
+ except asyncio.TimeoutError:
301
+ logger.warning("Tool '%s' timed out after %ss", tool_name, timeout)
302
+ return {
303
+ "isError": True,
304
+ "error": f"Tool call timed out after {timeout}s",
305
+ }
306
+ else:
307
+ # No timeout specified, call directly
308
+ return await transport.call_tool(tool_name, arguments)
309
+
278
310
  # ------------------------------------------------------------------ #
279
311
  # shutdown #
280
312
  # ------------------------------------------------------------------ #
@@ -14,6 +14,7 @@ from __future__ import annotations
14
14
  import asyncio
15
15
  import contextlib
16
16
  import json
17
+ import os
17
18
  from typing import Any, Dict, List, Optional
18
19
 
19
20
  import httpx
@@ -49,6 +50,13 @@ class SSETransport(MCPBaseTransport):
49
50
  def __init__(self, url: str, api_key: Optional[str] = None) -> None:
50
51
  self.base_url = url.rstrip("/")
51
52
  self.api_key = api_key
53
+
54
+ # NEW: Auto-detect bearer token from environment if not provided
55
+ if not self.api_key:
56
+ bearer_token = os.getenv("MCP_BEARER_TOKEN")
57
+ if bearer_token:
58
+ self.api_key = bearer_token
59
+ print(f"🔑 Using bearer token from MCP_BEARER_TOKEN environment variable")
52
60
 
53
61
  # httpx client (None until initialise)
54
62
  self._client: httpx.AsyncClient | None = None
@@ -75,7 +83,12 @@ class SSETransport(MCPBaseTransport):
75
83
 
76
84
  headers = {}
77
85
  if self.api_key:
78
- headers["authorization"] = self.api_key
86
+ # NEW: Handle both "Bearer token" and just "token" formats
87
+ if self.api_key.startswith("Bearer "):
88
+ headers["Authorization"] = self.api_key
89
+ else:
90
+ headers["Authorization"] = f"Bearer {self.api_key}"
91
+ print(f"🔑 Added Authorization header to httpx client")
79
92
 
80
93
  self._client = httpx.AsyncClient(
81
94
  headers=headers,
@@ -223,6 +236,11 @@ class SSETransport(MCPBaseTransport):
223
236
 
224
237
  if event_type == "endpoint":
225
238
  # Got the endpoint URL for messages - construct full URL
239
+ # NEW: Handle URLs that need trailing slash fix
240
+ if "/messages?" in data and "/messages/?" not in data:
241
+ data = data.replace("/messages?", "/messages/?", 1)
242
+ print(f"🔧 Fixed URL redirect: added trailing slash")
243
+
226
244
  self._message_url = f"{self.base_url}{data}"
227
245
 
228
246
  # Extract session_id if present
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chuk-tool-processor
3
- Version: 0.2
3
+ Version: 0.3
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -17,14 +17,14 @@ chuk_tool_processor/logging/formatter.py,sha256=RhlV6NqBYRBOtytDY49c9Y1J4l02ZjNX
17
17
  chuk_tool_processor/logging/helpers.py,sha256=c1mS1sb_rh4bKG0hisyvT7l7cirQfXPSyWeBqmqALRw,5941
18
18
  chuk_tool_processor/logging/metrics.py,sha256=s59Au8q0eqGGtJMDqmJBZhbJHh4BWGE1CzT0iI8lRS8,3624
19
19
  chuk_tool_processor/mcp/__init__.py,sha256=vR9HHxLpXlKTIIwJJRr3QTmZegcdedR1YKyb46j6FIM,689
20
- chuk_tool_processor/mcp/mcp_tool.py,sha256=BcFsW0iVXUpE0o-h3u2hYX-88EmfV80nMUoZPyZs_IY,3242
20
+ chuk_tool_processor/mcp/mcp_tool.py,sha256=a7WnBiu8DaSuZ8RI0Ums4M5A7v46RvijlqZa0020wWg,4922
21
21
  chuk_tool_processor/mcp/register_mcp_tools.py,sha256=0q73gafC1d0ei_gqNidcUeY7NUg13UZdjhVOKEFcD5o,3642
22
- chuk_tool_processor/mcp/setup_mcp_sse.py,sha256=FmXyOG9DuH2pzr1TLTFRFLlW23YkFG8rzSdpybtPCJg,2865
22
+ chuk_tool_processor/mcp/setup_mcp_sse.py,sha256=T0V27azQy06yc-RSc5uzEKyhbyAXFT-7O3pIn4k10HQ,3769
23
23
  chuk_tool_processor/mcp/setup_mcp_stdio.py,sha256=P9qSgmxoNQbsOlGp83DlLLpN9BsG__MhlRsxFplNP3M,2753
24
- chuk_tool_processor/mcp/stream_manager.py,sha256=mrmlG54P_xLbDYz_rBjdu-OPMnbi916dgyJg7BrIbjM,12798
24
+ chuk_tool_processor/mcp/stream_manager.py,sha256=cRnGiuX2A4vHLP91XxFyNKp9Qbv41ImiqMS9F3UlUoA,14030
25
25
  chuk_tool_processor/mcp/transport/__init__.py,sha256=7QQqeSKVKv0N9GcyJuYF0R4FDZeooii5RjggvFFg5GY,296
26
26
  chuk_tool_processor/mcp/transport/base_transport.py,sha256=1E29LjWw5vLQrPUDF_9TJt63P5dxAAN7n6E_KiZbGUY,3427
27
- chuk_tool_processor/mcp/transport/sse_transport.py,sha256=AkEs02ef11dLbBju6mYIZwdMF6zm0tcME_I8LEVSmrQ,16710
27
+ chuk_tool_processor/mcp/transport/sse_transport.py,sha256=RC_m1relMkr5gQCUbH1z9JB2raZHj2MIEIq9Qyfpw0Y,17696
28
28
  chuk_tool_processor/mcp/transport/stdio_transport.py,sha256=lFXL7p8ca4z_J0RBL8UCHrQ1UH7C2-LbC0tZhpya4V4,7763
29
29
  chuk_tool_processor/models/__init__.py,sha256=TC__rdVa0lQsmJHM_hbLDPRgToa_pQT_UxRcPZk6iVw,40
30
30
  chuk_tool_processor/models/execution_strategy.py,sha256=UVW35YIeMY2B3mpIKZD2rAkyOPayI6ckOOUALyf0YiQ,2115
@@ -52,7 +52,7 @@ chuk_tool_processor/registry/providers/__init__.py,sha256=eigwG_So11j7WbDGSWaKd3
52
52
  chuk_tool_processor/registry/providers/memory.py,sha256=LlpPUU9E7S8Se6Q3VyKxLwpNm82SvmP8GLUmI8MkHxQ,5188
53
53
  chuk_tool_processor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  chuk_tool_processor/utils/validation.py,sha256=fiTSsHq7zx-kyd755GaFCvPCa-EVasSpg0A1liNHkxU,4138
55
- chuk_tool_processor-0.2.dist-info/METADATA,sha256=BF2f_DLVJk59zAMransa5Ca3wH5alCzPih6xFsNkscc,10163
56
- chuk_tool_processor-0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
- chuk_tool_processor-0.2.dist-info/top_level.txt,sha256=7lTsnuRx4cOW4U2sNJWNxl4ZTt_J1ndkjTbj3pHPY5M,20
58
- chuk_tool_processor-0.2.dist-info/RECORD,,
55
+ chuk_tool_processor-0.3.dist-info/METADATA,sha256=9D_wk8oZqFKWnxdjMTcqB9K2bIa0QrBk7Ep_kxqBZZ0,10163
56
+ chuk_tool_processor-0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ chuk_tool_processor-0.3.dist-info/top_level.txt,sha256=7lTsnuRx4cOW4U2sNJWNxl4ZTt_J1ndkjTbj3pHPY5M,20
58
+ chuk_tool_processor-0.3.dist-info/RECORD,,