mcp-use 1.3.0__py3-none-any.whl → 1.3.2__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.

Files changed (38) hide show
  1. mcp_use/__init__.py +2 -0
  2. mcp_use/adapters/base.py +2 -6
  3. mcp_use/adapters/langchain_adapter.py +4 -11
  4. mcp_use/agents/base.py +1 -3
  5. mcp_use/agents/mcpagent.py +121 -45
  6. mcp_use/agents/prompts/system_prompt_builder.py +1 -3
  7. mcp_use/client.py +26 -11
  8. mcp_use/config.py +9 -9
  9. mcp_use/connectors/base.py +136 -32
  10. mcp_use/connectors/http.py +100 -30
  11. mcp_use/connectors/sandbox.py +11 -16
  12. mcp_use/connectors/stdio.py +8 -5
  13. mcp_use/connectors/websocket.py +8 -5
  14. mcp_use/logging.py +1 -1
  15. mcp_use/managers/server_manager.py +5 -16
  16. mcp_use/managers/tools/disconnect_server.py +1 -3
  17. mcp_use/managers/tools/get_active_server.py +1 -4
  18. mcp_use/managers/tools/search_tools.py +29 -36
  19. mcp_use/managers/tools/use_tool.py +5 -18
  20. mcp_use/observability/__init__.py +8 -0
  21. mcp_use/observability/laminar.py +21 -0
  22. mcp_use/observability/langfuse.py +35 -0
  23. mcp_use/session.py +1 -4
  24. mcp_use/task_managers/__init__.py +2 -1
  25. mcp_use/task_managers/base.py +10 -4
  26. mcp_use/task_managers/streamable_http.py +81 -0
  27. mcp_use/task_managers/websocket.py +5 -0
  28. mcp_use/telemetry/__init__.py +0 -0
  29. mcp_use/telemetry/events.py +93 -0
  30. mcp_use/telemetry/telemetry.py +306 -0
  31. mcp_use/telemetry/utils.py +48 -0
  32. mcp_use/utils.py +27 -0
  33. {mcp_use-1.3.0.dist-info → mcp_use-1.3.2.dist-info}/METADATA +82 -26
  34. mcp_use-1.3.2.dist-info/RECORD +49 -0
  35. mcp_use/types/clientoptions.py +0 -23
  36. mcp_use-1.3.0.dist-info/RECORD +0 -41
  37. {mcp_use-1.3.0.dist-info → mcp_use-1.3.2.dist-info}/WHEEL +0 -0
  38. {mcp_use-1.3.0.dist-info → mcp_use-1.3.2.dist-info}/licenses/LICENSE +0 -0
@@ -24,18 +24,25 @@ class BaseConnector(ABC):
24
24
 
25
25
  def __init__(self):
26
26
  """Initialize base connector with common attributes."""
27
- self.client: ClientSession | None = None
27
+ self.client_session: ClientSession | None = None
28
28
  self._connection_manager: ConnectionManager | None = None
29
29
  self._tools: list[Tool] | None = None
30
30
  self._resources: list[Resource] | None = None
31
31
  self._prompts: list[Prompt] | None = None
32
32
  self._connected = False
33
+ self.auto_reconnect = True # Whether to automatically reconnect on connection loss (not configurable for now)
33
34
 
34
35
  @abstractmethod
35
36
  async def connect(self) -> None:
36
37
  """Establish a connection to the MCP implementation."""
37
38
  pass
38
39
 
40
+ @property
41
+ @abstractmethod
42
+ def public_identifier(self) -> str:
43
+ """Get the identifier for the connector."""
44
+ pass
45
+
39
46
  async def disconnect(self) -> None:
40
47
  """Close the connection to the MCP implementation."""
41
48
  if not self._connected:
@@ -52,16 +59,16 @@ class BaseConnector(ABC):
52
59
  errors = []
53
60
 
54
61
  # First close the client session
55
- if self.client:
62
+ if self.client_session:
56
63
  try:
57
64
  logger.debug("Closing client session")
58
- await self.client.__aexit__(None, None, None)
65
+ await self.client_session.__aexit__(None, None, None)
59
66
  except Exception as e:
