mcp-use 1.3.0__py3-none-any.whl → 1.3.1__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 mcp-use might be problematic. Click here for more details.
- mcp_use/agents/mcpagent.py +109 -10
- mcp_use/client.py +28 -11
- mcp_use/config.py +9 -9
- mcp_use/connectors/base.py +135 -37
- mcp_use/connectors/http.py +108 -30
- mcp_use/connectors/sandbox.py +6 -1
- mcp_use/connectors/stdio.py +7 -2
- mcp_use/connectors/websocket.py +7 -2
- mcp_use/session.py +1 -4
- mcp_use/task_managers/__init__.py +2 -1
- mcp_use/task_managers/base.py +10 -4
- mcp_use/task_managers/streamable_http.py +81 -0
- mcp_use/task_managers/websocket.py +5 -0
- mcp_use/telemetry/__init__.py +0 -0
- mcp_use/telemetry/events.py +93 -0
- mcp_use/telemetry/posthog.py +214 -0
- mcp_use/telemetry/utils.py +48 -0
- mcp_use/utils.py +27 -0
- {mcp_use-1.3.0.dist-info → mcp_use-1.3.1.dist-info}/METADATA +73 -23
- {mcp_use-1.3.0.dist-info → mcp_use-1.3.1.dist-info}/RECORD +22 -17
- mcp_use/types/clientoptions.py +0 -23
- {mcp_use-1.3.0.dist-info → mcp_use-1.3.1.dist-info}/WHEEL +0 -0
- {mcp_use-1.3.0.dist-info → mcp_use-1.3.1.dist-info}/licenses/LICENSE +0 -0
mcp_use/connectors/http.py
CHANGED
|
@@ -2,20 +2,21 @@
|
|
|
2
2
|
HTTP connector for MCP implementations.
|
|
3
3
|
|
|
4
4
|
This module provides a connector for communicating with MCP implementations
|
|
5
|
-
through HTTP APIs with SSE for transport.
|
|
5
|
+
through HTTP APIs with SSE or Streamable HTTP for transport.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import httpx
|
|
8
9
|
from mcp import ClientSession
|
|
9
10
|
|
|
10
11
|
from ..logging import logger
|
|
11
|
-
from ..task_managers import SseConnectionManager
|
|
12
|
+
from ..task_managers import ConnectionManager, SseConnectionManager, StreamableHttpConnectionManager
|
|
12
13
|
from .base import BaseConnector
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class HttpConnector(BaseConnector):
|
|
16
|
-
"""Connector for MCP implementations using HTTP transport with SSE.
|
|
17
|
+
"""Connector for MCP implementations using HTTP transport with SSE or streamable HTTP.
|
|
17
18
|
|
|
18
|
-
This connector uses HTTP/SSE to communicate with remote MCP implementations,
|
|
19
|
+
This connector uses HTTP/SSE or streamable HTTP to communicate with remote MCP implementations,
|
|
19
20
|
using a connection manager to handle the proper lifecycle management.
|
|
20
21
|
"""
|
|
21
22
|
|
|
@@ -45,38 +46,115 @@ class HttpConnector(BaseConnector):
|
|
|
45
46
|
self.timeout = timeout
|
|
46
47
|
self.sse_read_timeout = sse_read_timeout
|
|
47
48
|
|
|
49
|
+
async def _setup_client(self, connection_manager: ConnectionManager) -> None:
|
|
50
|
+
"""Set up the client session with the provided connection manager."""
|
|
51
|
+
|
|
52
|
+
self._connection_manager = connection_manager
|
|
53
|
+
read_stream, write_stream = await self._connection_manager.start()
|
|
54
|
+
self.client_session = ClientSession(read_stream, write_stream, sampling_callback=None)
|
|
55
|
+
await self.client_session.__aenter__()
|
|
56
|
+
|
|
48
57
|
async def connect(self) -> None:
|
|
49
58
|
"""Establish a connection to the MCP implementation."""
|
|
50
59
|
if self._connected:
|
|
51
60
|
logger.debug("Already connected to MCP implementation")
|
|
52
61
|
return
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# Create and start the connection manager
|
|
60
|
-
self._connection_manager = SseConnectionManager(
|
|
61
|
-
sse_url, self.headers, self.timeout, self.sse_read_timeout
|
|
62
|
-
)
|
|
63
|
-
read_stream, write_stream = await self._connection_manager.start()
|
|
63
|
+
# Try streamable HTTP first (new transport), fall back to SSE (old transport)
|
|
64
|
+
# This implements backwards compatibility per MCP specification
|
|
65
|
+
self.transport_type = None
|
|
66
|
+
connection_manager = None
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
self._connected = True
|
|
71
|
-
logger.debug(
|
|
72
|
-
f"Successfully connected to MCP implementation via HTTP/SSE: {self.base_url}"
|
|
68
|
+
try:
|
|
69
|
+
# First, try the new streamable HTTP transport
|
|
70
|
+
logger.debug(f"Attempting streamable HTTP connection to: {self.base_url}")
|
|
71
|
+
connection_manager = StreamableHttpConnectionManager(
|
|
72
|
+
self.base_url, self.headers, self.timeout, self.sse_read_timeout
|
|
73
73
|
)
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
#
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
# Test if this is a streamable HTTP server by attempting initialization
|
|
76
|
+
read_stream, write_stream = await connection_manager.start()
|
|
77
|
+
|
|
78
|
+
# Test if this actually works by trying to create a client session and initialize it
|
|
79
|
+
test_client = ClientSession(read_stream, write_stream, sampling_callback=None)
|
|
80
|
+
await test_client.__aenter__()
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
# Try to initialize - this is where streamable HTTP vs SSE difference should show up
|
|
84
|
+
await test_client.initialize()
|
|
85
|
+
|
|
86
|
+
# If we get here, streamable HTTP works
|
|
87
|
+
|
|
88
|
+
self.client_session = test_client
|
|
89
|
+
self.transport_type = "streamable HTTP"
|
|
90
|
+
|
|
91
|
+
except Exception as init_error:
|
|
92
|
+
# Clean up the test client
|
|
93
|
+
try:
|
|
94
|
+
await test_client.__aexit__(None, None, None)
|
|
95
|
+
except Exception:
|
|
96
|
+
pass
|
|
97
|
+
raise init_error
|
|
98
|
+
|
|
99
|
+
except Exception as streamable_error:
|
|
100
|
+
logger.debug(f"Streamable HTTP failed: {streamable_error}")
|
|
101
|
+
|
|
102
|
+
# Clean up the failed streamable HTTP connection manager
|
|
103
|
+
if connection_manager:
|
|
104
|
+
try:
|
|
105
|
+
await connection_manager.close()
|
|
106
|
+
except Exception:
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
# Check if this is a 4xx error that indicates we should try SSE fallback
|
|
110
|
+
should_fallback = False
|
|
111
|
+
if isinstance(streamable_error, httpx.HTTPStatusError):
|
|
112
|
+
if streamable_error.response.status_code in [404, 405]:
|
|
113
|
+
should_fallback = True
|
|
114
|
+
elif "405 Method Not Allowed" in str(streamable_error) or "404 Not Found" in str(
|
|
115
|
+
streamable_error
|
|
116
|
+
):
|
|
117
|
+
should_fallback = True
|
|
118
|
+
else:
|
|
119
|
+
# For other errors, still try fallback but they might indicate
|
|
120
|
+
# real connectivity issues
|
|
121
|
+
should_fallback = True
|
|
122
|
+
|
|
123
|
+
if should_fallback:
|
|
124
|
+
try:
|
|
125
|
+
# Fall back to the old SSE transport
|
|
126
|
+
logger.debug(f"Attempting SSE fallback connection to: {self.base_url}")
|
|
127
|
+
connection_manager = SseConnectionManager(
|
|
128
|
+
self.base_url, self.headers, self.timeout, self.sse_read_timeout
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
read_stream, write_stream = await connection_manager.start()
|
|
132
|
+
|
|
133
|
+
# Create the client session for SSE
|
|
134
|
+
self.client_session = ClientSession(
|
|
135
|
+
read_stream, write_stream, sampling_callback=None
|
|
136
|
+
)
|
|
137
|
+
await self.client_session.__aenter__()
|
|
138
|
+
self.transport_type = "SSE"
|
|
139
|
+
|
|
140
|
+
except Exception as sse_error:
|
|
141
|
+
logger.error(
|
|
142
|
+
f"Both transport methods failed. Streamable HTTP: {streamable_error}, "
|
|
143
|
+
f"SSE: {sse_error}"
|
|
144
|
+
)
|
|
145
|
+
raise sse_error
|
|
146
|
+
else:
|
|
147
|
+
raise streamable_error
|
|
148
|
+
|
|
149
|
+
# Store the successful connection manager and mark as connected
|
|
150
|
+
self._connection_manager = connection_manager
|
|
151
|
+
self._connected = True
|
|
152
|
+
logger.debug(
|
|
153
|
+
f"Successfully connected to MCP implementation via"
|
|
154
|
+
f" {self.transport_type}: {self.base_url}"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def public_identifier(self) -> str:
|
|
159
|
+
"""Get the identifier for the connector."""
|
|
160
|
+
return {"type": self.transport_type, "base_url": self.base_url}
|
mcp_use/connectors/sandbox.py
CHANGED
|
@@ -79,7 +79,7 @@ class SandboxConnector(BaseConnector):
|
|
|
79
79
|
self.api_key = _e2b_options.get("api_key") or os.environ.get("E2B_API_KEY")
|
|
80
80
|
if not self.api_key:
|
|
81
81
|
raise ValueError(
|
|
82
|
-
"E2B API key is required. Provide it via '
|
|
82
|
+
"E2B API key is required. Provide it via 'sandbox_options.api_key' "
|
|
83
83
|
"or the E2B_API_KEY environment variable."
|
|
84
84
|
)
|
|
85
85
|
|
|
@@ -289,3 +289,8 @@ class SandboxConnector(BaseConnector):
|
|
|
289
289
|
await self._cleanup_resources()
|
|
290
290
|
self._connected = False
|
|
291
291
|
logger.debug("Disconnected from MCP implementation")
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def public_identifier(self) -> str:
|
|
295
|
+
"""Get the identifier for the connector."""
|
|
296
|
+
return {"type": "sandbox", "command": self.user_command, "args": self.user_args}
|
mcp_use/connectors/stdio.py
CHANGED
|
@@ -61,8 +61,8 @@ class StdioConnector(BaseConnector):
|
|
|
61
61
|
read_stream, write_stream = await self._connection_manager.start()
|
|
62
62
|
|
|
63
63
|
# Create the client session
|
|
64
|
-
self.
|
|
65
|
-
await self.
|
|
64
|
+
self.client_session = ClientSession(read_stream, write_stream, sampling_callback=None)
|
|
65
|
+
await self.client_session.__aenter__()
|
|
66
66
|
|
|
67
67
|
# Mark as connected
|
|
68
68
|
self._connected = True
|
|
@@ -76,3 +76,8 @@ class StdioConnector(BaseConnector):
|
|
|
76
76
|
|
|
77
77
|
# Re-raise the original exception
|
|
78
78
|
raise
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def public_identifier(self) -> str:
|
|
82
|
+
"""Get the identifier for the connector."""
|
|
83
|
+
return {"type": "stdio", "command&args": f"{self.command} {' '.join(self.args)}"}
|
mcp_use/connectors/websocket.py
CHANGED
|
@@ -11,7 +11,7 @@ import uuid
|
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
13
|
from mcp.types import Tool
|
|
14
|
-
from websockets
|
|
14
|
+
from websockets import ClientConnection
|
|
15
15
|
|
|
16
16
|
from ..logging import logger
|
|
17
17
|
from ..task_managers import ConnectionManager, WebSocketConnectionManager
|
|
@@ -44,7 +44,7 @@ class WebSocketConnector(BaseConnector):
|
|
|
44
44
|
if auth_token:
|
|
45
45
|
self.headers["Authorization"] = f"Bearer {auth_token}"
|
|
46
46
|
|
|
47
|
-
self.ws:
|
|
47
|
+
self.ws: ClientConnection | None = None
|
|
48
48
|
self._connection_manager: ConnectionManager | None = None
|
|
49
49
|
self._receiver_task: asyncio.Task | None = None
|
|
50
50
|
self.pending_requests: dict[str, asyncio.Future] = {}
|
|
@@ -243,3 +243,8 @@ class WebSocketConnector(BaseConnector):
|
|
|
243
243
|
"""Send a raw request to the MCP implementation."""
|
|
244
244
|
logger.debug(f"Sending request: {method} with params: {params}")
|
|
245
245
|
return await self._send_request(method, params)
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def public_identifier(self) -> str:
|
|
249
|
+
"""Get the identifier for the connector."""
|
|
250
|
+
return {"type": "websocket", "url": self.url}
|
mcp_use/session.py
CHANGED
|
@@ -7,8 +7,6 @@ which handles authentication, initialization, and tool discovery.
|
|
|
7
7
|
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
|
-
from mcp.types import Tool
|
|
11
|
-
|
|
12
10
|
from .connectors.base import BaseConnector
|
|
13
11
|
|
|
14
12
|
|
|
@@ -32,7 +30,6 @@ class MCPSession:
|
|
|
32
30
|
"""
|
|
33
31
|
self.connector = connector
|
|
34
32
|
self.session_info: dict[str, Any] | None = None
|
|
35
|
-
self.tools: list[Tool] = []
|
|
36
33
|
self.auto_connect = auto_connect
|
|
37
34
|
|
|
38
35
|
async def __aenter__(self) -> "MCPSession":
|
|
@@ -84,4 +81,4 @@ class MCPSession:
|
|
|
84
81
|
Returns:
|
|
85
82
|
True if the connector is connected, False otherwise.
|
|
86
83
|
"""
|
|
87
|
-
return
|
|
84
|
+
return self.connector.is_connected
|
|
@@ -8,12 +8,13 @@ through different transport mechanisms.
|
|
|
8
8
|
from .base import ConnectionManager
|
|
9
9
|
from .sse import SseConnectionManager
|
|
10
10
|
from .stdio import StdioConnectionManager
|
|
11
|
+
from .streamable_http import StreamableHttpConnectionManager
|
|
11
12
|
from .websocket import WebSocketConnectionManager
|
|
12
13
|
|
|
13
14
|
__all__ = [
|
|
14
15
|
"ConnectionManager",
|
|
15
|
-
"HttpConnectionManager",
|
|
16
16
|
"StdioConnectionManager",
|
|
17
17
|
"WebSocketConnectionManager",
|
|
18
18
|
"SseConnectionManager",
|
|
19
|
+
"StreamableHttpConnectionManager",
|
|
19
20
|
]
|
mcp_use/task_managers/base.py
CHANGED
|
@@ -70,9 +70,7 @@ class ConnectionManager(Generic[T], ABC):
|
|
|
70
70
|
self._exception = None
|
|
71
71
|
|
|
72
72
|
# Create a task to establish and maintain the connection
|
|
73
|
-
self._task = asyncio.create_task(
|
|
74
|
-
self._connection_task(), name=f"{self.__class__.__name__}_task"
|
|
75
|
-
)
|
|
73
|
+
self._task = asyncio.create_task(self._connection_task(), name=f"{self.__class__.__name__}_task")
|
|
76
74
|
|
|
77
75
|
# Wait for the connection to be ready or fail
|
|
78
76
|
await self._ready_event.wait()
|
|
@@ -105,6 +103,14 @@ class ConnectionManager(Generic[T], ABC):
|
|
|
105
103
|
await self._done_event.wait()
|
|
106
104
|
logger.debug(f"{self.__class__.__name__} task completed")
|
|
107
105
|
|
|
106
|
+
def get_streams(self) -> T | None:
|
|
107
|
+
"""Get the current connection streams.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
The current connection (typically a tuple of read_stream, write_stream) or None if not connected.
|
|
111
|
+
"""
|
|
112
|
+
return self._connection
|
|
113
|
+
|
|
108
114
|
async def _connection_task(self) -> None:
|
|
109
115
|
"""Run the connection task.
|
|
110
116
|
|
|
@@ -137,7 +143,7 @@ class ConnectionManager(Generic[T], ABC):
|
|
|
137
143
|
self._ready_event.set()
|
|
138
144
|
|
|
139
145
|
finally:
|
|
140
|
-
# Close the connection if it was
|
|
146
|
+
# Close the connection if it was established
|
|
141
147
|
if self._connection is not None:
|
|
142
148
|
try:
|
|
143
149
|
await self._close_connection()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Streamable HTTP connection management for MCP implementations.
|
|
3
|
+
|
|
4
|
+
This module provides a connection manager for streamable HTTP-based MCP connections
|
|
5
|
+
that ensures proper task isolation and resource cleanup.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from datetime import timedelta
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from mcp.client.streamable_http import streamablehttp_client
|
|
12
|
+
|
|
13
|
+
from ..logging import logger
|
|
14
|
+
from .base import ConnectionManager
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class StreamableHttpConnectionManager(ConnectionManager[tuple[Any, Any]]):
|
|
18
|
+
"""Connection manager for streamable HTTP-based MCP connections.
|
|
19
|
+
|
|
20
|
+
This class handles the proper task isolation for HTTP streaming connections
|
|
21
|
+
to prevent the "cancel scope in different task" error. It runs the http_stream_client
|
|
22
|
+
in a dedicated task and manages its lifecycle.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
url: str,
|
|
28
|
+
headers: dict[str, str] | None = None,
|
|
29
|
+
timeout: float = 5,
|
|
30
|
+
read_timeout: float = 60 * 5,
|
|
31
|
+
):
|
|
32
|
+
"""Initialize a new streamable HTTP connection manager.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
url: The HTTP endpoint URL
|
|
36
|
+
headers: Optional HTTP headers
|
|
37
|
+
timeout: Timeout for HTTP operations in seconds
|
|
38
|
+
read_timeout: Timeout for HTTP read operations in seconds
|
|
39
|
+
"""
|
|
40
|
+
super().__init__()
|
|
41
|
+
self.url = url
|
|
42
|
+
self.headers = headers or {}
|
|
43
|
+
self.timeout = timedelta(seconds=timeout)
|
|
44
|
+
self.read_timeout = timedelta(seconds=read_timeout)
|
|
45
|
+
self._http_ctx = None
|
|
46
|
+
|
|
47
|
+
async def _establish_connection(self) -> tuple[Any, Any]:
|
|
48
|
+
"""Establish a streamable HTTP connection.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
A tuple of (read_stream, write_stream)
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
Exception: If connection cannot be established.
|
|
55
|
+
"""
|
|
56
|
+
# Create the context manager
|
|
57
|
+
self._http_ctx = streamablehttp_client(
|
|
58
|
+
url=self.url,
|
|
59
|
+
headers=self.headers,
|
|
60
|
+
timeout=self.timeout,
|
|
61
|
+
sse_read_timeout=self.read_timeout,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Enter the context manager. Ignoring the session id callback
|
|
65
|
+
read_stream, write_stream, _ = await self._http_ctx.__aenter__()
|
|
66
|
+
|
|
67
|
+
# Return the streams
|
|
68
|
+
return (read_stream, write_stream)
|
|
69
|
+
|
|
70
|
+
async def _close_connection(self) -> None:
|
|
71
|
+
"""Close the streamable HTTP connection."""
|
|
72
|
+
|
|
73
|
+
if self._http_ctx:
|
|
74
|
+
# Exit the context manager
|
|
75
|
+
try:
|
|
76
|
+
await self._http_ctx.__aexit__(None, None, None)
|
|
77
|
+
except Exception as e:
|
|
78
|
+
# Only log if it's not a normal connection termination
|
|
79
|
+
logger.debug(f"Streamable HTTP context cleanup: {e}")
|
|
80
|
+
finally:
|
|
81
|
+
self._http_ctx = None
|
|
@@ -22,14 +22,17 @@ class WebSocketConnectionManager(ConnectionManager[tuple[Any, Any]]):
|
|
|
22
22
|
def __init__(
|
|
23
23
|
self,
|
|
24
24
|
url: str,
|
|
25
|
+
headers: dict[str, str] | None = None,
|
|
25
26
|
):
|
|
26
27
|
"""Initialize a new WebSocket connection manager.
|
|
27
28
|
|
|
28
29
|
Args:
|
|
29
30
|
url: The WebSocket URL to connect to
|
|
31
|
+
headers: Optional HTTP headers
|
|
30
32
|
"""
|
|
31
33
|
super().__init__()
|
|
32
34
|
self.url = url
|
|
35
|
+
self.headers = headers or {}
|
|
33
36
|
|
|
34
37
|
async def _establish_connection(self) -> tuple[Any, Any]:
|
|
35
38
|
"""Establish a WebSocket connection.
|
|
@@ -42,6 +45,8 @@ class WebSocketConnectionManager(ConnectionManager[tuple[Any, Any]]):
|
|
|
42
45
|
"""
|
|
43
46
|
logger.debug(f"Connecting to WebSocket: {self.url}")
|
|
44
47
|
# Create the context manager
|
|
48
|
+
# Note: The current MCP websocket_client implementation doesn't support headers
|
|
49
|
+
# If headers need to be passed, this would need to be updated when MCP supports it
|
|
45
50
|
self._ws_ctx = websocket_client(self.url)
|
|
46
51
|
|
|
47
52
|
# Enter the context manager
|
|
File without changes
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseTelemetryEvent(ABC):
|
|
7
|
+
"""Base class for all telemetry events"""
|
|
8
|
+
|
|
9
|
+
@property
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def name(self) -> str:
|
|
12
|
+
"""Event name for tracking"""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def properties(self) -> dict[str, Any]:
|
|
18
|
+
"""Event properties to send with the event"""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class MCPAgentExecutionEvent(BaseTelemetryEvent):
|
|
24
|
+
"""Comprehensive event for tracking complete MCP agent execution"""
|
|
25
|
+
|
|
26
|
+
# Execution method and context
|
|
27
|
+
execution_method: str # "run" or "astream"
|
|
28
|
+
query: str # The actual user query
|
|
29
|
+
success: bool
|
|
30
|
+
|
|
31
|
+
# Agent configuration
|
|
32
|
+
model_provider: str
|
|
33
|
+
model_name: str
|
|
34
|
+
server_count: int
|
|
35
|
+
server_identifiers: list[dict[str, str]]
|
|
36
|
+
total_tools_available: int
|
|
37
|
+
tools_available_names: list[str]
|
|
38
|
+
max_steps_configured: int
|
|
39
|
+
memory_enabled: bool
|
|
40
|
+
use_server_manager: bool
|
|
41
|
+
|
|
42
|
+
# Execution PARAMETERS
|
|
43
|
+
max_steps_used: int | None
|
|
44
|
+
manage_connector: bool
|
|
45
|
+
external_history_used: bool
|
|
46
|
+
|
|
47
|
+
# Execution results
|
|
48
|
+
steps_taken: int | None = None
|
|
49
|
+
tools_used_count: int | None = None
|
|
50
|
+
tools_used_names: list[str] | None = None
|
|
51
|
+
response: str | None = None # The actual response
|
|
52
|
+
execution_time_ms: int | None = None
|
|
53
|
+
error_type: str | None = None
|
|
54
|
+
|
|
55
|
+
# Context
|
|
56
|
+
conversation_history_length: int | None = None
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def name(self) -> str:
|
|
60
|
+
return "mcp_agent_execution"
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def properties(self) -> dict[str, Any]:
|
|
64
|
+
return {
|
|
65
|
+
# Core execution info
|
|
66
|
+
"execution_method": self.execution_method,
|
|
67
|
+
"query": self.query,
|
|
68
|
+
"query_length": len(self.query),
|
|
69
|
+
"success": self.success,
|
|
70
|
+
# Agent configuration
|
|
71
|
+
"model_provider": self.model_provider,
|
|
72
|
+
"model_name": self.model_name,
|
|
73
|
+
"server_count": self.server_count,
|
|
74
|
+
"server_identifiers": self.server_identifiers,
|
|
75
|
+
"total_tools_available": self.total_tools_available,
|
|
76
|
+
"tools_available_names": self.tools_available_names,
|
|
77
|
+
"max_steps_configured": self.max_steps_configured,
|
|
78
|
+
"memory_enabled": self.memory_enabled,
|
|
79
|
+
"use_server_manager": self.use_server_manager,
|
|
80
|
+
# Execution parameters (always include, even if None)
|
|
81
|
+
"max_steps_used": self.max_steps_used,
|
|
82
|
+
"manage_connector": self.manage_connector,
|
|
83
|
+
"external_history_used": self.external_history_used,
|
|
84
|
+
# Execution results (always include, even if None)
|
|
85
|
+
"steps_taken": self.steps_taken,
|
|
86
|
+
"tools_used_count": self.tools_used_count,
|
|
87
|
+
"tools_used_names": self.tools_used_names,
|
|
88
|
+
"response": self.response,
|
|
89
|
+
"response_length": len(self.response) if self.response else None,
|
|
90
|
+
"execution_time_ms": self.execution_time_ms,
|
|
91
|
+
"error_type": self.error_type,
|
|
92
|
+
"conversation_history_length": self.conversation_history_length,
|
|
93
|
+
}
|