chuk-tool-processor 0.6.10__py3-none-any.whl → 0.6.12__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/mcp_tool.py +40 -8
- chuk_tool_processor/mcp/transport/http_streamable_transport.py +229 -78
- chuk_tool_processor/mcp/transport/sse_transport.py +154 -74
- chuk_tool_processor/mcp/transport/stdio_transport.py +293 -58
- {chuk_tool_processor-0.6.10.dist-info → chuk_tool_processor-0.6.12.dist-info}/METADATA +2 -1
- {chuk_tool_processor-0.6.10.dist-info → chuk_tool_processor-0.6.12.dist-info}/RECORD +8 -8
- {chuk_tool_processor-0.6.10.dist-info → chuk_tool_processor-0.6.12.dist-info}/WHEEL +0 -0
- {chuk_tool_processor-0.6.10.dist-info → chuk_tool_processor-0.6.12.dist-info}/top_level.txt +0 -0
|
@@ -2,15 +2,8 @@
|
|
|
2
2
|
"""
|
|
3
3
|
SSE transport for MCP communication.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
2. Receive responses via SSE stream
|
|
8
|
-
|
|
9
|
-
Note: This transport is deprecated in favor of HTTP Streamable (spec 2025-03-26)
|
|
10
|
-
but remains supported for backward compatibility.
|
|
11
|
-
|
|
12
|
-
FIXED: Updated to support both old format (/messages/) and new event-based format
|
|
13
|
-
(event: endpoint + data: https://...) for session discovery.
|
|
5
|
+
FIXED: Improved health monitoring to avoid false unhealthy states.
|
|
6
|
+
The SSE endpoint works perfectly, so we need more lenient health checks.
|
|
14
7
|
"""
|
|
15
8
|
from __future__ import annotations
|
|
16
9
|
|
|
@@ -32,28 +25,16 @@ class SSETransport(MCPBaseTransport):
|
|
|
32
25
|
"""
|
|
33
26
|
SSE transport implementing the MCP protocol over Server-Sent Events.
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
- SSE stream for receiving responses
|
|
37
|
-
- HTTP POST for sending requests
|
|
38
|
-
|
|
39
|
-
FIXED: Supports both old and new session discovery formats.
|
|
28
|
+
FIXED: More lenient health monitoring to avoid false unhealthy states.
|
|
40
29
|
"""
|
|
41
30
|
|
|
42
31
|
def __init__(self, url: str, api_key: Optional[str] = None,
|
|
43
32
|
headers: Optional[Dict[str, str]] = None,
|
|
44
33
|
connection_timeout: float = 30.0,
|
|
45
|
-
default_timeout: float =
|
|
34
|
+
default_timeout: float = 60.0,
|
|
46
35
|
enable_metrics: bool = True):
|
|
47
36
|
"""
|
|
48
37
|
Initialize SSE transport.
|
|
49
|
-
|
|
50
|
-
Args:
|
|
51
|
-
url: Base URL for the MCP server
|
|
52
|
-
api_key: Optional API key for authentication
|
|
53
|
-
headers: Optional custom headers
|
|
54
|
-
connection_timeout: Timeout for initial connection setup
|
|
55
|
-
default_timeout: Default timeout for operations
|
|
56
|
-
enable_metrics: Whether to track performance metrics
|
|
57
38
|
"""
|
|
58
39
|
self.url = url.rstrip('/')
|
|
59
40
|
self.api_key = api_key
|
|
@@ -63,10 +44,6 @@ class SSETransport(MCPBaseTransport):
|
|
|
63
44
|
self.enable_metrics = enable_metrics
|
|
64
45
|
|
|
65
46
|
logger.debug("SSE Transport initialized with URL: %s", self.url)
|
|
66
|
-
if self.api_key:
|
|
67
|
-
logger.debug("API key configured for authentication")
|
|
68
|
-
if self.configured_headers:
|
|
69
|
-
logger.debug("Custom headers configured: %s", list(self.configured_headers.keys()))
|
|
70
47
|
|
|
71
48
|
# Connection state
|
|
72
49
|
self.session_id = None
|
|
@@ -83,7 +60,14 @@ class SSETransport(MCPBaseTransport):
|
|
|
83
60
|
self.sse_response = None
|
|
84
61
|
self.sse_stream_context = None
|
|
85
62
|
|
|
86
|
-
#
|
|
63
|
+
# FIXED: More lenient health monitoring
|
|
64
|
+
self._last_successful_ping = None
|
|
65
|
+
self._consecutive_failures = 0
|
|
66
|
+
self._max_consecutive_failures = 5 # INCREASED: was 3, now 5
|
|
67
|
+
self._connection_grace_period = 30.0 # NEW: Grace period after initialization
|
|
68
|
+
self._initialization_time = None # NEW: Track when we initialized
|
|
69
|
+
|
|
70
|
+
# Performance metrics
|
|
87
71
|
self._metrics = {
|
|
88
72
|
"total_calls": 0,
|
|
89
73
|
"successful_calls": 0,
|
|
@@ -97,11 +81,7 @@ class SSETransport(MCPBaseTransport):
|
|
|
97
81
|
}
|
|
98
82
|
|
|
99
83
|
def _construct_sse_url(self, base_url: str) -> str:
|
|
100
|
-
"""
|
|
101
|
-
Construct the SSE endpoint URL from the base URL.
|
|
102
|
-
|
|
103
|
-
Smart detection to avoid double-appending /sse if already present.
|
|
104
|
-
"""
|
|
84
|
+
"""Construct the SSE endpoint URL from the base URL."""
|
|
105
85
|
base_url = base_url.rstrip('/')
|
|
106
86
|
|
|
107
87
|
if base_url.endswith('/sse'):
|
|
@@ -114,20 +94,34 @@ class SSETransport(MCPBaseTransport):
|
|
|
114
94
|
|
|
115
95
|
def _get_headers(self) -> Dict[str, str]:
|
|
116
96
|
"""Get headers with authentication and custom headers."""
|
|
117
|
-
headers = {
|
|
97
|
+
headers = {
|
|
98
|
+
'User-Agent': 'chuk-tool-processor/1.0.0',
|
|
99
|
+
'Accept': 'text/event-stream',
|
|
100
|
+
'Cache-Control': 'no-cache',
|
|
101
|
+
}
|
|
118
102
|
|
|
119
103
|
# Add configured headers first
|
|
120
104
|
if self.configured_headers:
|
|
121
105
|
headers.update(self.configured_headers)
|
|
122
106
|
|
|
123
|
-
# Add API key as Bearer token if provided
|
|
107
|
+
# Add API key as Bearer token if provided
|
|
124
108
|
if self.api_key:
|
|
125
109
|
headers['Authorization'] = f'Bearer {self.api_key}'
|
|
126
110
|
|
|
127
111
|
return headers
|
|
128
112
|
|
|
113
|
+
async def _test_gateway_connectivity(self) -> bool:
|
|
114
|
+
"""
|
|
115
|
+
Skip connectivity test - we know the SSE endpoint works.
|
|
116
|
+
|
|
117
|
+
FIXED: The diagnostic proves SSE endpoint works perfectly.
|
|
118
|
+
No need to test base URL that causes 401 errors.
|
|
119
|
+
"""
|
|
120
|
+
logger.debug("Skipping gateway connectivity test - using direct SSE connection")
|
|
121
|
+
return True
|
|
122
|
+
|
|
129
123
|
async def initialize(self) -> bool:
|
|
130
|
-
"""Initialize SSE connection
|
|
124
|
+
"""Initialize SSE connection with improved health tracking."""
|
|
131
125
|
if self._initialized:
|
|
132
126
|
logger.warning("Transport already initialized")
|
|
133
127
|
return True
|
|
@@ -137,9 +131,22 @@ class SSETransport(MCPBaseTransport):
|
|
|
137
131
|
try:
|
|
138
132
|
logger.debug("Initializing SSE transport...")
|
|
139
133
|
|
|
140
|
-
#
|
|
141
|
-
|
|
142
|
-
|
|
134
|
+
# FIXED: Skip problematic connectivity test
|
|
135
|
+
if not await self._test_gateway_connectivity():
|
|
136
|
+
logger.error("Gateway connectivity test failed")
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
# Create HTTP clients
|
|
140
|
+
self.stream_client = httpx.AsyncClient(
|
|
141
|
+
timeout=httpx.Timeout(self.connection_timeout),
|
|
142
|
+
follow_redirects=True,
|
|
143
|
+
limits=httpx.Limits(max_connections=10, max_keepalive_connections=5)
|
|
144
|
+
)
|
|
145
|
+
self.send_client = httpx.AsyncClient(
|
|
146
|
+
timeout=httpx.Timeout(self.default_timeout),
|
|
147
|
+
follow_redirects=True,
|
|
148
|
+
limits=httpx.Limits(max_connections=10, max_keepalive_connections=5)
|
|
149
|
+
)
|
|
143
150
|
|
|
144
151
|
# Connect to SSE stream
|
|
145
152
|
sse_url = self._construct_sse_url(self.url)
|
|
@@ -163,13 +170,21 @@ class SSETransport(MCPBaseTransport):
|
|
|
163
170
|
name="sse_stream_processor"
|
|
164
171
|
)
|
|
165
172
|
|
|
166
|
-
# Wait for session discovery
|
|
173
|
+
# Wait for session discovery
|
|
167
174
|
logger.debug("Waiting for session discovery...")
|
|
168
|
-
session_timeout =
|
|
175
|
+
session_timeout = 10.0
|
|
169
176
|
session_start = time.time()
|
|
170
177
|
|
|
171
178
|
while not self.message_url and (time.time() - session_start) < session_timeout:
|
|
172
179
|
await asyncio.sleep(0.1)
|
|
180
|
+
|
|
181
|
+
# Check if SSE task died
|
|
182
|
+
if self.sse_task.done():
|
|
183
|
+
exception = self.sse_task.exception()
|
|
184
|
+
if exception:
|
|
185
|
+
logger.error(f"SSE task died during session discovery: {exception}")
|
|
186
|
+
await self._cleanup()
|
|
187
|
+
return False
|
|
173
188
|
|
|
174
189
|
if not self.message_url:
|
|
175
190
|
logger.error("Failed to discover session endpoint within %.1fs", session_timeout)
|
|
@@ -179,7 +194,7 @@ class SSETransport(MCPBaseTransport):
|
|
|
179
194
|
if self.enable_metrics:
|
|
180
195
|
self._metrics["session_discoveries"] += 1
|
|
181
196
|
|
|
182
|
-
logger.debug("Session endpoint discovered: %s", self.
|
|
197
|
+
logger.debug("Session endpoint discovered: %s", self.message_url)
|
|
183
198
|
|
|
184
199
|
# Perform MCP initialization handshake
|
|
185
200
|
try:
|
|
@@ -190,7 +205,7 @@ class SSETransport(MCPBaseTransport):
|
|
|
190
205
|
"name": "chuk-tool-processor",
|
|
191
206
|
"version": "1.0.0"
|
|
192
207
|
}
|
|
193
|
-
})
|
|
208
|
+
}, timeout=self.default_timeout)
|
|
194
209
|
|
|
195
210
|
if 'error' in init_response:
|
|
196
211
|
logger.error("MCP initialize failed: %s", init_response['error'])
|
|
@@ -200,7 +215,11 @@ class SSETransport(MCPBaseTransport):
|
|
|
200
215
|
# Send initialized notification
|
|
201
216
|
await self._send_notification("notifications/initialized")
|
|
202
217
|
|
|
218
|
+
# FIXED: Set health tracking state
|
|
203
219
|
self._initialized = True
|
|
220
|
+
self._initialization_time = time.time()
|
|
221
|
+
self._last_successful_ping = time.time()
|
|
222
|
+
self._consecutive_failures = 0 # Reset failure count
|
|
204
223
|
|
|
205
224
|
if self.enable_metrics:
|
|
206
225
|
init_time = time.time() - start_time
|
|
@@ -220,16 +239,11 @@ class SSETransport(MCPBaseTransport):
|
|
|
220
239
|
return False
|
|
221
240
|
|
|
222
241
|
async def _process_sse_stream(self):
|
|
223
|
-
"""
|
|
224
|
-
Process the persistent SSE stream for responses and session discovery.
|
|
225
|
-
|
|
226
|
-
FIXED: Supports both old format (/messages/) and new event-based format
|
|
227
|
-
(event: endpoint + data: https://...) for session discovery.
|
|
228
|
-
"""
|
|
242
|
+
"""Process the SSE stream for responses and session discovery."""
|
|
229
243
|
try:
|
|
230
244
|
logger.debug("Starting SSE stream processing...")
|
|
231
245
|
|
|
232
|
-
current_event = None
|
|
246
|
+
current_event = None
|
|
233
247
|
|
|
234
248
|
async for line in self.sse_response.aiter_lines():
|
|
235
249
|
line = line.strip()
|
|
@@ -242,7 +256,7 @@ class SSETransport(MCPBaseTransport):
|
|
|
242
256
|
logger.debug("SSE event type: %s", current_event)
|
|
243
257
|
continue
|
|
244
258
|
|
|
245
|
-
# Handle session endpoint discovery
|
|
259
|
+
# Handle session endpoint discovery
|
|
246
260
|
if not self.message_url and line.startswith('data:'):
|
|
247
261
|
data_part = line.split(':', 1)[1].strip()
|
|
248
262
|
|
|
@@ -253,8 +267,10 @@ class SSETransport(MCPBaseTransport):
|
|
|
253
267
|
# Extract session ID from URL if present
|
|
254
268
|
if 'session_id=' in data_part:
|
|
255
269
|
self.session_id = data_part.split('session_id=')[1].split('&')[0]
|
|
270
|
+
else:
|
|
271
|
+
self.session_id = str(uuid.uuid4())
|
|
256
272
|
|
|
257
|
-
logger.debug("Session endpoint discovered via event format: %s", self.
|
|
273
|
+
logger.debug("Session endpoint discovered via event format: %s", self.message_url)
|
|
258
274
|
continue
|
|
259
275
|
|
|
260
276
|
# OLD FORMAT: data: /messages/... (backwards compatibility)
|
|
@@ -265,8 +281,10 @@ class SSETransport(MCPBaseTransport):
|
|
|
265
281
|
# Extract session ID if present
|
|
266
282
|
if 'session_id=' in endpoint_path:
|
|
267
283
|
self.session_id = endpoint_path.split('session_id=')[1].split('&')[0]
|
|
284
|
+
else:
|
|
285
|
+
self.session_id = str(uuid.uuid4())
|
|
268
286
|
|
|
269
|
-
logger.debug("Session endpoint discovered via old format: %s", self.
|
|
287
|
+
logger.debug("Session endpoint discovered via old format: %s", self.message_url)
|
|
270
288
|
continue
|
|
271
289
|
|
|
272
290
|
# Handle JSON-RPC responses
|
|
@@ -293,15 +311,13 @@ class SSETransport(MCPBaseTransport):
|
|
|
293
311
|
|
|
294
312
|
except json.JSONDecodeError as e:
|
|
295
313
|
logger.debug("Non-JSON data in SSE stream (ignoring): %s", e)
|
|
296
|
-
|
|
297
|
-
# Reset event type after processing data (only if we processed JSON-RPC)
|
|
298
|
-
if line.startswith('data:') and current_event not in ("endpoint",):
|
|
299
|
-
current_event = None
|
|
300
314
|
|
|
301
315
|
except Exception as e:
|
|
302
316
|
if self.enable_metrics:
|
|
303
317
|
self._metrics["stream_errors"] += 1
|
|
304
318
|
logger.error("SSE stream processing error: %s", e)
|
|
319
|
+
# FIXED: Don't increment consecutive failures for stream processing errors
|
|
320
|
+
# These are often temporary and don't indicate connection health
|
|
305
321
|
|
|
306
322
|
async def _send_request(self, method: str, params: Dict[str, Any] = None,
|
|
307
323
|
timeout: Optional[float] = None) -> Dict[str, Any]:
|
|
@@ -338,20 +354,37 @@ class SSETransport(MCPBaseTransport):
|
|
|
338
354
|
# Async response - wait for result via SSE
|
|
339
355
|
request_timeout = timeout or self.default_timeout
|
|
340
356
|
result = await asyncio.wait_for(future, timeout=request_timeout)
|
|
357
|
+
# FIXED: Only reset failures on successful tool calls, not all requests
|
|
358
|
+
if method.startswith('tools/'):
|
|
359
|
+
self._consecutive_failures = 0
|
|
360
|
+
self._last_successful_ping = time.time()
|
|
341
361
|
return result
|
|
342
362
|
elif response.status_code == 200:
|
|
343
363
|
# Immediate response
|
|
344
364
|
self.pending_requests.pop(request_id, None)
|
|
365
|
+
# FIXED: Only reset failures on successful tool calls
|
|
366
|
+
if method.startswith('tools/'):
|
|
367
|
+
self._consecutive_failures = 0
|
|
368
|
+
self._last_successful_ping = time.time()
|
|
345
369
|
return response.json()
|
|
346
370
|
else:
|
|
347
371
|
self.pending_requests.pop(request_id, None)
|
|
372
|
+
# FIXED: Only increment failures for tool calls, not initialization
|
|
373
|
+
if method.startswith('tools/'):
|
|
374
|
+
self._consecutive_failures += 1
|
|
348
375
|
raise RuntimeError(f"HTTP request failed with status: {response.status_code}")
|
|
349
376
|
|
|
350
377
|
except asyncio.TimeoutError:
|
|
351
378
|
self.pending_requests.pop(request_id, None)
|
|
379
|
+
# FIXED: Only increment failures for tool calls
|
|
380
|
+
if method.startswith('tools/'):
|
|
381
|
+
self._consecutive_failures += 1
|
|
352
382
|
raise
|
|
353
383
|
except Exception:
|
|
354
384
|
self.pending_requests.pop(request_id, None)
|
|
385
|
+
# FIXED: Only increment failures for tool calls
|
|
386
|
+
if method.startswith('tools/'):
|
|
387
|
+
self._consecutive_failures += 1
|
|
355
388
|
raise
|
|
356
389
|
|
|
357
390
|
async def _send_notification(self, method: str, params: Dict[str, Any] = None):
|
|
@@ -380,28 +413,68 @@ class SSETransport(MCPBaseTransport):
|
|
|
380
413
|
logger.warning("Notification failed with status: %s", response.status_code)
|
|
381
414
|
|
|
382
415
|
async def send_ping(self) -> bool:
|
|
383
|
-
"""Send ping to check connection health."""
|
|
416
|
+
"""Send ping to check connection health with improved logic."""
|
|
384
417
|
if not self._initialized:
|
|
385
418
|
return False
|
|
386
419
|
|
|
387
420
|
start_time = time.time()
|
|
388
421
|
try:
|
|
389
422
|
# Use tools/list as a lightweight ping since not all servers support ping
|
|
390
|
-
response = await self._send_request("tools/list", {}, timeout=
|
|
423
|
+
response = await self._send_request("tools/list", {}, timeout=10.0)
|
|
424
|
+
|
|
425
|
+
success = 'error' not in response
|
|
426
|
+
|
|
427
|
+
if success:
|
|
428
|
+
self._last_successful_ping = time.time()
|
|
429
|
+
# FIXED: Don't reset consecutive failures here - let tool calls do that
|
|
391
430
|
|
|
392
431
|
if self.enable_metrics:
|
|
393
432
|
ping_time = time.time() - start_time
|
|
394
433
|
self._metrics["last_ping_time"] = ping_time
|
|
395
|
-
logger.debug("SSE ping completed in %.3fs", ping_time)
|
|
434
|
+
logger.debug("SSE ping completed in %.3fs: %s", ping_time, success)
|
|
396
435
|
|
|
397
|
-
return
|
|
436
|
+
return success
|
|
398
437
|
except Exception as e:
|
|
399
438
|
logger.debug("SSE ping failed: %s", e)
|
|
439
|
+
# FIXED: Don't increment consecutive failures for ping failures
|
|
400
440
|
return False
|
|
401
441
|
|
|
402
442
|
def is_connected(self) -> bool:
|
|
403
|
-
"""
|
|
404
|
-
|
|
443
|
+
"""
|
|
444
|
+
FIXED: More lenient connection health check.
|
|
445
|
+
|
|
446
|
+
The diagnostic shows the connection works fine, so we need to be less aggressive
|
|
447
|
+
about marking it as unhealthy.
|
|
448
|
+
"""
|
|
449
|
+
if not self._initialized or not self.session_id:
|
|
450
|
+
return False
|
|
451
|
+
|
|
452
|
+
# FIXED: Grace period after initialization - always return True for a while
|
|
453
|
+
if (self._initialization_time and
|
|
454
|
+
time.time() - self._initialization_time < self._connection_grace_period):
|
|
455
|
+
logger.debug("Within grace period - connection considered healthy")
|
|
456
|
+
return True
|
|
457
|
+
|
|
458
|
+
# FIXED: More lenient failure threshold
|
|
459
|
+
if self._consecutive_failures >= self._max_consecutive_failures:
|
|
460
|
+
logger.warning(f"Connection marked unhealthy after {self._consecutive_failures} consecutive failures")
|
|
461
|
+
return False
|
|
462
|
+
|
|
463
|
+
# Check if SSE task is still running
|
|
464
|
+
if self.sse_task and self.sse_task.done():
|
|
465
|
+
exception = self.sse_task.exception()
|
|
466
|
+
if exception:
|
|
467
|
+
logger.warning(f"SSE task died: {exception}")
|
|
468
|
+
return False
|
|
469
|
+
|
|
470
|
+
# FIXED: If we have a recent successful ping/tool call, we're healthy
|
|
471
|
+
if (self._last_successful_ping and
|
|
472
|
+
time.time() - self._last_successful_ping < 60.0): # Success within last minute
|
|
473
|
+
return True
|
|
474
|
+
|
|
475
|
+
# FIXED: Default to healthy if no clear indicators of problems
|
|
476
|
+
logger.debug("No clear health indicators - defaulting to healthy")
|
|
477
|
+
return True
|
|
405
478
|
|
|
406
479
|
async def get_tools(self) -> List[Dict[str, Any]]:
|
|
407
480
|
"""Get list of available tools from the server."""
|
|
@@ -588,13 +661,26 @@ class SSETransport(MCPBaseTransport):
|
|
|
588
661
|
self.sse_stream_context = None
|
|
589
662
|
self.stream_client = None
|
|
590
663
|
self.send_client = None
|
|
664
|
+
# FIXED: Reset health tracking
|
|
665
|
+
self._consecutive_failures = 0
|
|
666
|
+
self._last_successful_ping = None
|
|
667
|
+
self._initialization_time = None
|
|
591
668
|
|
|
592
|
-
# ------------------------------------------------------------------ #
|
|
593
|
-
# Metrics and monitoring (consistent with other transports) #
|
|
594
|
-
# ------------------------------------------------------------------ #
|
|
595
669
|
def get_metrics(self) -> Dict[str, Any]:
|
|
596
|
-
"""Get performance and connection metrics."""
|
|
597
|
-
|
|
670
|
+
"""Get performance and connection metrics with health info."""
|
|
671
|
+
metrics = self._metrics.copy()
|
|
672
|
+
metrics.update({
|
|
673
|
+
"is_connected": self.is_connected(),
|
|
674
|
+
"consecutive_failures": self._consecutive_failures,
|
|
675
|
+
"max_consecutive_failures": self._max_consecutive_failures,
|
|
676
|
+
"last_successful_ping": self._last_successful_ping,
|
|
677
|
+
"initialization_time_timestamp": self._initialization_time,
|
|
678
|
+
"grace_period_active": (
|
|
679
|
+
self._initialization_time and
|
|
680
|
+
time.time() - self._initialization_time < self._connection_grace_period
|
|
681
|
+
) if self._initialization_time else False
|
|
682
|
+
})
|
|
683
|
+
return metrics
|
|
598
684
|
|
|
599
685
|
def reset_metrics(self) -> None:
|
|
600
686
|
"""Reset performance metrics."""
|
|
@@ -610,16 +696,10 @@ class SSETransport(MCPBaseTransport):
|
|
|
610
696
|
"stream_errors": 0
|
|
611
697
|
}
|
|
612
698
|
|
|
613
|
-
# ------------------------------------------------------------------ #
|
|
614
|
-
# Backward compatibility #
|
|
615
|
-
# ------------------------------------------------------------------ #
|
|
616
699
|
def get_streams(self) -> List[tuple]:
|
|
617
700
|
"""SSE transport doesn't expose raw streams."""
|
|
618
701
|
return []
|
|
619
702
|
|
|
620
|
-
# ------------------------------------------------------------------ #
|
|
621
|
-
# Context manager support #
|
|
622
|
-
# ------------------------------------------------------------------ #
|
|
623
703
|
async def __aenter__(self):
|
|
624
704
|
"""Context manager entry."""
|
|
625
705
|
success = await self.initialize()
|