60
67
  error_msg = f"Error closing client session: {e}"
61
68
  logger.warning(error_msg)
62
69
  errors.append(error_msg)
63
70
  finally:
64
- self.client = None
71
+ self.client_session = None
65
72
 
66
73
  # Then stop the connection manager
67
74
  if self._connection_manager:
@@ -85,13 +92,13 @@ class BaseConnector(ABC):
85
92
 
86
93
  async def initialize(self) -> dict[str, Any]:
87
94
  """Initialize the MCP session and return session information."""
88
- if not self.client:
95
+ if not self.client_session:
89
96
  raise RuntimeError("MCP client is not connected")
90
97
 
91
98
  logger.debug("Initializing MCP session")
92
99
 
93
100
  # Initialize the session
94
- result = await self.client.initialize()
101
+ result = await self.client_session.initialize()
95
102
 
96
103
  server_capabilities = result.capabilities
97
104
 
@@ -145,24 +152,123 @@ class BaseConnector(ABC):
145
152
  raise RuntimeError("MCP client is not initialized")
146
153
  return self._prompts
147
154
 
148
- async def call_tool(self, name: str, arguments: dict[str, Any]) -> CallToolResult:
149
- """Call an MCP tool with the given arguments."""
150
- if not self.client:
155
+ @property
156
+ def is_connected(self) -> bool:
157
+ """Check if the connector is actually connected and the connection is alive.
158
+
159
+ This property checks not only the connected flag but also verifies that
160
+ the underlying connection manager and streams are still active.
161
+
162
+ Returns:
163
+ True if the connector is connected and the connection is alive, False otherwise.
164
+ """
165
+
166
+ # Check if we have a client session
167
+ if not self.client_session:
168
+ # Update the connected flag since we don't have a client session
169
+ self._connected = False
170
+ return False
171
+
172
+ # First check the basic connected flag
173
+ if not self._connected:
174
+ return False
175
+
176
+ # Check if we have a connection manager and if its task is still running
177
+ if self._connection_manager:
178
+ try:
179
+ # Check if the connection manager task is done (indicates disconnection)
180
+ if hasattr(self._connection_manager, "_task") and self._connection_manager._task:
181
+ if self._connection_manager._task.done():
182
+ logger.debug("Connection manager task is done, marking as disconnected")
183
+ self._connected = False
184
+ return False
185
+
186
+ # For HTTP-based connectors, also check if streams are still open
187
+ # Use the get_streams method to get the current connection
188
+ streams = self._connection_manager.get_streams()
189
+ if streams:
190
+ # Connection should be a tuple of (read_stream, write_stream)
191
+ if isinstance(streams, tuple) and len(streams) == 2:
192
+ read_stream, write_stream = streams
193
+ # Check if streams are closed using getattr with default value
194
+ if getattr(read_stream, "_closed", False):
195
+ logger.debug("Read stream is closed, marking as disconnected")
196
+ self._connected = False
197
+ return False
198
+ if getattr(write_stream, "_closed", False):
199
+ logger.debug("Write stream is closed, marking as disconnected")
200
+ self._connected = False
201
+ return False
202
+
203
+ except Exception as e:
204
+ # If we can't check the connection state, assume disconnected for safety
205
+ logger.debug(f"Error checking connection state: {e}, marking as disconnected")
206
+ self._connected = False
207
+ return False
208
+
209
+ return True
210
+
211
+ async def _ensure_connected(self) -> None:
212
+ """Ensure the connector is connected, reconnecting if necessary.
213
+
214
+ Raises:
215
+ RuntimeError: If connection cannot be established and auto_reconnect is False.
216
+ """
217
+ if not self.client_session:
151
218
  raise RuntimeError("MCP client is not connected")
152
219
 
