mcp-use 1.3.11__py3-none-any.whl → 1.3.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 mcp-use might be problematic. Click here for more details.
- mcp_use/adapters/langchain_adapter.py +3 -48
- mcp_use/agents/mcpagent.py +72 -33
- mcp_use/agents/remote.py +154 -128
- mcp_use/client.py +24 -0
- mcp_use/config.py +5 -0
- mcp_use/connectors/base.py +8 -0
- mcp_use/connectors/http.py +19 -7
- mcp_use/connectors/sandbox.py +12 -3
- mcp_use/connectors/stdio.py +11 -3
- mcp_use/connectors/websocket.py +1 -1
- mcp_use/middleware/__init__.py +50 -0
- mcp_use/middleware/logging.py +31 -0
- mcp_use/middleware/metrics.py +314 -0
- mcp_use/middleware/middleware.py +262 -0
- {mcp_use-1.3.11.dist-info → mcp_use-1.3.12.dist-info}/METADATA +8 -7
- {mcp_use-1.3.11.dist-info → mcp_use-1.3.12.dist-info}/RECORD +19 -15
- {mcp_use-1.3.11.dist-info → mcp_use-1.3.12.dist-info}/WHEEL +0 -0
- {mcp_use-1.3.11.dist-info → mcp_use-1.3.12.dist-info}/entry_points.txt +0 -0
- {mcp_use-1.3.11.dist-info → mcp_use-1.3.12.dist-info}/licenses/LICENSE +0 -0
mcp_use/client.py
CHANGED
|
@@ -15,6 +15,7 @@ from mcp_use.types.sandbox import SandboxOptions
|
|
|
15
15
|
|
|
16
16
|
from .config import create_connector_from_config, load_config_file
|
|
17
17
|
from .logging import logger
|
|
18
|
+
from .middleware import Middleware, default_logging_middleware
|
|
18
19
|
from .session import MCPSession
|
|
19
20
|
|
|
20
21
|
|
|
@@ -35,6 +36,7 @@ class MCPClient:
|
|
|
35
36
|
elicitation_callback: ElicitationFnT | None = None,
|
|
36
37
|
message_handler: MessageHandlerFnT | None = None,
|
|
37
38
|
logging_callback: LoggingFnT | None = None,
|
|
39
|
+
middleware: list[Middleware] | None = None,
|
|
38
40
|
) -> None:
|
|
39
41
|
"""Initialize a new MCP client.
|
|
40
42
|
|
|
@@ -55,6 +57,12 @@ class MCPClient:
|
|
|
55
57
|
self.elicitation_callback = elicitation_callback
|
|
56
58
|
self.message_handler = message_handler
|
|
57
59
|
self.logging_callback = logging_callback
|
|
60
|
+
# Add default logging middleware if no middleware provided, or prepend it to existing middleware
|
|
61
|
+
default_middleware = [default_logging_middleware]
|
|
62
|
+
if middleware:
|
|
63
|
+
self.middleware = default_middleware + middleware
|
|
64
|
+
else:
|
|
65
|
+
self.middleware = default_middleware
|
|
58
66
|
# Load configuration if provided
|
|
59
67
|
if config is not None:
|
|
60
68
|
if isinstance(config, str):
|
|
@@ -151,6 +159,21 @@ class MCPClient:
|
|
|
151
159
|
if name in self.active_sessions:
|
|
152
160
|
self.active_sessions.remove(name)
|
|
153
161
|
|
|
162
|
+
def add_middleware(self, middleware: Middleware) -> None:
|
|
163
|
+
"""Add a middleware.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
middleware: The middleware to add
|
|
167
|
+
"""
|
|
168
|
+
if len(self.sessions) == 0 and middleware not in self.middleware:
|
|
169
|
+
self.middleware.append(middleware)
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
if middleware not in self.middleware:
|
|
173
|
+
self.middleware.append(middleware)
|
|
174
|
+
for session in self.sessions.values():
|
|
175
|
+
session.connector.middleware_manager.add_middleware(middleware)
|
|
176
|
+
|
|
154
177
|
def get_server_names(self) -> list[str]:
|
|
155
178
|
"""Get the list of configured server names.
|
|
156
179
|
|
|
@@ -201,6 +224,7 @@ class MCPClient:
|
|
|
201
224
|
elicitation_callback=self.elicitation_callback,
|
|
202
225
|
message_handler=self.message_handler,
|
|
203
226
|
logging_callback=self.logging_callback,
|
|
227
|
+
middleware=self.middleware,
|
|
204
228
|
)
|
|
205
229
|
|
|
206
230
|
# Create the session
|
mcp_use/config.py
CHANGED
|
@@ -13,6 +13,7 @@ from mcp_use.types.sandbox import SandboxOptions
|
|
|
13
13
|
|
|
14
14
|
from .connectors import BaseConnector, HttpConnector, SandboxConnector, StdioConnector, WebSocketConnector
|
|
15
15
|
from .connectors.utils import is_stdio_server
|
|
16
|
+
from .middleware import Middleware
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def load_config_file(filepath: str) -> dict[str, Any]:
|
|
@@ -36,6 +37,7 @@ def create_connector_from_config(
|
|
|
36
37
|
elicitation_callback: ElicitationFnT | None = None,
|
|
37
38
|
message_handler: MessageHandlerFnT | None = None,
|
|
38
39
|
logging_callback: LoggingFnT | None = None,
|
|
40
|
+
middleware: list[Middleware] | None = None,
|
|
39
41
|
) -> BaseConnector:
|
|
40
42
|
"""Create a connector based on server configuration.
|
|
41
43
|
This function can be called with just the server_config parameter:
|
|
@@ -59,6 +61,7 @@ def create_connector_from_config(
|
|
|
59
61
|
elicitation_callback=elicitation_callback,
|
|
60
62
|
message_handler=message_handler,
|
|
61
63
|
logging_callback=logging_callback,
|
|
64
|
+
middleware=middleware,
|
|
62
65
|
)
|
|
63
66
|
|
|
64
67
|
# Sandboxed connector
|
|
@@ -72,6 +75,7 @@ def create_connector_from_config(
|
|
|
72
75
|
elicitation_callback=elicitation_callback,
|
|
73
76
|
message_handler=message_handler,
|
|
74
77
|
logging_callback=logging_callback,
|
|
78
|
+
middleware=middleware,
|
|
75
79
|
)
|
|
76
80
|
|
|
77
81
|
# HTTP connector
|
|
@@ -86,6 +90,7 @@ def create_connector_from_config(
|
|
|
86
90
|
elicitation_callback=elicitation_callback,
|
|
87
91
|
message_handler=message_handler,
|
|
88
92
|
logging_callback=logging_callback,
|
|
93
|
+
middleware=middleware,
|
|
89
94
|
)
|
|
90
95
|
|
|
91
96
|
# WebSocket connector
|
mcp_use/connectors/base.py
CHANGED
|
@@ -31,6 +31,7 @@ from pydantic import AnyUrl
|
|
|
31
31
|
import mcp_use
|
|
32
32
|
|
|
33
33
|
from ..logging import logger
|
|
34
|
+
from ..middleware import Middleware, MiddlewareManager
|
|
34
35
|
from ..task_managers import ConnectionManager
|
|
35
36
|
|
|
36
37
|
|
|
@@ -46,6 +47,7 @@ class BaseConnector(ABC):
|
|
|
46
47
|
elicitation_callback: ElicitationFnT | None = None,
|
|
47
48
|
message_handler: MessageHandlerFnT | None = None,
|
|
48
49
|
logging_callback: LoggingFnT | None = None,
|
|
50
|
+
middleware: list[Middleware] | None = None,
|
|
49
51
|
):
|
|
50
52
|
"""Initialize base connector with common attributes."""
|
|
51
53
|
self.client_session: ClientSession | None = None
|
|
@@ -62,6 +64,12 @@ class BaseConnector(ABC):
|
|
|
62
64
|
self.logging_callback = logging_callback
|
|
63
65
|
self.capabilities: ServerCapabilities | None = None
|
|
64
66
|
|
|
67
|
+
# Set up middleware manager
|
|
68
|
+
self.middleware_manager = MiddlewareManager()
|
|
69
|
+
if middleware:
|
|
70
|
+
for mw in middleware:
|
|
71
|
+
self.middleware_manager.add_middleware(mw)
|
|
72
|
+
|
|
65
73
|
@property
|
|
66
74
|
def client_info(self) -> Implementation:
|
|
67
75
|
"""Get the client info for the connector."""
|
mcp_use/connectors/http.py
CHANGED
|
@@ -17,6 +17,7 @@ from mcp_use.auth.oauth import OAuthClientProvider
|
|
|
17
17
|
from ..auth import BearerAuth, OAuth
|
|
18
18
|
from ..exceptions import OAuthAuthenticationError, OAuthDiscoveryError
|
|
19
19
|
from ..logging import logger
|
|
20
|
+
from ..middleware import CallbackClientSession, Middleware
|
|
20
21
|
from ..task_managers import SseConnectionManager, StreamableHttpConnectionManager
|
|
21
22
|
from .base import BaseConnector
|
|
22
23
|
|
|
@@ -39,6 +40,7 @@ class HttpConnector(BaseConnector):
|
|
|
39
40
|
elicitation_callback: ElicitationFnT | None = None,
|
|
40
41
|
message_handler: MessageHandlerFnT | None = None,
|
|
41
42
|
logging_callback: LoggingFnT | None = None,
|
|
43
|
+
middleware: list[Middleware] | None = None,
|
|
42
44
|
):
|
|
43
45
|
"""Initialize a new HTTP connector.
|
|
44
46
|
|
|
@@ -59,6 +61,7 @@ class HttpConnector(BaseConnector):
|
|
|
59
61
|
elicitation_callback=elicitation_callback,
|
|
60
62
|
message_handler=message_handler,
|
|
61
63
|
logging_callback=logging_callback,
|
|
64
|
+
middleware=middleware,
|
|
62
65
|
)
|
|
63
66
|
self.base_url = base_url.rstrip("/")
|
|
64
67
|
self.headers = headers or {}
|
|
@@ -158,7 +161,7 @@ class HttpConnector(BaseConnector):
|
|
|
158
161
|
read_stream, write_stream = await connection_manager.start()
|
|
159
162
|
|
|
160
163
|
# Test if this actually works by trying to create a client session and initialize it
|
|
161
|
-
|
|
164
|
+
raw_test_client = ClientSession(
|
|
162
165
|
read_stream,
|
|
163
166
|
write_stream,
|
|
164
167
|
sampling_callback=self.sampling_callback,
|
|
@@ -167,7 +170,10 @@ class HttpConnector(BaseConnector):
|
|
|
167
170
|
logging_callback=self.logging_callback,
|
|
168
171
|
client_info=self.client_info,
|
|
169
172
|
)
|
|
170
|
-
await
|
|
173
|
+
await raw_test_client.__aenter__()
|
|
174
|
+
|
|
175
|
+
# Wrap test client with middleware temporarily for testing
|
|
176
|
+
test_client = CallbackClientSession(raw_test_client, self.public_identifier, self.middleware_manager)
|
|
171
177
|
|
|
172
178
|
try:
|
|
173
179
|
# Try to initialize - this is where streamable HTTP vs SSE difference should show up
|
|
@@ -209,7 +215,7 @@ class HttpConnector(BaseConnector):
|
|
|
209
215
|
logger.error("MCP protocol error during initialization: %s", mcp_error.error)
|
|
210
216
|
# Clean up the test client
|
|
211
217
|
try:
|
|
212
|
-
await
|
|
218
|
+
await raw_test_client.__aexit__(None, None, None)
|
|
213
219
|
except Exception:
|
|
214
220
|
pass
|
|
215
221
|
raise mcp_error
|
|
@@ -218,7 +224,7 @@ class HttpConnector(BaseConnector):
|
|
|
218
224
|
# This catches non-McpError exceptions, like a direct httpx timeout
|
|
219
225
|
# but in the most cases this won't happen. It's for safety.
|
|
220
226
|
try:
|
|
221
|
-
await
|
|
227
|
+
await raw_test_client.__aexit__(None, None, None)
|
|
222
228
|
except Exception:
|
|
223
229
|
pass
|
|
224
230
|
raise init_error
|
|
@@ -251,7 +257,7 @@ class HttpConnector(BaseConnector):
|
|
|
251
257
|
read_stream, write_stream = await connection_manager.start()
|
|
252
258
|
|
|
253
259
|
# Create the client session for SSE
|
|
254
|
-
|
|
260
|
+
raw_client_session = ClientSession(
|
|
255
261
|
read_stream,
|
|
256
262
|
write_stream,
|
|
257
263
|
sampling_callback=self.sampling_callback,
|
|
@@ -260,7 +266,12 @@ class HttpConnector(BaseConnector):
|
|
|
260
266
|
logging_callback=self.logging_callback,
|
|
261
267
|
client_info=self.client_info,
|
|
262
268
|
)
|
|
263
|
-
await
|
|
269
|
+
await raw_client_session.__aenter__()
|
|
270
|
+
|
|
271
|
+
# Wrap with middleware
|
|
272
|
+
self.client_session = CallbackClientSession(
|
|
273
|
+
raw_client_session, self.public_identifier, self.middleware_manager
|
|
274
|
+
)
|
|
264
275
|
self.transport_type = "SSE"
|
|
265
276
|
|
|
266
277
|
except* Exception as sse_error:
|
|
@@ -290,4 +301,5 @@ class HttpConnector(BaseConnector):
|
|
|
290
301
|
@property
|
|
291
302
|
def public_identifier(self) -> str:
|
|
292
303
|
"""Get the identifier for the connector."""
|
|
293
|
-
|
|
304
|
+
transport_type = getattr(self, "transport_type", "http")
|
|
305
|
+
return f"{transport_type}:{self.base_url}"
|
mcp_use/connectors/sandbox.py
CHANGED
|
@@ -15,6 +15,7 @@ from mcp import ClientSession
|
|
|
15
15
|
from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
|
|
16
16
|
|
|
17
17
|
from ..logging import logger
|
|
18
|
+
from ..middleware import CallbackClientSession, Middleware
|
|
18
19
|
from ..task_managers import SseConnectionManager
|
|
19
20
|
|
|
20
21
|
# Import E2B SDK components (optional dependency)
|
|
@@ -52,6 +53,7 @@ class SandboxConnector(BaseConnector):
|
|
|
52
53
|
elicitation_callback: ElicitationFnT | None = None,
|
|
53
54
|
message_handler: MessageHandlerFnT | None = None,
|
|
54
55
|
logging_callback: LoggingFnT | None = None,
|
|
56
|
+
middleware: list[Middleware] | None = None,
|
|
55
57
|
):
|
|
56
58
|
"""Initialize a new sandbox connector.
|
|
57
59
|
|
|
@@ -71,6 +73,7 @@ class SandboxConnector(BaseConnector):
|
|
|
71
73
|
elicitation_callback=elicitation_callback,
|
|
72
74
|
message_handler=message_handler,
|
|
73
75
|
logging_callback=logging_callback,
|
|
76
|
+
middleware=middleware,
|
|
74
77
|
)
|
|
75
78
|
if Sandbox is None:
|
|
76
79
|
raise ImportError(
|
|
@@ -226,7 +229,7 @@ class SandboxConnector(BaseConnector):
|
|
|
226
229
|
read_stream, write_stream = await self._connection_manager.start()
|
|
227
230
|
|
|
228
231
|
# Create the client session
|
|
229
|
-
|
|
232
|
+
raw_client_session = ClientSession(
|
|
230
233
|
read_stream,
|
|
231
234
|
write_stream,
|
|
232
235
|
sampling_callback=self.sampling_callback,
|
|
@@ -235,7 +238,12 @@ class SandboxConnector(BaseConnector):
|
|
|
235
238
|
logging_callback=self.logging_callback,
|
|
236
239
|
client_info=self.client_info,
|
|
237
240
|
)
|
|
238
|
-
await
|
|
241
|
+
await raw_client_session.__aenter__()
|
|
242
|
+
|
|
243
|
+
# Wrap with middleware
|
|
244
|
+
self.client_session = CallbackClientSession(
|
|
245
|
+
raw_client_session, self.public_identifier, self.middleware_manager
|
|
246
|
+
)
|
|
239
247
|
|
|
240
248
|
# Mark as connected
|
|
241
249
|
self._connected = True
|
|
@@ -299,4 +307,5 @@ class SandboxConnector(BaseConnector):
|
|
|
299
307
|
@property
|
|
300
308
|
def public_identifier(self) -> str:
|
|
301
309
|
"""Get the identifier for the connector."""
|
|
302
|
-
|
|
310
|
+
args_str = " ".join(self.user_args) if self.user_args else ""
|
|
311
|
+
return f"sandbox:{self.user_command} {args_str}".strip()
|
mcp_use/connectors/stdio.py
CHANGED
|
@@ -11,6 +11,7 @@ from mcp import ClientSession, StdioServerParameters
|
|
|
11
11
|
from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
|
|
12
12
|
|
|
13
13
|
from ..logging import logger
|
|
14
|
+
from ..middleware import CallbackClientSession, Middleware
|
|
14
15
|
from ..task_managers import StdioConnectionManager
|
|
15
16
|
from .base import BaseConnector
|
|
16
17
|
|
|
@@ -33,6 +34,7 @@ class StdioConnector(BaseConnector):
|
|
|
33
34
|
elicitation_callback: ElicitationFnT | None = None,
|
|
34
35
|
message_handler: MessageHandlerFnT | None = None,
|
|
35
36
|
logging_callback: LoggingFnT | None = None,
|
|
37
|
+
middleware: list[Middleware] | None = None,
|
|
36
38
|
):
|
|
37
39
|
"""Initialize a new stdio connector.
|
|
38
40
|
|
|
@@ -49,6 +51,7 @@ class StdioConnector(BaseConnector):
|
|
|
49
51
|
elicitation_callback=elicitation_callback,
|
|
50
52
|
message_handler=message_handler,
|
|
51
53
|
logging_callback=logging_callback,
|
|
54
|
+
middleware=middleware,
|
|
52
55
|
)
|
|
53
56
|
self.command = command
|
|
54
57
|
self.args = args or [] # Ensure args is never None
|
|
@@ -71,7 +74,7 @@ class StdioConnector(BaseConnector):
|
|
|
71
74
|
read_stream, write_stream = await self._connection_manager.start()
|
|
72
75
|
|
|
73
76
|
# Create the client session
|
|
74
|
-
|
|
77
|
+
raw_client_session = ClientSession(
|
|
75
78
|
read_stream,
|
|
76
79
|
write_stream,
|
|
77
80
|
sampling_callback=self.sampling_callback,
|
|
@@ -80,7 +83,12 @@ class StdioConnector(BaseConnector):
|
|
|
80
83
|
logging_callback=self.logging_callback,
|
|
81
84
|
client_info=self.client_info,
|
|
82
85
|
)
|
|
83
|
-
await
|
|
86
|
+
await raw_client_session.__aenter__()
|
|
87
|
+
|
|
88
|
+
# Wrap with middleware
|
|
89
|
+
self.client_session = CallbackClientSession(
|
|
90
|
+
raw_client_session, self.public_identifier, self.middleware_manager
|
|
91
|
+
)
|
|
84
92
|
|
|
85
93
|
# Mark as connected
|
|
86
94
|
self._connected = True
|
|
@@ -98,4 +106,4 @@ class StdioConnector(BaseConnector):
|
|
|
98
106
|
@property
|
|
99
107
|
def public_identifier(self) -> str:
|
|
100
108
|
"""Get the identifier for the connector."""
|
|
101
|
-
return
|
|
109
|
+
return f"stdio:{self.command} {' '.join(self.args)}"
|
mcp_use/connectors/websocket.py
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Middleware package for MCP request interception and processing.
|
|
3
|
+
|
|
4
|
+
This package provides a flexible middleware system for intercepting MCP requests
|
|
5
|
+
and responses, enabling logging, metrics, caching, and custom processing.
|
|
6
|
+
|
|
7
|
+
The middleware system follows an Express.js-style pattern where middleware functions
|
|
8
|
+
receive a request context and a call_next function, allowing them to process both
|
|
9
|
+
incoming requests and outgoing responses.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# Core middleware implementation
|
|
13
|
+
# Default logging middleware
|
|
14
|
+
from .logging import default_logging_middleware
|
|
15
|
+
|
|
16
|
+
# Metrics middleware classes
|
|
17
|
+
from .metrics import (
|
|
18
|
+
CombinedAnalyticsMiddleware,
|
|
19
|
+
ErrorTrackingMiddleware,
|
|
20
|
+
MetricsMiddleware,
|
|
21
|
+
PerformanceMetricsMiddleware,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Protocol types for type-safe middleware
|
|
25
|
+
from .middleware import (
|
|
26
|
+
CallbackClientSession,
|
|
27
|
+
MCPResponseContext,
|
|
28
|
+
Middleware,
|
|
29
|
+
MiddlewareContext,
|
|
30
|
+
MiddlewareManager,
|
|
31
|
+
NextFunctionT,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
# Core types and classes
|
|
36
|
+
"MiddlewareContext",
|
|
37
|
+
"MCPResponseContext",
|
|
38
|
+
"Middleware",
|
|
39
|
+
"MiddlewareManager",
|
|
40
|
+
"CallbackClientSession",
|
|
41
|
+
# Protocol types
|
|
42
|
+
"NextFunctionT",
|
|
43
|
+
# Default logging middleware
|
|
44
|
+
"default_logging_middleware",
|
|
45
|
+
# Metrics middleware
|
|
46
|
+
"MetricsMiddleware",
|
|
47
|
+
"PerformanceMetricsMiddleware",
|
|
48
|
+
"ErrorTrackingMiddleware",
|
|
49
|
+
"CombinedAnalyticsMiddleware",
|
|
50
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Default logging middleware for MCP requests.
|
|
3
|
+
|
|
4
|
+
Simple debug logging for all MCP requests and responses.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import time
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from ..logging import logger
|
|
11
|
+
from .middleware import Middleware, MiddlewareContext, NextFunctionT
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LoggingMiddleware(Middleware):
|
|
15
|
+
"""Default logging middleware that logs all MCP requests and responses with logger.debug."""
|
|
16
|
+
|
|
17
|
+
async def on_request(self, context: MiddlewareContext[Any], call_next: NextFunctionT) -> Any:
|
|
18
|
+
"""Logs all MCP requests and responses with logger.debug."""
|
|
19
|
+
logger.debug(f"[{context.id}] {context.connection_id} -> {context.method}")
|
|
20
|
+
try:
|
|
21
|
+
result = await call_next(context)
|
|
22
|
+
duration = time.time() - context.timestamp
|
|
23
|
+
logger.debug(f"[{context.id}] {context.connection_id} <- {context.method} ({duration:.3f}s)")
|
|
24
|
+
return result
|
|
25
|
+
except Exception as e:
|
|
26
|
+
duration = time.time() - context.timestamp
|
|
27
|
+
logger.debug(f"[{context.id}] {context.connection_id} <- {context.method} FAILED ({duration:.3f}s): {e}")
|
|
28
|
+
raise
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
default_logging_middleware = LoggingMiddleware()
|