mcp-use 1.3.12__py3-none-any.whl → 1.4.0__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/__init__.py +1 -1
- mcp_use/adapters/.deprecated +0 -0
- mcp_use/adapters/__init__.py +18 -7
- mcp_use/adapters/base.py +12 -185
- mcp_use/adapters/langchain_adapter.py +12 -219
- mcp_use/agents/adapters/__init__.py +17 -0
- mcp_use/agents/adapters/anthropic.py +93 -0
- mcp_use/agents/adapters/base.py +316 -0
- mcp_use/agents/adapters/google.py +103 -0
- mcp_use/agents/adapters/langchain_adapter.py +212 -0
- mcp_use/agents/adapters/openai.py +111 -0
- mcp_use/agents/base.py +1 -1
- mcp_use/agents/managers/__init__.py +19 -0
- mcp_use/agents/managers/base.py +36 -0
- mcp_use/agents/managers/server_manager.py +131 -0
- mcp_use/agents/managers/tools/__init__.py +15 -0
- mcp_use/agents/managers/tools/base_tool.py +19 -0
- mcp_use/agents/managers/tools/connect_server.py +69 -0
- mcp_use/agents/managers/tools/disconnect_server.py +43 -0
- mcp_use/agents/managers/tools/get_active_server.py +29 -0
- mcp_use/agents/managers/tools/list_servers_tool.py +53 -0
- mcp_use/agents/managers/tools/search_tools.py +328 -0
- mcp_use/agents/mcpagent.py +386 -485
- mcp_use/agents/prompts/system_prompt_builder.py +1 -1
- mcp_use/agents/remote.py +15 -2
- mcp_use/auth/.deprecated +0 -0
- mcp_use/auth/__init__.py +19 -4
- mcp_use/auth/bearer.py +11 -12
- mcp_use/auth/oauth.py +11 -620
- mcp_use/auth/oauth_callback.py +16 -207
- mcp_use/client/__init__.py +1 -0
- mcp_use/client/auth/__init__.py +6 -0
- mcp_use/client/auth/bearer.py +23 -0
- mcp_use/client/auth/oauth.py +629 -0
- mcp_use/client/auth/oauth_callback.py +215 -0
- mcp_use/client/client.py +356 -0
- mcp_use/client/config.py +106 -0
- mcp_use/client/connectors/__init__.py +20 -0
- mcp_use/client/connectors/base.py +470 -0
- mcp_use/client/connectors/http.py +304 -0
- mcp_use/client/connectors/sandbox.py +332 -0
- mcp_use/client/connectors/stdio.py +109 -0
- mcp_use/client/connectors/utils.py +13 -0
- mcp_use/client/connectors/websocket.py +257 -0
- mcp_use/client/exceptions.py +31 -0
- mcp_use/client/middleware/__init__.py +50 -0
- mcp_use/client/middleware/logging.py +31 -0
- mcp_use/client/middleware/metrics.py +314 -0
- mcp_use/client/middleware/middleware.py +266 -0
- mcp_use/client/session.py +162 -0
- mcp_use/client/task_managers/__init__.py +20 -0
- mcp_use/client/task_managers/base.py +145 -0
- mcp_use/client/task_managers/sse.py +84 -0
- mcp_use/client/task_managers/stdio.py +69 -0
- mcp_use/client/task_managers/streamable_http.py +86 -0
- mcp_use/client/task_managers/websocket.py +68 -0
- mcp_use/client.py +12 -344
- mcp_use/config.py +20 -97
- mcp_use/connectors/.deprecated +0 -0
- mcp_use/connectors/__init__.py +46 -20
- mcp_use/connectors/base.py +12 -455
- mcp_use/connectors/http.py +13 -300
- mcp_use/connectors/sandbox.py +13 -306
- mcp_use/connectors/stdio.py +13 -104
- mcp_use/connectors/utils.py +15 -8
- mcp_use/connectors/websocket.py +13 -252
- mcp_use/exceptions.py +33 -18
- mcp_use/logging.py +1 -1
- mcp_use/managers/.deprecated +0 -0
- mcp_use/managers/__init__.py +56 -17
- mcp_use/managers/base.py +13 -31
- mcp_use/managers/server_manager.py +13 -119
- mcp_use/managers/tools/__init__.py +45 -15
- mcp_use/managers/tools/base_tool.py +5 -16
- mcp_use/managers/tools/connect_server.py +5 -67
- mcp_use/managers/tools/disconnect_server.py +5 -41
- mcp_use/managers/tools/get_active_server.py +5 -26
- mcp_use/managers/tools/list_servers_tool.py +5 -51
- mcp_use/managers/tools/search_tools.py +17 -321
- mcp_use/middleware/.deprecated +0 -0
- mcp_use/middleware/__init__.py +89 -50
- mcp_use/middleware/logging.py +14 -26
- mcp_use/middleware/metrics.py +30 -303
- mcp_use/middleware/middleware.py +39 -246
- mcp_use/session.py +13 -149
- mcp_use/task_managers/.deprecated +0 -0
- mcp_use/task_managers/__init__.py +48 -20
- mcp_use/task_managers/base.py +13 -140
- mcp_use/task_managers/sse.py +13 -79
- mcp_use/task_managers/stdio.py +13 -64
- mcp_use/task_managers/streamable_http.py +15 -81
- mcp_use/task_managers/websocket.py +13 -63
- mcp_use/telemetry/events.py +58 -0
- mcp_use/telemetry/telemetry.py +71 -1
- mcp_use/telemetry/utils.py +1 -1
- mcp_use/types/.deprecated +0 -0
- mcp_use/types/sandbox.py +13 -18
- {mcp_use-1.3.12.dist-info → mcp_use-1.4.0.dist-info}/METADATA +68 -43
- mcp_use-1.4.0.dist-info/RECORD +111 -0
- mcp_use/cli.py +0 -581
- mcp_use-1.3.12.dist-info/RECORD +0 -64
- mcp_use-1.3.12.dist-info/licenses/LICENSE +0 -21
- /mcp_use/{observability → agents/observability}/__init__.py +0 -0
- /mcp_use/{observability → agents/observability}/callbacks_manager.py +0 -0
- /mcp_use/{observability → agents/observability}/laminar.py +0 -0
- /mcp_use/{observability → agents/observability}/langfuse.py +0 -0
- {mcp_use-1.3.12.dist-info → mcp_use-1.4.0.dist-info}/WHEEL +0 -0
- {mcp_use-1.3.12.dist-info → mcp_use-1.4.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StdIO connector for MCP implementations.
|
|
3
|
+
|
|
4
|
+
This module provides a connector for communicating with MCP implementations
|
|
5
|
+
through the standard input/output streams.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from mcp import ClientSession, StdioServerParameters
|
|
11
|
+
from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
|
|
12
|
+
|
|
13
|
+
from mcp_use.client.connectors.base import BaseConnector
|
|
14
|
+
from mcp_use.client.middleware import CallbackClientSession, Middleware
|
|
15
|
+
from mcp_use.client.task_managers import StdioConnectionManager
|
|
16
|
+
from mcp_use.logging import logger
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class StdioConnector(BaseConnector):
|
|
20
|
+
"""Connector for MCP implementations using stdio transport.
|
|
21
|
+
|
|
22
|
+
This connector uses the stdio transport to communicate with MCP implementations
|
|
23
|
+
that are executed as child processes. It uses a connection manager to handle
|
|
24
|
+
the proper lifecycle management of the stdio client.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
command: str = "npx",
|
|
30
|
+
args: list[str] | None = None,
|
|
31
|
+
env: dict[str, str] | None = None,
|
|
32
|
+
errlog=sys.stderr,
|
|
33
|
+
sampling_callback: SamplingFnT | None = None,
|
|
34
|
+
elicitation_callback: ElicitationFnT | None = None,
|
|
35
|
+
message_handler: MessageHandlerFnT | None = None,
|
|
36
|
+
logging_callback: LoggingFnT | None = None,
|
|
37
|
+
middleware: list[Middleware] | None = None,
|
|
38
|
+
):
|
|
39
|
+
"""Initialize a new stdio connector.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
command: The command to execute.
|
|
43
|
+
args: Optional command line arguments.
|
|
44
|
+
env: Optional environment variables.
|
|
45
|
+
errlog: Stream to write error output to.
|
|
46
|
+
sampling_callback: Optional callback to sample the client.
|
|
47
|
+
elicitation_callback: Optional callback to elicit the client.
|
|
48
|
+
"""
|
|
49
|
+
super().__init__(
|
|
50
|
+
sampling_callback=sampling_callback,
|
|
51
|
+
elicitation_callback=elicitation_callback,
|
|
52
|
+
message_handler=message_handler,
|
|
53
|
+
logging_callback=logging_callback,
|
|
54
|
+
middleware=middleware,
|
|
55
|
+
)
|
|
56
|
+
self.command = command
|
|
57
|
+
self.args = args or [] # Ensure args is never None
|
|
58
|
+
self.env = env
|
|
59
|
+
self.errlog = errlog
|
|
60
|
+
|
|
61
|
+
async def connect(self) -> None:
|
|
62
|
+
"""Establish a connection to the MCP implementation."""
|
|
63
|
+
if self._connected:
|
|
64
|
+
logger.debug("Already connected to MCP implementation")
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
logger.debug(f"Connecting to MCP implementation: {self.command}")
|
|
68
|
+
try:
|
|
69
|
+
# Create server parameters
|
|
70
|
+
server_params = StdioServerParameters(command=self.command, args=self.args, env=self.env)
|
|
71
|
+
|
|
72
|
+
# Create and start the connection manager
|
|
73
|
+
self._connection_manager = StdioConnectionManager(server_params, self.errlog)
|
|
74
|
+
read_stream, write_stream = await self._connection_manager.start()
|
|
75
|
+
|
|
76
|
+
# Create the client session
|
|
77
|
+
raw_client_session = ClientSession(
|
|
78
|
+
read_stream,
|
|
79
|
+
write_stream,
|
|
80
|
+
sampling_callback=self.sampling_callback,
|
|
81
|
+
elicitation_callback=self.elicitation_callback,
|
|
82
|
+
message_handler=self._internal_message_handler,
|
|
83
|
+
logging_callback=self.logging_callback,
|
|
84
|
+
client_info=self.client_info,
|
|
85
|
+
)
|
|
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
|
+
)
|
|
92
|
+
|
|
93
|
+
# Mark as connected
|
|
94
|
+
self._connected = True
|
|
95
|
+
logger.debug(f"Successfully connected to MCP implementation: {self.command}")
|
|
96
|
+
|
|
97
|
+
except Exception as e:
|
|
98
|
+
logger.error(f"Failed to connect to MCP implementation: {e}")
|
|
99
|
+
|
|
100
|
+
# Clean up any resources if connection failed
|
|
101
|
+
await self._cleanup_resources()
|
|
102
|
+
|
|
103
|
+
# Re-raise the original exception
|
|
104
|
+
raise
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def public_identifier(self) -> str:
|
|
108
|
+
"""Get the identifier for the connector."""
|
|
109
|
+
return f"stdio:{self.command} {' '.join(self.args)}"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def is_stdio_server(server_config: dict[str, Any]) -> bool:
|
|
5
|
+
"""Check if the server configuration is for a stdio server.
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
server_config: The server configuration section
|
|
9
|
+
|
|
10
|
+
Returns:
|
|
11
|
+
True if the server is a stdio server, False otherwise
|
|
12
|
+
"""
|
|
13
|
+
return "command" in server_config and "args" in server_config
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WebSocket connector for MCP implementations.
|
|
3
|
+
|
|
4
|
+
This module provides a connector for communicating with MCP implementations
|
|
5
|
+
through WebSocket connections.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import uuid
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
from mcp.types import Tool
|
|
15
|
+
from websockets import ClientConnection
|
|
16
|
+
|
|
17
|
+
from mcp_use.client.connectors.base import BaseConnector
|
|
18
|
+
from mcp_use.client.task_managers import ConnectionManager, WebSocketConnectionManager
|
|
19
|
+
from mcp_use.logging import logger
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class WebSocketConnector(BaseConnector):
|
|
23
|
+
"""Connector for MCP implementations using WebSocket transport.
|
|
24
|
+
|
|
25
|
+
This connector uses WebSockets to communicate with remote MCP implementations,
|
|
26
|
+
using a connection manager to handle the proper lifecycle management.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
url: str,
|
|
32
|
+
headers: dict[str, str] | None = None,
|
|
33
|
+
auth: str | dict[str, Any] | httpx.Auth | None = None,
|
|
34
|
+
):
|
|
35
|
+
"""Initialize a new WebSocket connector.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
url: The WebSocket URL to connect to.
|
|
39
|
+
headers: Optional additional headers.
|
|
40
|
+
auth: Authentication method - can be:
|
|
41
|
+
- A string token: Use Bearer token authentication
|
|
42
|
+
- A dict: Not supported for WebSocket (will log warning)
|
|
43
|
+
- An httpx.Auth object: Not supported for WebSocket (will log warning)
|
|
44
|
+
"""
|
|
45
|
+
self.url = url
|
|
46
|
+
self.headers = headers or {}
|
|
47
|
+
|
|
48
|
+
# Handle authentication - WebSocket only supports bearer tokens
|
|
49
|
+
# An auth field it's not needed
|
|
50
|
+
if auth is not None:
|
|
51
|
+
if isinstance(auth, str):
|
|
52
|
+
self.headers["Authorization"] = f"Bearer {auth}"
|
|
53
|
+
else:
|
|
54
|
+
logger.warning("WebSocket connector only supports bearer token authentication")
|
|
55
|
+
|
|
56
|
+
self.ws: ClientConnection | None = None
|
|
57
|
+
self._connection_manager: ConnectionManager | None = None
|
|
58
|
+
self._receiver_task: asyncio.Task | None = None
|
|
59
|
+
self.pending_requests: dict[str, asyncio.Future] = {}
|
|
60
|
+
self._tools: list[Tool] | None = None
|
|
61
|
+
self._connected = False
|
|
62
|
+
|
|
63
|
+
async def connect(self) -> None:
|
|
64
|
+
"""Establish a connection to the MCP implementation."""
|
|
65
|
+
if self._connected:
|
|
66
|
+
logger.debug("Already connected to MCP implementation")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
logger.debug(f"Connecting to MCP implementation via WebSocket: {self.url}")
|
|
70
|
+
try:
|
|
71
|
+
# Create and start the connection manager
|
|
72
|
+
self._connection_manager = WebSocketConnectionManager(self.url, self.headers)
|
|
73
|
+
self.ws = await self._connection_manager.start()
|
|
74
|
+
|
|
75
|
+
# Start the message receiver task
|
|
76
|
+
self._receiver_task = asyncio.create_task(self._receive_messages(), name="websocket_receiver_task")
|
|
77
|
+
|
|
78
|
+
# Mark as connected
|
|
79
|
+
self._connected = True
|
|
80
|
+
logger.debug(f"Successfully connected to MCP implementation via WebSocket: {self.url}")
|
|
81
|
+
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.error(f"Failed to connect to MCP implementation via WebSocket: {e}")
|
|
84
|
+
|
|
85
|
+
# Clean up any resources if connection failed
|
|
86
|
+
await self._cleanup_resources()
|
|
87
|
+
|
|
88
|
+
# Re-raise the original exception
|
|
89
|
+
raise
|
|
90
|
+
|
|
91
|
+
async def _receive_messages(self) -> None:
|
|
92
|
+
"""Continuously receive and process messages from the WebSocket."""
|
|
93
|
+
if not self.ws:
|
|
94
|
+
raise RuntimeError("WebSocket is not connected")
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
async for message in self.ws:
|
|
98
|
+
# Parse the message
|
|
99
|
+
data = json.loads(message)
|
|
100
|
+
|
|
101
|
+
# Check if this is a response to a pending request
|
|
102
|
+
request_id = data.get("id")
|
|
103
|
+
if request_id and request_id in self.pending_requests:
|
|
104
|
+
future = self.pending_requests.pop(request_id)
|
|
105
|
+
if "result" in data:
|
|
106
|
+
future.set_result(data["result"])
|
|
107
|
+
elif "error" in data:
|
|
108
|
+
future.set_exception(Exception(data["error"]))
|
|
109
|
+
|
|
110
|
+
logger.debug(f"Received response for request {request_id}")
|
|
111
|
+
else:
|
|
112
|
+
logger.debug(f"Received message: {data}")
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.error(f"Error in WebSocket message receiver: {e}")
|
|
115
|
+
# If the websocket connection was closed or errored,
|
|
116
|
+
# reject all pending requests
|
|
117
|
+
for future in self.pending_requests.values():
|
|
118
|
+
if not future.done():
|
|
119
|
+
future.set_exception(e)
|
|
120
|
+
|
|
121
|
+
async def disconnect(self) -> None:
|
|
122
|
+
"""Close the connection to the MCP implementation."""
|
|
123
|
+
if not self._connected:
|
|
124
|
+
logger.debug("Not connected to MCP implementation")
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
logger.debug("Disconnecting from MCP implementation")
|
|
128
|
+
await self._cleanup_resources()
|
|
129
|
+
self._connected = False
|
|
130
|
+
logger.debug("Disconnected from MCP implementation")
|
|
131
|
+
|
|
132
|
+
async def _cleanup_resources(self) -> None:
|
|
133
|
+
"""Clean up all resources associated with this connector."""
|
|
134
|
+
errors = []
|
|
135
|
+
|
|
136
|
+
# First cancel the receiver task
|
|
137
|
+
if self._receiver_task and not self._receiver_task.done():
|
|
138
|
+
try:
|
|
139
|
+
logger.debug("Cancelling WebSocket receiver task")
|
|
140
|
+
self._receiver_task.cancel()
|
|
141
|
+
try:
|
|
142
|
+
await self._receiver_task
|
|
143
|
+
except asyncio.CancelledError:
|
|
144
|
+
logger.debug("WebSocket receiver task cancelled successfully")
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.warning(f"Error during WebSocket receiver task cancellation: {e}")
|
|
147
|
+
except Exception as e:
|
|
148
|
+
error_msg = f"Error cancelling WebSocket receiver task: {e}"
|
|
149
|
+
logger.warning(error_msg)
|
|
150
|
+
errors.append(error_msg)
|
|
151
|
+
finally:
|
|
152
|
+
self._receiver_task = None
|
|
153
|
+
|
|
154
|
+
# Reject any pending requests
|
|
155
|
+
if self.pending_requests:
|
|
156
|
+
logger.debug(f"Rejecting {len(self.pending_requests)} pending requests")
|
|
157
|
+
for future in self.pending_requests.values():
|
|
158
|
+
if not future.done():
|
|
159
|
+
future.set_exception(ConnectionError("WebSocket disconnected"))
|
|
160
|
+
self.pending_requests.clear()
|
|
161
|
+
|
|
162
|
+
# Then stop the connection manager
|
|
163
|
+
if self._connection_manager:
|
|
164
|
+
try:
|
|
165
|
+
logger.debug("Stopping connection manager")
|
|
166
|
+
await self._connection_manager.stop()
|
|
167
|
+
except Exception as e:
|
|
168
|
+
error_msg = f"Error stopping connection manager: {e}"
|
|
169
|
+
logger.warning(error_msg)
|
|
170
|
+
errors.append(error_msg)
|
|
171
|
+
finally:
|
|
172
|
+
self._connection_manager = None
|
|
173
|
+
self.ws = None
|
|
174
|
+
|
|
175
|
+
# Reset tools
|
|
176
|
+
self._tools = None
|
|
177
|
+
|
|
178
|
+
if errors:
|
|
179
|
+
logger.warning(f"Encountered {len(errors)} errors during resource cleanup")
|
|
180
|
+
|
|
181
|
+
async def _send_request(self, method: str, params: dict[str, Any] | None = None) -> Any:
|
|
182
|
+
"""Send a request and wait for a response."""
|
|
183
|
+
if not self.ws:
|
|
184
|
+
raise RuntimeError("WebSocket is not connected")
|
|
185
|
+
|
|
186
|
+
# Create a request ID
|
|
187
|
+
request_id = str(uuid.uuid4())
|
|
188
|
+
|
|
189
|
+
# Create a future to receive the response
|
|
190
|
+
future = asyncio.Future()
|
|
191
|
+
self.pending_requests[request_id] = future
|
|
192
|
+
|
|
193
|
+
# Send the request
|
|
194
|
+
await self.ws.send(json.dumps({"id": request_id, "method": method, "params": params or {}}))
|
|
195
|
+
|
|
196
|
+
logger.debug(f"Sent request {request_id} method: {method}")
|
|
197
|
+
|
|
198
|
+
# Wait for the response
|
|
199
|
+
try:
|
|
200
|
+
return await future
|
|
201
|
+
except Exception as e:
|
|
202
|
+
# Remove the request from pending requests
|
|
203
|
+
self.pending_requests.pop(request_id, None)
|
|
204
|
+
logger.error(f"Error waiting for response to request {request_id}: {e}")
|
|
205
|
+
raise
|
|
206
|
+
|
|
207
|
+
async def initialize(self) -> dict[str, Any]:
|
|
208
|
+
"""Initialize the MCP session and return session information."""
|
|
209
|
+
logger.debug("Initializing MCP session")
|
|
210
|
+
result = await self._send_request("initialize")
|
|
211
|
+
|
|
212
|
+
# Get available tools
|
|
213
|
+
tools_result = await self.list_tools()
|
|
214
|
+
self._tools = [Tool(**tool) for tool in tools_result]
|
|
215
|
+
|
|
216
|
+
logger.debug(f"MCP session initialized with {len(self._tools)} tools")
|
|
217
|
+
return result
|
|
218
|
+
|
|
219
|
+
async def list_tools(self) -> list[dict[str, Any]]:
|
|
220
|
+
"""List all available tools from the MCP implementation."""
|
|
221
|
+
logger.debug("Listing tools")
|
|
222
|
+
result = await self._send_request("tools/list")
|
|
223
|
+
return result.get("tools", [])
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def tools(self) -> list[Tool]:
|
|
227
|
+
"""Get the list of available tools."""
|
|
228
|
+
if not self._tools:
|
|
229
|
+
raise RuntimeError("MCP client is not initialized")
|
|
230
|
+
return self._tools
|
|
231
|
+
|
|
232
|
+
async def call_tool(self, name: str, arguments: dict[str, Any]) -> Any:
|
|
233
|
+
"""Call an MCP tool with the given arguments."""
|
|
234
|
+
logger.debug(f"Calling tool '{name}' with arguments: {arguments}")
|
|
235
|
+
return await self._send_request("tools/call", {"name": name, "arguments": arguments})
|
|
236
|
+
|
|
237
|
+
async def list_resources(self) -> list[dict[str, Any]]:
|
|
238
|
+
"""List all available resources from the MCP implementation."""
|
|
239
|
+
logger.debug("Listing resources")
|
|
240
|
+
result = await self._send_request("resources/list")
|
|
241
|
+
return result
|
|
242
|
+
|
|
243
|
+
async def read_resource(self, uri: str) -> tuple[bytes, str]:
|
|
244
|
+
"""Read a resource by URI."""
|
|
245
|
+
logger.debug(f"Reading resource: {uri}")
|
|
246
|
+
result = await self._send_request("resources/read", {"uri": uri})
|
|
247
|
+
return result.get("content", b""), result.get("mimeType", "")
|
|
248
|
+
|
|
249
|
+
async def request(self, method: str, params: dict[str, Any] | None = None) -> Any:
|
|
250
|
+
"""Send a raw request to the MCP implementation."""
|
|
251
|
+
logger.debug(f"Sending request: {method} with params: {params}")
|
|
252
|
+
return await self._send_request(method, params)
|
|
253
|
+
|
|
254
|
+
@property
|
|
255
|
+
def public_identifier(self) -> str:
|
|
256
|
+
"""Get the identifier for the connector."""
|
|
257
|
+
return f"websocket:{self.url}"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""MCP-use exceptions."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class MCPError(Exception):
|
|
5
|
+
"""Base exception for MCP-use."""
|
|
6
|
+
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class OAuthDiscoveryError(MCPError):
|
|
11
|
+
"""OAuth discovery auth metadata error"""
|
|
12
|
+
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OAuthAuthenticationError(MCPError):
|
|
17
|
+
"""OAuth authentication-related errors"""
|
|
18
|
+
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ConnectionError(MCPError):
|
|
23
|
+
"""Connection-related errors."""
|
|
24
|
+
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ConfigurationError(MCPError):
|
|
29
|
+
"""Configuration-related errors."""
|
|
30
|
+
|
|
31
|
+
pass
|
|
@@ -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 mcp_use.client.middleware.middleware import Middleware, MiddlewareContext, NextFunctionT
|
|
11
|
+
from mcp_use.logging import logger
|
|
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()
|