220
+ if not self.is_connected:
221
+ if self.auto_reconnect:
222
+ logger.debug("Connection lost, attempting to reconnect...")
223
+ try:
224
+ await self.connect()
225
+ logger.debug("Reconnection successful")
226
+ except Exception as e:
227
+ raise RuntimeError(f"Failed to reconnect to MCP server: {e}") from e
228
+ else:
229
+ raise RuntimeError(
230
+ "Connection to MCP server has been lost. Auto-reconnection is disabled. Please reconnect manually."
231
+ )
232
+
233
+ async def call_tool(self, name: str, arguments: dict[str, Any]) -> CallToolResult:
234
+ """Call an MCP tool with automatic reconnection handling.
235
+
236
+ Args:
237
+ name: The name of the tool to call.
238
+ arguments: The arguments to pass to the tool.
239
+
240
+ Returns:
241
+ The result of the tool call.
242
+
243
+ Raises:
244
+ RuntimeError: If the connection is lost and cannot be reestablished.
245
+ """
246
+
247
+ # Ensure we're connected
248
+ await self._ensure_connected()
249
+
153
250
  logger.debug(f"Calling tool '{name}' with arguments: {arguments}")
154
- result = await self.client.call_tool(name, arguments)
155
- logger.debug(f"Tool '{name}' called with result: {result}")
156
- return result
251
+ try:
252
+ result = await self.client_session.call_tool(name, arguments)
253
+ logger.debug(f"Tool '{name}' called with result: {result}")
254
+ return result
255
+ except Exception as e:
256
+ # Check if the error might be due to connection loss
257
+ if not self.is_connected:
258
+ raise RuntimeError(f"Tool call '{name}' failed due to connection loss: {e}") from e
259
+ else:
260
+ # Re-raise the original error if it's not connection-related
261
+ raise
157
262
 
158
263
  async def list_tools(self) -> list[Tool]:
159
264
  """List all available tools from the MCP implementation."""
160
- if not self.client:
161
- raise RuntimeError("MCP client is not connected")
265
+
266
+ # Ensure we're connected
267
+ await self._ensure_connected()
162
268
 
163
269
  logger.debug("Listing tools")
164
270
  try:
165
- result = await self.client.list_tools()
271
+ result = await self.client_session.list_tools()
166
272
  return result.tools
167
273
  except McpError as e:
168
274
  logger.error(f"Error listing tools: {e}")
@@ -170,12 +276,12 @@ class BaseConnector(ABC):
170
276
 
171
277
  async def list_resources(self) -> list[Resource]:
172
278
  """List all available resources from the MCP implementation."""
173
- if not self.client:
174
- raise RuntimeError("MCP client is not connected")
279
+ # Ensure we're connected
280
+ await self._ensure_connected()
175
281
 
176
282
  logger.debug("Listing resources")
177
283
  try:
178
- result = await self.client.list_resources()
284
+ result = await self.client_session.list_resources()
179
285
  return result.resources
180
286
  except McpError as e:
181
287
  logger.error(f"Error listing resources: {e}")
@@ -183,41 +289,39 @@ class BaseConnector(ABC):
183
289
 
184
290
  async def read_resource(self, uri: str) -> ReadResourceResult:
185
291
  """Read a resource by URI."""
186
- if not self.client:
292
+ if not self.client_session:
187
293
  raise RuntimeError("MCP client is not connected")
188
294
 
189
295
  logger.debug(f"Reading resource: {uri}")
190
- result = await self.client.read_resource(uri)
296
+ result = await self.client_session.read_resource(uri)
191
297
  return result
192
298
 
193
299
  async def list_prompts(self) -> list[Prompt]:
194
300
  """List all available prompts from the MCP implementation."""
195
- if not self.client:
196
- raise RuntimeError("MCP client is not connected")
301
+ # Ensure we're connected
302
+ await self._ensure_connected()
197
303
 
198
304
  logger.debug("Listing prompts")
199
305
  try:
200
- result = await self.client.list_prompts()
306
+ result = await self.client_session.list_prompts()
201
307
  return result.prompts
202
308
  except McpError as e:
203
309
  logger.error(f"Error listing prompts: {e}")
204
310
  return []
205
311
 
206
- async def get_prompt(
207
- self, name: str, arguments: dict[str, Any] | None = None
208
- ) -> GetPromptResult:
312
+ async def get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> GetPromptResult:
209
313
  """Get a prompt by name."""
210
- if not self.client:
211
- raise RuntimeError("MCP client is not connected")
314
+ # Ensure we're connected
315
+ await self._ensure_connected()
212
316
 
