chuk-tool-processor 0.6.26__py3-none-any.whl → 0.6.27__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.
- chuk_tool_processor/mcp/setup_mcp_http_streamable.py +3 -0
- chuk_tool_processor/mcp/setup_mcp_sse.py +3 -0
- chuk_tool_processor/mcp/stream_manager.py +16 -0
- chuk_tool_processor/mcp/transport/http_streamable_transport.py +58 -2
- chuk_tool_processor/mcp/transport/sse_transport.py +65 -1
- {chuk_tool_processor-0.6.26.dist-info → chuk_tool_processor-0.6.27.dist-info}/METADATA +73 -1
- {chuk_tool_processor-0.6.26.dist-info → chuk_tool_processor-0.6.27.dist-info}/RECORD +9 -9
- {chuk_tool_processor-0.6.26.dist-info → chuk_tool_processor-0.6.27.dist-info}/WHEEL +0 -0
- {chuk_tool_processor-0.6.26.dist-info → chuk_tool_processor-0.6.27.dist-info}/top_level.txt +0 -0
|
@@ -44,6 +44,7 @@ async def setup_mcp_http_streamable(
|
|
|
44
44
|
enable_retries: bool = True,
|
|
45
45
|
max_retries: int = 3,
|
|
46
46
|
namespace: str = "http",
|
|
47
|
+
oauth_refresh_callback: any | None = None, # NEW: OAuth token refresh callback
|
|
47
48
|
) -> tuple[ToolProcessor, StreamManager]:
|
|
48
49
|
"""
|
|
49
50
|
Initialize HTTP Streamable transport MCP + a :class:`ToolProcessor`.
|
|
@@ -69,6 +70,7 @@ 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
|
|
73
|
+
oauth_refresh_callback: Optional async callback to refresh OAuth tokens (NEW)
|
|
72
74
|
|
|
73
75
|
Returns:
|
|
74
76
|
Tuple of (ToolProcessor, StreamManager)
|
|
@@ -93,6 +95,7 @@ async def setup_mcp_http_streamable(
|
|
|
93
95
|
connection_timeout=connection_timeout,
|
|
94
96
|
default_timeout=default_timeout,
|
|
95
97
|
initialization_timeout=initialization_timeout,
|
|
98
|
+
oauth_refresh_callback=oauth_refresh_callback, # NEW: Pass OAuth callback
|
|
96
99
|
)
|
|
97
100
|
|
|
98
101
|
# 2️⃣ pull the remote tool list and register each one locally
|
|
@@ -40,6 +40,7 @@ async def setup_mcp_sse( # noqa: C901 - long but just a config facade
|
|
|
40
40
|
enable_retries: bool = True,
|
|
41
41
|
max_retries: int = 3,
|
|
42
42
|
namespace: str = "sse",
|
|
43
|
+
oauth_refresh_callback: any | None = None, # NEW: OAuth token refresh callback
|
|
43
44
|
) -> tuple[ToolProcessor, StreamManager]:
|
|
44
45
|
"""
|
|
45
46
|
Initialise SSE-transport MCP + a :class:`ToolProcessor`.
|
|
@@ -61,6 +62,7 @@ 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
|
|
65
|
+
oauth_refresh_callback: Optional async callback to refresh OAuth tokens (NEW)
|
|
64
66
|
|
|
65
67
|
Returns:
|
|
66
68
|
Tuple of (ToolProcessor, StreamManager)
|
|
@@ -72,6 +74,7 @@ async def setup_mcp_sse( # noqa: C901 - long but just a config facade
|
|
|
72
74
|
connection_timeout=connection_timeout, # 🔧 ADD THIS LINE
|
|
73
75
|
default_timeout=default_timeout, # 🔧 ADD THIS LINE
|
|
74
76
|
initialization_timeout=initialization_timeout,
|
|
77
|
+
oauth_refresh_callback=oauth_refresh_callback, # NEW: Pass OAuth callback
|
|
75
78
|
)
|
|
76
79
|
|
|
77
80
|
# 2️⃣ pull the remote tool list and register each one locally
|
|
@@ -81,6 +81,7 @@ class StreamManager:
|
|
|
81
81
|
connection_timeout: float = 10.0,
|
|
82
82
|
default_timeout: float = 30.0,
|
|
83
83
|
initialization_timeout: float = 60.0, # NEW
|
|
84
|
+
oauth_refresh_callback: any | None = None, # NEW: OAuth token refresh callback
|
|
84
85
|
) -> StreamManager:
|
|
85
86
|
"""Create StreamManager with SSE transport and timeout protection."""
|
|
86
87
|
inst = cls()
|
|
@@ -90,6 +91,7 @@ class StreamManager:
|
|
|
90
91
|
connection_timeout=connection_timeout,
|
|
91
92
|
default_timeout=default_timeout,
|
|
92
93
|
initialization_timeout=initialization_timeout,
|
|
94
|
+
oauth_refresh_callback=oauth_refresh_callback, # NEW: Pass OAuth callback
|
|
93
95
|
)
|
|
94
96
|
return inst
|
|
95
97
|
|
|
@@ -101,6 +103,7 @@ class StreamManager:
|
|
|
101
103
|
connection_timeout: float = 30.0,
|
|
102
104
|
default_timeout: float = 30.0,
|
|
103
105
|
initialization_timeout: float = 60.0, # NEW
|
|
106
|
+
oauth_refresh_callback: any | None = None, # NEW: OAuth token refresh callback
|
|
104
107
|
) -> StreamManager:
|
|
105
108
|
"""Create StreamManager with HTTP Streamable transport and timeout protection."""
|
|
106
109
|
inst = cls()
|
|
@@ -110,6 +113,7 @@ class StreamManager:
|
|
|
110
113
|
connection_timeout=connection_timeout,
|
|
111
114
|
default_timeout=default_timeout,
|
|
112
115
|
initialization_timeout=initialization_timeout,
|
|
116
|
+
oauth_refresh_callback=oauth_refresh_callback, # NEW: Pass OAuth callback
|
|
113
117
|
)
|
|
114
118
|
return inst
|
|
115
119
|
|
|
@@ -285,6 +289,7 @@ class StreamManager:
|
|
|
285
289
|
connection_timeout: float = 10.0,
|
|
286
290
|
default_timeout: float = 30.0,
|
|
287
291
|
initialization_timeout: float = 60.0,
|
|
292
|
+
oauth_refresh_callback: any | None = None, # NEW: OAuth token refresh callback
|
|
288
293
|
) -> None:
|
|
289
294
|
"""Initialize with SSE transport with optional headers support."""
|
|
290
295
|
if self._closed:
|
|
@@ -313,6 +318,11 @@ class StreamManager:
|
|
|
313
318
|
logger.debug("SSE %s: Using configured headers: %s", name, list(headers.keys()))
|
|
314
319
|
transport_params["headers"] = headers
|
|
315
320
|
|
|
321
|
+
# Add OAuth refresh callback if provided (NEW)
|
|
322
|
+
if oauth_refresh_callback:
|
|
323
|
+
transport_params["oauth_refresh_callback"] = oauth_refresh_callback
|
|
324
|
+
logger.debug("SSE %s: OAuth refresh callback configured", name)
|
|
325
|
+
|
|
316
326
|
transport = SSETransport(**transport_params)
|
|
317
327
|
|
|
318
328
|
try:
|
|
@@ -354,6 +364,7 @@ class StreamManager:
|
|
|
354
364
|
connection_timeout: float = 30.0,
|
|
355
365
|
default_timeout: float = 30.0,
|
|
356
366
|
initialization_timeout: float = 60.0,
|
|
367
|
+
oauth_refresh_callback: any | None = None, # NEW: OAuth token refresh callback
|
|
357
368
|
) -> None:
|
|
358
369
|
"""Initialize with HTTP Streamable transport with graceful headers handling."""
|
|
359
370
|
if self._closed:
|
|
@@ -385,6 +396,11 @@ class StreamManager:
|
|
|
385
396
|
transport_params["headers"] = headers
|
|
386
397
|
logger.debug("HTTP Streamable %s: Custom headers configured: %s", name, list(headers.keys()))
|
|
387
398
|
|
|
399
|
+
# Add OAuth refresh callback if provided (NEW)
|
|
400
|
+
if oauth_refresh_callback:
|
|
401
|
+
transport_params["oauth_refresh_callback"] = oauth_refresh_callback
|
|
402
|
+
logger.debug("HTTP Streamable %s: OAuth refresh callback configured", name)
|
|
403
|
+
|
|
388
404
|
transport = HTTPStreamableTransport(**transport_params)
|
|
389
405
|
|
|
390
406
|
logger.debug(f"Calling transport.initialize() for {name} with timeout={initialization_timeout}s")
|
|
@@ -45,6 +45,7 @@ class HTTPStreamableTransport(MCPBaseTransport):
|
|
|
45
45
|
default_timeout: float = 30.0,
|
|
46
46
|
session_id: str | None = None,
|
|
47
47
|
enable_metrics: bool = True,
|
|
48
|
+
oauth_refresh_callback: Any | None = None, # NEW: OAuth token refresh callback
|
|
48
49
|
):
|
|
49
50
|
"""
|
|
50
51
|
Initialize HTTP Streamable transport with enhanced configuration.
|
|
@@ -57,6 +58,7 @@ class HTTPStreamableTransport(MCPBaseTransport):
|
|
|
57
58
|
default_timeout: Default timeout for operations
|
|
58
59
|
session_id: Optional session ID for stateful connections
|
|
59
60
|
enable_metrics: Whether to track performance metrics
|
|
61
|
+
oauth_refresh_callback: Optional async callback to refresh OAuth tokens (NEW)
|
|
60
62
|
"""
|
|
61
63
|
# Ensure URL points to the /mcp endpoint
|
|
62
64
|
if not url.endswith("/mcp"):
|
|
@@ -70,6 +72,7 @@ class HTTPStreamableTransport(MCPBaseTransport):
|
|
|
70
72
|
self.default_timeout = default_timeout
|
|
71
73
|
self.session_id = session_id
|
|
72
74
|
self.enable_metrics = enable_metrics
|
|
75
|
+
self.oauth_refresh_callback = oauth_refresh_callback # NEW: OAuth refresh callback
|
|
73
76
|
|
|
74
77
|
logger.debug("HTTP Streamable transport initialized with URL: %s", self.url)
|
|
75
78
|
if self.api_key:
|
|
@@ -422,9 +425,44 @@ class HTTPStreamableTransport(MCPBaseTransport):
|
|
|
422
425
|
response_time = time.time() - start_time
|
|
423
426
|
result = self._normalize_mcp_response(raw_response)
|
|
424
427
|
|
|
428
|
+
# NEW: Check for OAuth errors and attempt refresh if callback is available
|
|
429
|
+
if result.get("isError", False) and self._is_oauth_error(result.get("error", "")):
|
|
430
|
+
logger.warning("OAuth error detected: %s", result.get("error"))
|
|
431
|
+
|
|
432
|
+
if self.oauth_refresh_callback:
|
|
433
|
+
logger.info("Attempting OAuth token refresh...")
|
|
434
|
+
try:
|
|
435
|
+
# Call the refresh callback
|
|
436
|
+
new_headers = await self.oauth_refresh_callback()
|
|
437
|
+
|
|
438
|
+
if new_headers and "Authorization" in new_headers:
|
|
439
|
+
# Update configured headers with new token
|
|
440
|
+
self.configured_headers.update(new_headers)
|
|
441
|
+
logger.info("OAuth token refreshed, reconnecting...")
|
|
442
|
+
|
|
443
|
+
# Reconnect with new token
|
|
444
|
+
if await self._attempt_recovery():
|
|
445
|
+
logger.info("Retrying tool call after token refresh...")
|
|
446
|
+
# Retry the tool call once with new token
|
|
447
|
+
raw_response = await asyncio.wait_for(
|
|
448
|
+
send_tools_call(self._read_stream, self._write_stream, tool_name, arguments),
|
|
449
|
+
timeout=tool_timeout,
|
|
450
|
+
)
|
|
451
|
+
result = self._normalize_mcp_response(raw_response)
|
|
452
|
+
logger.info("Tool call retry completed")
|
|
453
|
+
else:
|
|
454
|
+
logger.error("Failed to reconnect after token refresh")
|
|
455
|
+
else:
|
|
456
|
+
logger.warning("Token refresh did not return valid Authorization header")
|
|
457
|
+
except Exception as refresh_error:
|
|
458
|
+
logger.error("OAuth token refresh failed: %s", refresh_error)
|
|
459
|
+
else:
|
|
460
|
+
logger.warning("OAuth error detected but no refresh callback configured")
|
|
461
|
+
|
|
425
462
|
# Reset failure count on success
|
|
426
|
-
|
|
427
|
-
|
|
463
|
+
if not result.get("isError", False):
|
|
464
|
+
self._consecutive_failures = 0
|
|
465
|
+
self._last_successful_ping = time.time() # Update health timestamp
|
|
428
466
|
|
|
429
467
|
if self.enable_metrics:
|
|
430
468
|
self._update_metrics(response_time, not result.get("isError", False))
|
|
@@ -477,6 +515,24 @@ class HTTPStreamableTransport(MCPBaseTransport):
|
|
|
477
515
|
if self._metrics["total_calls"] > 0:
|
|
478
516
|
self._metrics["avg_response_time"] = self._metrics["total_time"] / self._metrics["total_calls"]
|
|
479
517
|
|
|
518
|
+
def _is_oauth_error(self, error_msg: str) -> bool:
|
|
519
|
+
"""Detect if error is OAuth-related (NEW)."""
|
|
520
|
+
if not error_msg:
|
|
521
|
+
return False
|
|
522
|
+
|
|
523
|
+
error_lower = error_msg.lower()
|
|
524
|
+
oauth_indicators = [
|
|
525
|
+
"invalid_token",
|
|
526
|
+
"expired token",
|
|
527
|
+
"oauth validation",
|
|
528
|
+
"unauthorized",
|
|
529
|
+
"token expired",
|
|
530
|
+
"authentication failed",
|
|
531
|
+
"invalid access token",
|
|
532
|
+
]
|
|
533
|
+
|
|
534
|
+
return any(indicator in error_lower for indicator in oauth_indicators)
|
|
535
|
+
|
|
480
536
|
async def list_resources(self) -> dict[str, Any]:
|
|
481
537
|
"""Enhanced resource listing with error handling."""
|
|
482
538
|
if not self._initialized:
|
|
@@ -38,6 +38,7 @@ class SSETransport(MCPBaseTransport):
|
|
|
38
38
|
connection_timeout: float = 30.0,
|
|
39
39
|
default_timeout: float = 60.0,
|
|
40
40
|
enable_metrics: bool = True,
|
|
41
|
+
oauth_refresh_callback: Any | None = None, # NEW: OAuth token refresh callback
|
|
41
42
|
):
|
|
42
43
|
"""
|
|
43
44
|
Initialize SSE transport.
|
|
@@ -48,6 +49,7 @@ class SSETransport(MCPBaseTransport):
|
|
|
48
49
|
self.connection_timeout = connection_timeout
|
|
49
50
|
self.default_timeout = default_timeout
|
|
50
51
|
self.enable_metrics = enable_metrics
|
|
52
|
+
self.oauth_refresh_callback = oauth_refresh_callback # NEW: OAuth refresh callback
|
|
51
53
|
|
|
52
54
|
logger.debug("SSE Transport initialized with URL: %s", self.url)
|
|
53
55
|
|
|
@@ -517,11 +519,55 @@ class SSETransport(MCPBaseTransport):
|
|
|
517
519
|
"tools/call", {"name": tool_name, "arguments": arguments}, timeout=timeout
|
|
518
520
|
)
|
|
519
521
|
|
|
522
|
+
# Check for errors
|
|
520
523
|
if "error" in response:
|
|
524
|
+
error_msg = response["error"].get("message", "Unknown error")
|
|
525
|
+
|
|
526
|
+
# NEW: Check for OAuth errors and attempt refresh if callback is available
|
|
527
|
+
if self._is_oauth_error(error_msg):
|
|
528
|
+
logger.warning("OAuth error detected: %s", error_msg)
|
|
529
|
+
|
|
530
|
+
if self.oauth_refresh_callback:
|
|
531
|
+
logger.info("Attempting OAuth token refresh...")
|
|
532
|
+
try:
|
|
533
|
+
# Call the refresh callback
|
|
534
|
+
new_headers = await self.oauth_refresh_callback()
|
|
535
|
+
|
|
536
|
+
if new_headers and "Authorization" in new_headers:
|
|
537
|
+
# Update configured headers with new token
|
|
538
|
+
self.configured_headers.update(new_headers)
|
|
539
|
+
logger.info("OAuth token refreshed, retrying tool call...")
|
|
540
|
+
|
|
541
|
+
# Retry the tool call once with new token
|
|
542
|
+
response = await self._send_request(
|
|
543
|
+
"tools/call", {"name": tool_name, "arguments": arguments}, timeout=timeout
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
# Check if retry succeeded
|
|
547
|
+
if "error" not in response:
|
|
548
|
+
logger.info("Tool call succeeded after token refresh")
|
|
549
|
+
result = response.get("result", {})
|
|
550
|
+
normalized_result = self._normalize_mcp_response({"result": result})
|
|
551
|
+
|
|
552
|
+
if self.enable_metrics:
|
|
553
|
+
self._update_metrics(time.time() - start_time, True)
|
|
554
|
+
|
|
555
|
+
return normalized_result
|
|
556
|
+
else:
|
|
557
|
+
error_msg = response["error"].get("message", "Unknown error")
|
|
558
|
+
logger.error("Tool call failed after token refresh: %s", error_msg)
|
|
559
|
+
else:
|
|
560
|
+
logger.warning("Token refresh did not return valid Authorization header")
|
|
561
|
+
except Exception as refresh_error:
|
|
562
|
+
logger.error("OAuth token refresh failed: %s", refresh_error)
|
|
563
|
+
else:
|
|
564
|
+
logger.warning("OAuth error detected but no refresh callback configured")
|
|
565
|
+
|
|
566
|
+
# Return error (original or from failed retry)
|
|
521
567
|
if self.enable_metrics:
|
|
522
568
|
self._update_metrics(time.time() - start_time, False)
|
|
523
569
|
|
|
524
|
-
return {"isError": True, "error":
|
|
570
|
+
return {"isError": True, "error": error_msg}
|
|
525
571
|
|
|
526
572
|
# Extract and normalize result using base class method
|
|
527
573
|
result = response.get("result", {})
|
|
@@ -555,6 +601,24 @@ class SSETransport(MCPBaseTransport):
|
|
|
555
601
|
if self._metrics["total_calls"] > 0:
|
|
556
602
|
self._metrics["avg_response_time"] = self._metrics["total_time"] / self._metrics["total_calls"]
|
|
557
603
|
|
|
604
|
+
def _is_oauth_error(self, error_msg: str) -> bool:
|
|
605
|
+
"""Detect if error is OAuth-related (NEW)."""
|
|
606
|
+
if not error_msg:
|
|
607
|
+
return False
|
|
608
|
+
|
|
609
|
+
error_lower = error_msg.lower()
|
|
610
|
+
oauth_indicators = [
|
|
611
|
+
"invalid_token",
|
|
612
|
+
"expired token",
|
|
613
|
+
"oauth validation",
|
|
614
|
+
"unauthorized",
|
|
615
|
+
"token expired",
|
|
616
|
+
"authentication failed",
|
|
617
|
+
"invalid access token",
|
|
618
|
+
]
|
|
619
|
+
|
|
620
|
+
return any(indicator in error_lower for indicator in oauth_indicators)
|
|
621
|
+
|
|
558
622
|
async def list_resources(self) -> dict[str, Any]:
|
|
559
623
|
"""List available resources from the server."""
|
|
560
624
|
if not self._initialized:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chuk-tool-processor
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.27
|
|
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>
|
|
@@ -770,6 +770,78 @@ results = await processor.process(
|
|
|
770
770
|
|
|
771
771
|
See `examples/notion_oauth.py`, `examples/stdio_sqlite.py`, and `examples/stdio_echo.py` for complete working implementations.
|
|
772
772
|
|
|
773
|
+
#### OAuth Token Refresh
|
|
774
|
+
|
|
775
|
+
For MCP servers that use OAuth authentication, CHUK Tool Processor supports automatic token refresh when access tokens expire. This prevents your tools from failing due to expired tokens during long-running sessions.
|
|
776
|
+
|
|
777
|
+
**How it works:**
|
|
778
|
+
1. When a tool call receives an OAuth-related error (e.g., "invalid_token", "expired token", "unauthorized")
|
|
779
|
+
2. The processor automatically calls your refresh callback
|
|
780
|
+
3. Updates the authentication headers with the new token
|
|
781
|
+
4. Retries the tool call with fresh credentials
|
|
782
|
+
|
|
783
|
+
**Setup with HTTP Streamable:**
|
|
784
|
+
|
|
785
|
+
```python
|
|
786
|
+
from chuk_tool_processor.mcp import setup_mcp_http_streamable
|
|
787
|
+
|
|
788
|
+
async def refresh_oauth_token():
|
|
789
|
+
"""Called automatically when tokens expire."""
|
|
790
|
+
# Your token refresh logic here
|
|
791
|
+
# Return dict with new Authorization header
|
|
792
|
+
new_token = await your_refresh_logic()
|
|
793
|
+
return {"Authorization": f"Bearer {new_token}"}
|
|
794
|
+
|
|
795
|
+
processor, manager = await setup_mcp_http_streamable(
|
|
796
|
+
servers=[{
|
|
797
|
+
"name": "notion",
|
|
798
|
+
"url": "https://mcp.notion.com/mcp",
|
|
799
|
+
"headers": {"Authorization": f"Bearer {initial_access_token}"}
|
|
800
|
+
}],
|
|
801
|
+
namespace="notion",
|
|
802
|
+
oauth_refresh_callback=refresh_oauth_token # Enable auto-refresh
|
|
803
|
+
)
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
**Setup with SSE:**
|
|
807
|
+
|
|
808
|
+
```python
|
|
809
|
+
from chuk_tool_processor.mcp import setup_mcp_sse
|
|
810
|
+
|
|
811
|
+
async def refresh_oauth_token():
|
|
812
|
+
"""Refresh expired OAuth token."""
|
|
813
|
+
# Exchange refresh token for new access token
|
|
814
|
+
new_access_token = await exchange_refresh_token(refresh_token)
|
|
815
|
+
return {"Authorization": f"Bearer {new_access_token}"}
|
|
816
|
+
|
|
817
|
+
processor, manager = await setup_mcp_sse(
|
|
818
|
+
servers=[{
|
|
819
|
+
"name": "atlassian",
|
|
820
|
+
"url": "https://mcp.atlassian.com/v1/sse",
|
|
821
|
+
"headers": {"Authorization": f"Bearer {initial_token}"}
|
|
822
|
+
}],
|
|
823
|
+
namespace="atlassian",
|
|
824
|
+
oauth_refresh_callback=refresh_oauth_token
|
|
825
|
+
)
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
**OAuth errors detected automatically:**
|
|
829
|
+
- `invalid_token`
|
|
830
|
+
- `expired token`
|
|
831
|
+
- `OAuth validation failed`
|
|
832
|
+
- `unauthorized`
|
|
833
|
+
- `token expired`
|
|
834
|
+
- `authentication failed`
|
|
835
|
+
- `invalid access token`
|
|
836
|
+
|
|
837
|
+
**Important notes:**
|
|
838
|
+
- The refresh callback must return a dict with an `Authorization` key
|
|
839
|
+
- If refresh fails or returns invalid headers, the original error is returned
|
|
840
|
+
- Token refresh is attempted only once per tool call (no infinite retry loops)
|
|
841
|
+
- After successful refresh, the updated headers are used for all subsequent calls
|
|
842
|
+
|
|
843
|
+
See `examples/notion_oauth.py` for a complete OAuth 2.1 implementation with PKCE and automatic token refresh.
|
|
844
|
+
|
|
773
845
|
### Observability
|
|
774
846
|
|
|
775
847
|
#### Structured Logging
|
|
@@ -19,14 +19,14 @@ chuk_tool_processor/logging/metrics.py,sha256=8LRHjgkfdcQnh4H7AP2FJDfcRO3q1UpsBS
|
|
|
19
19
|
chuk_tool_processor/mcp/__init__.py,sha256=xqtoSGX1_5jZDJ6AKJpBByaytS22baDOrhzFiecvVSs,1031
|
|
20
20
|
chuk_tool_processor/mcp/mcp_tool.py,sha256=zOx3YeuKyuFK-PbUt3gqdq_q8VRrHFD6sgma6qKbfPY,19170
|
|
21
21
|
chuk_tool_processor/mcp/register_mcp_tools.py,sha256=OyHczwVnqhvBZO9g4I0T56EPMvFYBOl0Y2ivNPdKjCE,4822
|
|
22
|
-
chuk_tool_processor/mcp/setup_mcp_http_streamable.py,sha256=
|
|
23
|
-
chuk_tool_processor/mcp/setup_mcp_sse.py,sha256=
|
|
22
|
+
chuk_tool_processor/mcp/setup_mcp_http_streamable.py,sha256=8NCjeEZjV0KrapCqAvGh6jr5G8B24xOxxAaKUyoiykw,4935
|
|
23
|
+
chuk_tool_processor/mcp/setup_mcp_sse.py,sha256=gvixVml8HN2BdM9Ug_JYp0yHqA1owieyX8Yxx0HNOqg,4174
|
|
24
24
|
chuk_tool_processor/mcp/setup_mcp_stdio.py,sha256=KxCC0BL0C6z5ZHxBzPhWZC9CKrGUACXqx1tkjru-UYI,2922
|
|
25
|
-
chuk_tool_processor/mcp/stream_manager.py,sha256=
|
|
25
|
+
chuk_tool_processor/mcp/stream_manager.py,sha256=GKUXqJpRn_O8008qJompwZfdFlb6nRlZZGh8jqr23y0,34184
|
|
26
26
|
chuk_tool_processor/mcp/transport/__init__.py,sha256=Gpw9QZctxfO-tWZ8URpyFU8rePc5Xe7VZiAvXaiF8cw,657
|
|
27
27
|
chuk_tool_processor/mcp/transport/base_transport.py,sha256=rG61TlaignbVZbsqdBS38TnFzTVO666ehKEI0IUAJCM,8675
|
|
28
|
-
chuk_tool_processor/mcp/transport/http_streamable_transport.py,sha256=
|
|
29
|
-
chuk_tool_processor/mcp/transport/sse_transport.py,sha256=
|
|
28
|
+
chuk_tool_processor/mcp/transport/http_streamable_transport.py,sha256=PKU-s2cofagY_x5nwocfi3Q0uJjoAHh8ALP0nePWVRc,26547
|
|
29
|
+
chuk_tool_processor/mcp/transport/sse_transport.py,sha256=WfsfsHzN579vPLl13cjHwi5sn_uKrf4ZnVxasEYHy6I,30629
|
|
30
30
|
chuk_tool_processor/mcp/transport/stdio_transport.py,sha256=bDh3eqe9BnPvcmG7F4BWqGjGO3e8q0lwcZeswx_jK0U,29558
|
|
31
31
|
chuk_tool_processor/models/__init__.py,sha256=A3ysSvRxaxso_AN57QZt5ZYahJH5zlL-IENqNaius84,41
|
|
32
32
|
chuk_tool_processor/models/execution_strategy.py,sha256=O0h8d8JSgm-tv26Cc5jAkZqup8vIgx0zfb5n0b2vpSk,1967
|
|
@@ -54,7 +54,7 @@ chuk_tool_processor/registry/providers/__init__.py,sha256=iGc_2JzlYJSBRQ6tFbX781
|
|
|
54
54
|
chuk_tool_processor/registry/providers/memory.py,sha256=udfboAHH0gRxtnf3GsI3wMshhobJxYnCkMwKjQ_uqkw,5017
|
|
55
55
|
chuk_tool_processor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
56
|
chuk_tool_processor/utils/validation.py,sha256=jHPO65sB61ynm9P6V3th4pN7j4u0SQhYR-bstj5QjnI,4175
|
|
57
|
-
chuk_tool_processor-0.6.
|
|
58
|
-
chuk_tool_processor-0.6.
|
|
59
|
-
chuk_tool_processor-0.6.
|
|
60
|
-
chuk_tool_processor-0.6.
|
|
57
|
+
chuk_tool_processor-0.6.27.dist-info/METADATA,sha256=_EJFoLfhbyqOeXNhpHu9N2z4HMLlTfkDMCb9nO97OVo,39362
|
|
58
|
+
chuk_tool_processor-0.6.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
59
|
+
chuk_tool_processor-0.6.27.dist-info/top_level.txt,sha256=7lTsnuRx4cOW4U2sNJWNxl4ZTt_J1ndkjTbj3pHPY5M,20
|
|
60
|
+
chuk_tool_processor-0.6.27.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|