chuk-tool-processor 0.6.7__py3-none-any.whl → 0.6.9__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/stream_manager.py +53 -34
- chuk_tool_processor/mcp/transport/__init__.py +10 -27
- chuk_tool_processor/mcp/transport/base_transport.py +197 -46
- chuk_tool_processor/mcp/transport/http_streamable_transport.py +134 -173
- chuk_tool_processor/mcp/transport/sse_transport.py +238 -119
- chuk_tool_processor/mcp/transport/stdio_transport.py +297 -81
- {chuk_tool_processor-0.6.7.dist-info → chuk_tool_processor-0.6.9.dist-info}/METADATA +1 -1
- {chuk_tool_processor-0.6.7.dist-info → chuk_tool_processor-0.6.9.dist-info}/RECORD +10 -10
- {chuk_tool_processor-0.6.7.dist-info → chuk_tool_processor-0.6.9.dist-info}/WHEEL +0 -0
- {chuk_tool_processor-0.6.7.dist-info → chuk_tool_processor-0.6.9.dist-info}/top_level.txt +0 -0
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
import asyncio
|
|
5
5
|
import json
|
|
6
|
+
import time
|
|
6
7
|
from typing import Dict, Any, List, Optional
|
|
7
8
|
import logging
|
|
8
9
|
|
|
@@ -11,28 +12,34 @@ from chuk_mcp.transports.stdio import stdio_client
|
|
|
11
12
|
from chuk_mcp.transports.stdio.parameters import StdioParameters
|
|
12
13
|
from chuk_mcp.protocol.messages import (
|
|
13
14
|
send_initialize, send_ping, send_tools_list, send_tools_call,
|
|
15
|
+
send_resources_list, send_resources_read,
|
|
16
|
+
send_prompts_list, send_prompts_get,
|
|
14
17
|
)
|
|
15
18
|
|
|
16
|
-
# Optional imports
|
|
17
|
-
try:
|
|
18
|
-
from chuk_mcp.protocol.messages import send_resources_list, send_resources_read
|
|
19
|
-
HAS_RESOURCES = True
|
|
20
|
-
except ImportError:
|
|
21
|
-
HAS_RESOURCES = False
|
|
22
|
-
|
|
23
|
-
try:
|
|
24
|
-
from chuk_mcp.protocol.messages import send_prompts_list, send_prompts_get
|
|
25
|
-
HAS_PROMPTS = True
|
|
26
|
-
except ImportError:
|
|
27
|
-
HAS_PROMPTS = False
|
|
28
|
-
|
|
29
19
|
logger = logging.getLogger(__name__)
|
|
30
20
|
|
|
31
21
|
|
|
32
22
|
class StdioTransport(MCPBaseTransport):
|
|
33
|
-
"""
|
|
23
|
+
"""
|
|
24
|
+
STDIO transport for MCP communication using process pipes.
|
|
25
|
+
|
|
26
|
+
This transport uses subprocess communication via stdin/stdout pipes
|
|
27
|
+
to communicate with MCP servers.
|
|
28
|
+
"""
|
|
34
29
|
|
|
35
|
-
def __init__(self, server_params
|
|
30
|
+
def __init__(self, server_params,
|
|
31
|
+
connection_timeout: float = 30.0,
|
|
32
|
+
default_timeout: float = 30.0,
|
|
33
|
+
enable_metrics: bool = True):
|
|
34
|
+
"""
|
|
35
|
+
Initialize STDIO transport.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
server_params: Server parameters (dict or StdioParameters object)
|
|
39
|
+
connection_timeout: Timeout for initial connection setup
|
|
40
|
+
default_timeout: Default timeout for operations
|
|
41
|
+
enable_metrics: Whether to track performance metrics
|
|
42
|
+
"""
|
|
36
43
|
# Convert dict to StdioParameters if needed
|
|
37
44
|
if isinstance(server_params, dict):
|
|
38
45
|
self.server_params = StdioParameters(
|
|
@@ -43,107 +50,278 @@ class StdioTransport(MCPBaseTransport):
|
|
|
43
50
|
else:
|
|
44
51
|
self.server_params = server_params
|
|
45
52
|
|
|
53
|
+
self.connection_timeout = connection_timeout
|
|
54
|
+
self.default_timeout = default_timeout
|
|
55
|
+
self.enable_metrics = enable_metrics
|
|
56
|
+
|
|
57
|
+
# Connection state
|
|
46
58
|
self._context = None
|
|
47
59
|
self._streams = None
|
|
48
60
|
self._initialized = False
|
|
61
|
+
|
|
62
|
+
# Performance metrics (consistent with other transports)
|
|
63
|
+
self._metrics = {
|
|
64
|
+
"total_calls": 0,
|
|
65
|
+
"successful_calls": 0,
|
|
66
|
+
"failed_calls": 0,
|
|
67
|
+
"total_time": 0.0,
|
|
68
|
+
"avg_response_time": 0.0,
|
|
69
|
+
"last_ping_time": None,
|
|
70
|
+
"initialization_time": None,
|
|
71
|
+
"process_restarts": 0,
|
|
72
|
+
"pipe_errors": 0
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
logger.debug("STDIO transport initialized for command: %s",
|
|
76
|
+
getattr(self.server_params, 'command', 'unknown'))
|
|
49
77
|
|
|
50
78
|
async def initialize(self) -> bool:
|
|
51
|
-
"""Initialize by delegating to chuk-mcp."""
|
|
79
|
+
"""Initialize by delegating to chuk-mcp with timeout protection."""
|
|
52
80
|
if self._initialized:
|
|
81
|
+
logger.warning("Transport already initialized")
|
|
53
82
|
return True
|
|
54
|
-
|
|
83
|
+
|
|
84
|
+
start_time = time.time()
|
|
85
|
+
|
|
55
86
|
try:
|
|
56
87
|
logger.debug("Initializing STDIO transport...")
|
|
88
|
+
|
|
89
|
+
# Create context with timeout protection
|
|
57
90
|
self._context = stdio_client(self.server_params)
|
|
58
|
-
self._streams = await
|
|
91
|
+
self._streams = await asyncio.wait_for(
|
|
92
|
+
self._context.__aenter__(),
|
|
93
|
+
timeout=self.connection_timeout
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Send initialize message with timeout
|
|
97
|
+
init_result = await asyncio.wait_for(
|
|
98
|
+
send_initialize(*self._streams),
|
|
99
|
+
timeout=self.default_timeout
|
|
100
|
+
)
|
|
59
101
|
|
|
60
|
-
# Send initialize message
|
|
61
|
-
init_result = await send_initialize(*self._streams)
|
|
62
102
|
if init_result:
|
|
63
103
|
self._initialized = True
|
|
64
|
-
|
|
104
|
+
|
|
105
|
+
if self.enable_metrics:
|
|
106
|
+
init_time = time.time() - start_time
|
|
107
|
+
self._metrics["initialization_time"] = init_time
|
|
108
|
+
|
|
109
|
+
logger.debug("STDIO transport initialized successfully in %.3fs", time.time() - start_time)
|
|
65
110
|
return True
|
|
66
111
|
else:
|
|
112
|
+
logger.error("STDIO initialization failed")
|
|
67
113
|
await self._cleanup()
|
|
68
114
|
return False
|
|
115
|
+
|
|
116
|
+
except asyncio.TimeoutError:
|
|
117
|
+
logger.error("STDIO initialization timed out after %ss", self.connection_timeout)
|
|
118
|
+
await self._cleanup()
|
|
119
|
+
return False
|
|
69
120
|
except Exception as e:
|
|
70
121
|
logger.error("Error initializing STDIO transport: %s", e)
|
|
71
122
|
await self._cleanup()
|
|
72
123
|
return False
|
|
73
124
|
|
|
74
125
|
async def close(self) -> None:
|
|
75
|
-
"""Close by delegating to chuk-mcp context manager."""
|
|
126
|
+
"""Close by delegating to chuk-mcp context manager with enhanced cleanup."""
|
|
127
|
+
if not self._initialized:
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
# Log final metrics
|
|
131
|
+
if self.enable_metrics and self._metrics["total_calls"] > 0:
|
|
132
|
+
logger.debug(
|
|
133
|
+
"STDIO transport closing - Total calls: %d, Success rate: %.1f%%, Avg response time: %.3fs",
|
|
134
|
+
self._metrics["total_calls"],
|
|
135
|
+
(self._metrics["successful_calls"] / self._metrics["total_calls"] * 100),
|
|
136
|
+
self._metrics["avg_response_time"]
|
|
137
|
+
)
|
|
138
|
+
|
|
76
139
|
if self._context:
|
|
77
140
|
try:
|
|
78
|
-
# Simple delegation - the StreamManager now calls this in the correct context
|
|
79
141
|
await self._context.__aexit__(None, None, None)
|
|
142
|
+
logger.debug("STDIO context closed")
|
|
80
143
|
except Exception as e:
|
|
81
|
-
logger.debug("Error during close: %s", e)
|
|
144
|
+
logger.debug("Error during STDIO close: %s", e)
|
|
82
145
|
finally:
|
|
83
146
|
await self._cleanup()
|
|
84
147
|
|
|
85
148
|
async def _cleanup(self) -> None:
|
|
86
|
-
"""
|
|
149
|
+
"""Clean up internal state."""
|
|
87
150
|
self._context = None
|
|
88
151
|
self._streams = None
|
|
89
152
|
self._initialized = False
|
|
90
153
|
|
|
91
154
|
async def send_ping(self) -> bool:
|
|
92
|
-
"""
|
|
155
|
+
"""Send ping with performance tracking."""
|
|
93
156
|
if not self._initialized:
|
|
94
157
|
return False
|
|
158
|
+
|
|
159
|
+
start_time = time.time()
|
|
95
160
|
try:
|
|
96
|
-
|
|
97
|
-
|
|
161
|
+
result = await asyncio.wait_for(
|
|
162
|
+
send_ping(*self._streams),
|
|
163
|
+
timeout=self.default_timeout
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if self.enable_metrics:
|
|
167
|
+
ping_time = time.time() - start_time
|
|
168
|
+
self._metrics["last_ping_time"] = ping_time
|
|
169
|
+
logger.debug("STDIO ping completed in %.3fs: %s", ping_time, result)
|
|
170
|
+
|
|
171
|
+
return bool(result)
|
|
172
|
+
except asyncio.TimeoutError:
|
|
173
|
+
logger.error("STDIO ping timed out")
|
|
98
174
|
return False
|
|
175
|
+
except Exception as e:
|
|
176
|
+
logger.error("STDIO ping failed: %s", e)
|
|
177
|
+
if self.enable_metrics:
|
|
178
|
+
self._metrics["pipe_errors"] += 1
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
def is_connected(self) -> bool:
|
|
182
|
+
"""Check connection status."""
|
|
183
|
+
return self._initialized and self._streams is not None
|
|
99
184
|
|
|
100
185
|
async def get_tools(self) -> List[Dict[str, Any]]:
|
|
101
|
-
"""
|
|
186
|
+
"""Get tools list with performance tracking."""
|
|
102
187
|
if not self._initialized:
|
|
188
|
+
logger.error("Cannot get tools: transport not initialized")
|
|
103
189
|
return []
|
|
190
|
+
|
|
191
|
+
start_time = time.time()
|
|
104
192
|
try:
|
|
105
|
-
response = await
|
|
193
|
+
response = await asyncio.wait_for(
|
|
194
|
+
send_tools_list(*self._streams),
|
|
195
|
+
timeout=self.default_timeout
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Normalize response
|
|
106
199
|
if isinstance(response, dict):
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
200
|
+
tools = response.get("tools", [])
|
|
201
|
+
elif isinstance(response, list):
|
|
202
|
+
tools = response
|
|
203
|
+
else:
|
|
204
|
+
logger.warning("Unexpected tools response type: %s", type(response))
|
|
205
|
+
tools = []
|
|
206
|
+
|
|
207
|
+
if self.enable_metrics:
|
|
208
|
+
response_time = time.time() - start_time
|
|
209
|
+
logger.debug("Retrieved %d tools in %.3fs", len(tools), response_time)
|
|
210
|
+
|
|
211
|
+
return tools
|
|
212
|
+
|
|
213
|
+
except asyncio.TimeoutError:
|
|
214
|
+
logger.error("Get tools timed out")
|
|
215
|
+
return []
|
|
216
|
+
except Exception as e:
|
|
217
|
+
logger.error("Error getting tools: %s", e)
|
|
218
|
+
if self.enable_metrics:
|
|
219
|
+
self._metrics["pipe_errors"] += 1
|
|
110
220
|
return []
|
|
111
221
|
|
|
112
|
-
async def call_tool(self, tool_name: str, arguments: Dict[str, Any]
|
|
113
|
-
|
|
222
|
+
async def call_tool(self, tool_name: str, arguments: Dict[str, Any],
|
|
223
|
+
timeout: Optional[float] = None) -> Dict[str, Any]:
|
|
224
|
+
"""Call tool with timeout support and performance tracking."""
|
|
114
225
|
if not self._initialized:
|
|
115
226
|
return {"isError": True, "error": "Transport not initialized"}
|
|
227
|
+
|
|
228
|
+
tool_timeout = timeout or self.default_timeout
|
|
229
|
+
start_time = time.time()
|
|
116
230
|
|
|
231
|
+
if self.enable_metrics:
|
|
232
|
+
self._metrics["total_calls"] += 1 # FIXED: INCREMENT FIRST
|
|
233
|
+
|
|
117
234
|
try:
|
|
118
|
-
|
|
119
|
-
|
|
235
|
+
logger.debug("Calling tool '%s' with timeout %ss", tool_name, tool_timeout)
|
|
236
|
+
|
|
237
|
+
response = await asyncio.wait_for(
|
|
238
|
+
send_tools_call(*self._streams, tool_name, arguments),
|
|
239
|
+
timeout=tool_timeout
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
response_time = time.time() - start_time
|
|
243
|
+
result = self._normalize_mcp_response(response)
|
|
244
|
+
|
|
245
|
+
if self.enable_metrics:
|
|
246
|
+
self._update_metrics(response_time, not result.get("isError", False))
|
|
247
|
+
|
|
248
|
+
if not result.get("isError", False):
|
|
249
|
+
logger.debug("Tool '%s' completed successfully in %.3fs", tool_name, response_time)
|
|
250
|
+
else:
|
|
251
|
+
logger.warning("Tool '%s' failed in %.3fs: %s", tool_name, response_time, result.get('error', 'Unknown error'))
|
|
252
|
+
|
|
253
|
+
return result
|
|
254
|
+
|
|
255
|
+
except asyncio.TimeoutError:
|
|
256
|
+
response_time = time.time() - start_time
|
|
257
|
+
if self.enable_metrics:
|
|
258
|
+
self._update_metrics(response_time, False)
|
|
259
|
+
|
|
260
|
+
error_msg = f"Tool execution timed out after {tool_timeout}s"
|
|
261
|
+
logger.error("Tool '%s' %s", tool_name, error_msg)
|
|
262
|
+
return {
|
|
263
|
+
"isError": True,
|
|
264
|
+
"error": error_msg
|
|
265
|
+
}
|
|
120
266
|
except Exception as e:
|
|
121
|
-
|
|
267
|
+
response_time = time.time() - start_time
|
|
268
|
+
if self.enable_metrics:
|
|
269
|
+
self._update_metrics(response_time, False)
|
|
270
|
+
self._metrics["pipe_errors"] += 1
|
|
271
|
+
|
|
272
|
+
error_msg = f"Tool execution failed: {str(e)}"
|
|
273
|
+
logger.error("Tool '%s' error: %s", tool_name, error_msg)
|
|
274
|
+
return {
|
|
275
|
+
"isError": True,
|
|
276
|
+
"error": error_msg
|
|
277
|
+
}
|
|
122
278
|
|
|
123
|
-
def
|
|
124
|
-
"""
|
|
279
|
+
def _update_metrics(self, response_time: float, success: bool) -> None:
|
|
280
|
+
"""Update performance metrics."""
|
|
281
|
+
if success:
|
|
282
|
+
self._metrics["successful_calls"] += 1
|
|
283
|
+
else:
|
|
284
|
+
self._metrics["failed_calls"] += 1
|
|
285
|
+
|
|
286
|
+
self._metrics["total_time"] += response_time
|
|
287
|
+
# FIXED: Only calculate average if we have total calls
|
|
288
|
+
if self._metrics["total_calls"] > 0:
|
|
289
|
+
self._metrics["avg_response_time"] = (
|
|
290
|
+
self._metrics["total_time"] / self._metrics["total_calls"]
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def _normalize_mcp_response(self, response: Dict[str, Any]) -> Dict[str, Any]:
|
|
294
|
+
"""
|
|
295
|
+
Normalize response using shared base class logic with STDIO-specific handling.
|
|
296
|
+
|
|
297
|
+
STDIO has special requirements for preserving string representations
|
|
298
|
+
of numeric values for backward compatibility.
|
|
299
|
+
"""
|
|
300
|
+
# Handle explicit error in response
|
|
125
301
|
if "error" in response:
|
|
126
302
|
error_info = response["error"]
|
|
127
303
|
error_msg = error_info.get("message", str(error_info)) if isinstance(error_info, dict) else str(error_info)
|
|
128
304
|
return {"isError": True, "error": error_msg}
|
|
129
305
|
|
|
306
|
+
# Handle successful response with result
|
|
130
307
|
if "result" in response:
|
|
131
308
|
result = response["result"]
|
|
132
309
|
if isinstance(result, dict) and "content" in result:
|
|
133
|
-
return {"isError": False, "content": self.
|
|
310
|
+
return {"isError": False, "content": self._extract_stdio_content(result["content"])}
|
|
134
311
|
return {"isError": False, "content": result}
|
|
135
312
|
|
|
313
|
+
# Handle direct content-based response
|
|
136
314
|
if "content" in response:
|
|
137
|
-
return {"isError": False, "content": self.
|
|
315
|
+
return {"isError": False, "content": self._extract_stdio_content(response["content"])}
|
|
138
316
|
|
|
139
317
|
return {"isError": False, "content": response}
|
|
140
318
|
|
|
141
|
-
def
|
|
319
|
+
def _extract_stdio_content(self, content_list: Any) -> Any:
|
|
142
320
|
"""
|
|
143
|
-
|
|
321
|
+
Extract content with STDIO-specific string preservation logic.
|
|
144
322
|
|
|
145
|
-
|
|
146
|
-
|
|
323
|
+
STDIO transport preserves string representations of numeric values
|
|
324
|
+
for backward compatibility with existing tests.
|
|
147
325
|
"""
|
|
148
326
|
if not isinstance(content_list, list) or not content_list:
|
|
149
327
|
return content_list
|
|
@@ -153,12 +331,11 @@ class StdioTransport(MCPBaseTransport):
|
|
|
153
331
|
if isinstance(item, dict) and item.get("type") == "text":
|
|
154
332
|
text = item.get("text", "")
|
|
155
333
|
|
|
156
|
-
#
|
|
157
|
-
# if they look like simple values (numbers, booleans, etc.)
|
|
334
|
+
# STDIO-specific: preserve string format for numeric values
|
|
158
335
|
try:
|
|
159
336
|
parsed = json.loads(text)
|
|
160
337
|
# If the parsed result is a simple type and the original was a string,
|
|
161
|
-
# keep it as a string to maintain
|
|
338
|
+
# keep it as a string to maintain compatibility
|
|
162
339
|
if isinstance(parsed, (int, float, bool)) and isinstance(text, str):
|
|
163
340
|
# Check if this looks like a simple numeric string
|
|
164
341
|
if text.strip().isdigit() or (text.strip().replace('.', '', 1).isdigit()):
|
|
@@ -170,67 +347,106 @@ class StdioTransport(MCPBaseTransport):
|
|
|
170
347
|
|
|
171
348
|
return content_list
|
|
172
349
|
|
|
173
|
-
# Optional features
|
|
174
350
|
async def list_resources(self) -> Dict[str, Any]:
|
|
175
|
-
"""
|
|
176
|
-
if not
|
|
351
|
+
"""List resources with error handling."""
|
|
352
|
+
if not self._initialized:
|
|
177
353
|
return {}
|
|
178
354
|
try:
|
|
179
|
-
response = await
|
|
355
|
+
response = await asyncio.wait_for(
|
|
356
|
+
send_resources_list(*self._streams),
|
|
357
|
+
timeout=self.default_timeout
|
|
358
|
+
)
|
|
180
359
|
return response if isinstance(response, dict) else {}
|
|
181
|
-
except
|
|
360
|
+
except asyncio.TimeoutError:
|
|
361
|
+
logger.error("List resources timed out")
|
|
362
|
+
return {}
|
|
363
|
+
except Exception as e:
|
|
364
|
+
logger.debug("Error listing resources: %s", e)
|
|
182
365
|
return {}
|
|
183
366
|
|
|
184
|
-
async def
|
|
185
|
-
"""
|
|
186
|
-
if not
|
|
367
|
+
async def list_prompts(self) -> Dict[str, Any]:
|
|
368
|
+
"""List prompts with error handling."""
|
|
369
|
+
if not self._initialized:
|
|
187
370
|
return {}
|
|
188
371
|
try:
|
|
189
|
-
response = await
|
|
372
|
+
response = await asyncio.wait_for(
|
|
373
|
+
send_prompts_list(*self._streams),
|
|
374
|
+
timeout=self.default_timeout
|
|
375
|
+
)
|
|
190
376
|
return response if isinstance(response, dict) else {}
|
|
191
|
-
except
|
|
377
|
+
except asyncio.TimeoutError:
|
|
378
|
+
logger.error("List prompts timed out")
|
|
379
|
+
return {}
|
|
380
|
+
except Exception as e:
|
|
381
|
+
logger.debug("Error listing prompts: %s", e)
|
|
192
382
|
return {}
|
|
193
383
|
|
|
194
|
-
async def
|
|
195
|
-
"""
|
|
196
|
-
if not
|
|
384
|
+
async def read_resource(self, uri: str) -> Dict[str, Any]:
|
|
385
|
+
"""Read a specific resource."""
|
|
386
|
+
if not self._initialized:
|
|
197
387
|
return {}
|
|
198
388
|
try:
|
|
199
|
-
response = await
|
|
389
|
+
response = await asyncio.wait_for(
|
|
390
|
+
send_resources_read(*self._streams, uri),
|
|
391
|
+
timeout=self.default_timeout
|
|
392
|
+
)
|
|
200
393
|
return response if isinstance(response, dict) else {}
|
|
201
|
-
except Exception:
|
|
394
|
+
except Exception as e:
|
|
395
|
+
logger.debug("Error reading resource: %s", e)
|
|
202
396
|
return {}
|
|
203
397
|
|
|
204
398
|
async def get_prompt(self, name: str, arguments: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
205
|
-
"""
|
|
206
|
-
if not
|
|
399
|
+
"""Get a specific prompt."""
|
|
400
|
+
if not self._initialized:
|
|
207
401
|
return {}
|
|
208
402
|
try:
|
|
209
|
-
response = await
|
|
403
|
+
response = await asyncio.wait_for(
|
|
404
|
+
send_prompts_get(*self._streams, name, arguments or {}),
|
|
405
|
+
timeout=self.default_timeout
|
|
406
|
+
)
|
|
210
407
|
return response if isinstance(response, dict) else {}
|
|
211
|
-
except Exception:
|
|
408
|
+
except Exception as e:
|
|
409
|
+
logger.debug("Error getting prompt: %s", e)
|
|
212
410
|
return {}
|
|
213
411
|
|
|
214
|
-
#
|
|
412
|
+
# ------------------------------------------------------------------ #
|
|
413
|
+
# Metrics and monitoring (now consistent with other transports) #
|
|
414
|
+
# ------------------------------------------------------------------ #
|
|
415
|
+
def get_metrics(self) -> Dict[str, Any]:
|
|
416
|
+
"""Get performance and connection metrics."""
|
|
417
|
+
return self._metrics.copy()
|
|
418
|
+
|
|
419
|
+
def reset_metrics(self) -> None:
|
|
420
|
+
"""Reset performance metrics."""
|
|
421
|
+
self._metrics = {
|
|
422
|
+
"total_calls": 0,
|
|
423
|
+
"successful_calls": 0,
|
|
424
|
+
"failed_calls": 0,
|
|
425
|
+
"total_time": 0.0,
|
|
426
|
+
"avg_response_time": 0.0,
|
|
427
|
+
"last_ping_time": self._metrics.get("last_ping_time"),
|
|
428
|
+
"initialization_time": self._metrics.get("initialization_time"),
|
|
429
|
+
"process_restarts": self._metrics.get("process_restarts", 0),
|
|
430
|
+
"pipe_errors": 0
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
# ------------------------------------------------------------------ #
|
|
434
|
+
# Backward compatibility #
|
|
435
|
+
# ------------------------------------------------------------------ #
|
|
215
436
|
def get_streams(self) -> List[tuple]:
|
|
216
437
|
"""Provide streams for backward compatibility."""
|
|
217
438
|
return [self._streams] if self._streams else []
|
|
218
439
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
440
|
+
# ------------------------------------------------------------------ #
|
|
441
|
+
# Context manager support (now uses base class with fixed error) #
|
|
442
|
+
# ------------------------------------------------------------------ #
|
|
223
443
|
async def __aenter__(self):
|
|
224
444
|
"""Context manager support."""
|
|
225
|
-
|
|
226
|
-
|
|
445
|
+
success = await self.initialize()
|
|
446
|
+
if not success:
|
|
447
|
+
raise RuntimeError("Failed to initialize StdioTransport") # FIXED: message
|
|
227
448
|
return self
|
|
228
449
|
|
|
229
450
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
230
451
|
"""Context manager cleanup."""
|
|
231
|
-
await self.close()
|
|
232
|
-
|
|
233
|
-
def __repr__(self) -> str:
|
|
234
|
-
"""String representation."""
|
|
235
|
-
status = "initialized" if self._initialized else "not initialized"
|
|
236
|
-
return f"StdioTransport(status={status}, command={getattr(self.server_params, 'command', 'unknown')})"
|
|
452
|
+
await self.close()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chuk-tool-processor
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.9
|
|
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>
|
|
@@ -22,12 +22,12 @@ chuk_tool_processor/mcp/register_mcp_tools.py,sha256=s6mQMtZr7dswT2WXDJ84zjOTSi3
|
|
|
22
22
|
chuk_tool_processor/mcp/setup_mcp_http_streamable.py,sha256=ZJUAj7LL4CRfc-CBl0SQJk0qfW12IuixR-7J2hbQ8S8,4538
|
|
23
23
|
chuk_tool_processor/mcp/setup_mcp_sse.py,sha256=4nf0V6cykAPLxtgsl8RTAYQdVWITUNu_3CIU1vcLjlo,3795
|
|
24
24
|
chuk_tool_processor/mcp/setup_mcp_stdio.py,sha256=L8anrx_b5HDsaMqAfbpWaHex084DTd76W8WBf3ClC48,2884
|
|
25
|
-
chuk_tool_processor/mcp/stream_manager.py,sha256=
|
|
26
|
-
chuk_tool_processor/mcp/transport/__init__.py,sha256=
|
|
27
|
-
chuk_tool_processor/mcp/transport/base_transport.py,sha256=
|
|
28
|
-
chuk_tool_processor/mcp/transport/http_streamable_transport.py,sha256=
|
|
29
|
-
chuk_tool_processor/mcp/transport/sse_transport.py,sha256=
|
|
30
|
-
chuk_tool_processor/mcp/transport/stdio_transport.py,sha256=
|
|
25
|
+
chuk_tool_processor/mcp/stream_manager.py,sha256=sRy6X893ISht4XUFVIjUHQ0zVisyP7ex2s2wiPQv2fg,33858
|
|
26
|
+
chuk_tool_processor/mcp/transport/__init__.py,sha256=2IZHt5x8GD7jgPmt5JvUIhSBij4kf9vZta1rL_-UDMc,657
|
|
27
|
+
chuk_tool_processor/mcp/transport/base_transport.py,sha256=EapglrjRuSniGkEoEeL76ZGx_VriHqR1coK32nFMrd8,9175
|
|
28
|
+
chuk_tool_processor/mcp/transport/http_streamable_transport.py,sha256=FkOlZDROLrRdPV5e3UeEwHlUbQlMK9eLaK8LbVcnnOo,18372
|
|
29
|
+
chuk_tool_processor/mcp/transport/sse_transport.py,sha256=zE91K48HNcqcyiUL8nEn-9oDDpR-rHi483WgZ3f-CSA,22684
|
|
30
|
+
chuk_tool_processor/mcp/transport/stdio_transport.py,sha256=I64dYt3ZXGmWMmTegdspKZcLggoNAdvxfqh-z8tAO1s,17683
|
|
31
31
|
chuk_tool_processor/models/__init__.py,sha256=TC__rdVa0lQsmJHM_hbLDPRgToa_pQT_UxRcPZk6iVw,40
|
|
32
32
|
chuk_tool_processor/models/execution_strategy.py,sha256=UVW35YIeMY2B3mpIKZD2rAkyOPayI6ckOOUALyf0YiQ,2115
|
|
33
33
|
chuk_tool_processor/models/streaming_tool.py,sha256=0v2PSPTgZ5TS_PpVdohvVhh99fPwPQM_R_z4RU0mlLM,3541
|
|
@@ -54,7 +54,7 @@ chuk_tool_processor/registry/providers/__init__.py,sha256=eigwG_So11j7WbDGSWaKd3
|
|
|
54
54
|
chuk_tool_processor/registry/providers/memory.py,sha256=6cMtUwLO6zrk3pguQRgxJ2CReHAzewgZsizWZhsoStk,5184
|
|
55
55
|
chuk_tool_processor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
56
|
chuk_tool_processor/utils/validation.py,sha256=V5N1dH9sJlHepFIbiI2k2MU82o7nvnh0hKyIt2jdgww,4136
|
|
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.9.dist-info/METADATA,sha256=ywTwAgW9KZmQxNtELdMvGenXaiFNvjflyvI3oevrbuY,23463
|
|
58
|
+
chuk_tool_processor-0.6.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
59
|
+
chuk_tool_processor-0.6.9.dist-info/top_level.txt,sha256=7lTsnuRx4cOW4U2sNJWNxl4ZTt_J1ndkjTbj3pHPY5M,20
|
|
60
|
+
chuk_tool_processor-0.6.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|