213
317
  logger.debug(f"Getting prompt: {name}")
214
- result = await self.client.get_prompt(name, arguments)
318
+ result = await self.client_session.get_prompt(name, arguments)
215
319
  return result
216
320
 
217
321
  async def request(self, method: str, params: dict[str, Any] | None = None) -> Any:
218
322
  """Send a raw request to the MCP implementation."""
219
- if not self.client:
220
- raise RuntimeError("MCP client is not connected")
323
+ # Ensure we're connected
324
+ await self._ensure_connected()
221
325
 
222
326
  logger.debug(f"Sending request: {method} with params: {params}")
223
- return await self.client.request({"method": method, "params": params or {}})
327
+ return await self.client_session.request({"method": method, "params": params or {}})
@@ -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,107 @@ 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
- logger.debug(f"Connecting to MCP implementation via HTTP/SSE: {self.base_url}")
55
- try:
56
- # Create the SSE connection URL
57
- sse_url = f"{self.base_url}"
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
- # Create the client session
66
- self.client = ClientSession(read_stream, write_stream, sampling_callback=None)
67
- await self.client.__aenter__()
68
-
69
- # Mark as connected
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
- except Exception as e:
76
- logger.error(f"Failed to connect to MCP implementation via HTTP/SSE: {e}")
77
-
78
- # Clean up any resources if connection failed
79
- await self._cleanup_resources()
80
-
81
- # Re-raise the original exception
82
- raise
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(streamable_error):
115
+ should_fallback = True
116
+ else:
117
+ # For other errors, still try fallback but they might indicate
118
+ # real connectivity issues
119
+ should_fallback = True
120
+
121
+ if should_fallback:
122
+ try:
123
+ # Fall back to the old SSE transport
124
+ logger.debug(f"Attempting SSE fallback connection to: {self.base_url}")
125
+ connection_manager = SseConnectionManager(
126
+ self.base_url, self.headers, self.timeout, self.sse_read_timeout
127
+ )
128
+
129
+ read_stream, write_stream = await connection_manager.start()
130
+
131
+ # Create the client session for SSE
132
+ self.client_session = ClientSession(read_stream, write_stream, sampling_callback=None)
133
+ await self.client_session.__aenter__()
134
+ self.transport_type = "SSE"
135
+
136
+ except Exception as sse_error:
137
+ logger.error(
138
+ f"Both transport methods failed. Streamable HTTP: {streamable_error}, SSE: {sse_error}"
139
+ )
140
+ raise sse_error
141
+ else:
142
+ raise streamable_error
143
+
144
+ # Store the successful connection manager and mark as connected
145
+ self._connection_manager = connection_manager
146
+ self._connected = True
147
+ logger.debug(f"Successfully connected to MCP implementation via {self.transport_type}: {self.base_url}")
148
+
149
+ @property
150
+ def public_identifier(self) -> str:
151
+ """Get the identifier for the connector."""
152
+ return {"type": self.transport_type, "base_url": self.base_url}
@@ -79,14 +79,12 @@ 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 'e2b_options.api_key' "
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
 
86
86
  self.sandbox_template_id = _e2b_options.get("sandbox_template_id", "base")
87
- self.supergateway_cmd_parts = _e2b_options.get(
88
- "supergateway_command", "npx -y supergateway"
89
- )
87
+ self.supergateway_cmd_parts = _e2b_options.get("supergateway_command", "npx -y supergateway")
90
88
 
91
89
  self.sandbox: Sandbox | None = None
92
90
  self.process: CommandHandle | None = None
@@ -138,10 +136,7 @@ class SandboxConnector(BaseConnector):
138
136
  async with session.get(ping_url, timeout=2) as response:
139
137
  if response.status == 200:
140
138
  elapsed = time.time() - start_time
141
- logger.info(
142
- f"Server is ready! "
143
- f"SSE endpoint responded with 200 after {elapsed:.1f}s"
144
- )
139
+ logger.info(f"Server is ready! SSE endpoint responded with 200 after {elapsed:.1f}s")
145
140
  return True
146
141
  except Exception:
147
142
  # If sse endpoint doesn't work, try the base URL
@@ -149,8 +144,7 @@ class SandboxConnector(BaseConnector):
149
144
  if response.status < 500: # Accept any non-server error
150
145
  elapsed = time.time() - start_time
151
146
  logger.info(
152
- f"Server is ready! Base URL responded with "
153
- f"{response.status} after {elapsed:.1f}s"
147
+ f"Server is ready! Base URL responded with {response.status} after {elapsed:.1f}s"
154
148
  )
155
149
  return True
156
150
  except Exception:
@@ -220,9 +214,7 @@ class SandboxConnector(BaseConnector):
220
214
  sse_url = f"{self.base_url}/sse"
221
215
 
222
216
  # Create and start the connection manager
223
- self._connection_manager = SseConnectionManager(
224
- sse_url, self.headers, self.timeout, self.sse_read_timeout
225
- )
217
+ self._connection_manager = SseConnectionManager(sse_url, self.headers, self.timeout, self.sse_read_timeout)
226
218
  read_stream, write_stream = await self._connection_manager.start()
227
219
 
228
220
  # Create the client session
@@ -231,9 +223,7 @@ class SandboxConnector(BaseConnector):
231
223
 
232
224
  # Mark as connected
233
225
  self._connected = True
234
- logger.debug(
235
- f"Successfully connected to MCP implementation via HTTP/SSE: {self.base_url}"
236
- )
226
+ logger.debug(f"Successfully connected to MCP implementation via HTTP/SSE: {self.base_url}")
237
227
 
238
228
  except Exception as e:
239
229
  logger.error(f"Failed to connect to MCP implementation: {e}")
@@ -289,3 +279,8 @@ class SandboxConnector(BaseConnector):
289
279
  await self._cleanup_resources()
290
280
  self._connected = False
291
281
  logger.debug("Disconnected from MCP implementation")
282
+
283
+ @property
284
+ def public_identifier(self) -> str:
285
+ """Get the identifier for the connector."""
286
+ return {"type": "sandbox", "command": self.user_command, "args": self.user_args}
@@ -52,17 +52,15 @@ class StdioConnector(BaseConnector):
52
52
  logger.debug(f"Connecting to MCP implementation: {self.command}")
53
53
  try:
54
54
  # Create server parameters
55
- server_params = StdioServerParameters(
56
- command=self.command, args=self.args, env=self.env
57
- )
55
+ server_params = StdioServerParameters(command=self.command, args=self.args, env=self.env)
58
56
 
59
57
  # Create and start the connection manager
60
58
  self._connection_manager = StdioConnectionManager(server_params, self.errlog)
61
59
  read_stream, write_stream = await self._connection_manager.start()
62
60
 
63
61
  # Create the client session
64
- self.client = ClientSession(read_stream, write_stream, sampling_callback=None)
65
- await self.client.__aenter__()
62
+ self.client_session = ClientSession(read_stream, write_stream, sampling_callback=None)
63
+ await self.client_session.__aenter__()
66
64
 
67
65
  # Mark as connected
68
66
  self._connected = True
@@ -76,3 +74,8 @@ class StdioConnector(BaseConnector):
76
74
 
77
75
  # Re-raise the original exception
78
76
  raise
77
+
78
+ @property
79
+ def public_identifier(self) -> str:
80
+ """Get the identifier for the connector."""
81
+ return {"type": "stdio", "command&args": f"{self.command} {' '.join(self.args)}"}
@@ -11,7 +11,7 @@ import uuid
11
11
  from typing import Any
12
12
 
13
13
  from mcp.types import Tool
14
- from websockets.client import WebSocketClientProtocol
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: WebSocketClientProtocol | None = None
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] = {}
@@ -64,9 +64,7 @@ class WebSocketConnector(BaseConnector):
64
64
  self.ws = await self._connection_manager.start()
65
65
 
66
66
  # Start the message receiver task
67
- self._receiver_task = asyncio.create_task(
68
- self._receive_messages(), name="websocket_receiver_task"
69
- )
67
+ self._receiver_task = asyncio.create_task(self._receive_messages(), name="websocket_receiver_task")
70
68
 
71
69
  # Mark as connected
72
70
  self._connected = True
@@ -243,3 +241,8 @@ class WebSocketConnector(BaseConnector):
243
241
  """Send a raw request to the MCP implementation."""
244
242
  logger.debug(f"Sending request: {method} with params: {params}")
245
243
  return await self._send_request(method, params)
244
+
245
+ @property
246
+ def public_identifier(self) -> str:
247
+ """Get the identifier for the connector."""
248
+ return {"type": "websocket", "url": self.url}
mcp_use/logging.py CHANGED
@@ -12,7 +12,7 @@ import sys
12
12
  from langchain.globals import set_debug as langchain_set_debug
13
13
 
14
14
  # Global debug flag - can be set programmatically or from environment
15
- MCP_USE_DEBUG = False
15
+ MCP_USE_DEBUG = 1
16
16
 
17
17
 
18
18
  class Logger:
@@ -49,19 +49,13 @@ class ServerManager:
49
49
  session = None
50
50
  try:
51
51
  session = self.client.get_session(server_name)
52
- logger.debug(
53
- f"Using existing session for server '{server_name}' to prefetch tools."
54
- )
52
+ logger.debug(f"Using existing session for server '{server_name}' to prefetch tools.")
55
53
  except ValueError:
56
54
  try:
57
55
  session = await self.client.create_session(server_name)
58
- logger.debug(
59
- f"Temporarily created session for '{server_name}' to prefetch tools"
60
- )
56
+ logger.debug(f"Temporarily created session for '{server_name}' to prefetch tools")
61
57
  except Exception:
62
- logger.warning(
63
- f"Could not create session for '{server_name}' during prefetch"
64
- )
58
+ logger.warning(f"Could not create session for '{server_name}' during prefetch")
65
59
  continue
66
60
 
67
61
  # Fetch tools if session is available
@@ -70,17 +64,12 @@ class ServerManager:
70
64
  tools = await self.adapter._create_tools_from_connectors([connector])
71
65
 
72
66
  # Check if this server's tools have changed
73
- if (
74
- server_name not in self._server_tools
75
- or self._server_tools[server_name] != tools
76
- ):
67
+ if server_name not in self._server_tools or self._server_tools[server_name] != tools:
77
68
  self._server_tools[server_name] = tools # Cache tools
78
69
  self.initialized_servers[server_name] = True # Mark as initialized
79
70
  logger.debug(f"Prefetched {len(tools)} tools for server '{server_name}'.")
80
71
  else:
81
- logger.debug(
82
- f"Tools for server '{server_name}' unchanged, using cached version."
83
- )
72
+ logger.debug(f"Tools for server '{server_name}' unchanged, using cached version.")
84
73
  except Exception as e:
85
74
  logger.error(f"Error prefetching tools for server '{server_name}': {e}")
86
75
 
@@ -17,9 +17,7 @@ class DisconnectServerTool(MCPServerTool):
17
17
  """Tool for disconnecting from the currently active MCP server."""
18
18
 
19
19
  name: ClassVar[str] = "disconnect_from_mcp_server"
20
- description: ClassVar[str] = (
21
- "Disconnect from the currently active MCP (Model Context Protocol) server"
22
- )
20
+ description: ClassVar[str] = "Disconnect from the currently active MCP (Model Context Protocol) server"
23
21
  args_schema: ClassVar[type[BaseModel]] = DisconnectServerInput
24
22
 
25
23
  def _run(self, **kwargs) -> str:
@@ -21,10 +21,7 @@ class GetActiveServerTool(MCPServerTool):
21
21
  def _run(self, **kwargs) -> str:
22
22
  """Get the currently active MCP server."""
23
23
  if not self.server_manager.active_server:
24
- return (
25
- "No MCP server is currently active. "
26
- "Use connect_to_mcp_server to connect to a server."
27
- )
24
+ return "No MCP server is currently active. Use connect_to_mcp_server to connect to a server."
28
25
  return f"Currently active MCP server: {self.server_manager.active_server}"
29
26
 
30
27
  async def _arun(self, **kwargs) -> str: