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

@@ -5,14 +5,27 @@ This module provides the base connector interface that all MCP connectors
5
5
  must implement.
6
6
  """
7
7
 
8
+ import warnings
8
9
  from abc import ABC, abstractmethod
9
10
  from datetime import timedelta
10
11
  from typing import Any
11
12
 
12
13
  from mcp import ClientSession, Implementation
13
- from mcp.client.session import ElicitationFnT, SamplingFnT
14
+ from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
14
15
  from mcp.shared.exceptions import McpError
15
- from mcp.types import CallToolResult, GetPromptResult, Prompt, ReadResourceResult, Resource, Tool
16
+ from mcp.types import (
17
+ CallToolResult,
18
+ GetPromptResult,
19
+ Prompt,
20
+ PromptListChangedNotification,
21
+ ReadResourceResult,
22
+ Resource,
23
+ ResourceListChangedNotification,
24
+ ServerCapabilities,
25
+ ServerNotification,
26
+ Tool,
27
+ ToolListChangedNotification,
28
+ )
16
29
  from pydantic import AnyUrl
17
30
 
18
31
  import mcp_use
@@ -31,6 +44,8 @@ class BaseConnector(ABC):
31
44
  self,
32
45
  sampling_callback: SamplingFnT | None = None,
33
46
  elicitation_callback: ElicitationFnT | None = None,
47
+ message_handler: MessageHandlerFnT | None = None,
48
+ logging_callback: LoggingFnT | None = None,
34
49
  ):
35
50
  """Initialize base connector with common attributes."""
36
51
  self.client_session: ClientSession | None = None
@@ -43,6 +58,9 @@ class BaseConnector(ABC):
43
58
  self.auto_reconnect = True # Whether to automatically reconnect on connection loss (not configurable for now)
44
59
  self.sampling_callback = sampling_callback
45
60
  self.elicitation_callback = elicitation_callback
61
+ self.message_handler = message_handler
62
+ self.logging_callback = logging_callback
63
+ self.capabilities: ServerCapabilities | None = None
46
64
 
47
65
  @property
48
66
  def client_info(self) -> Implementation:
@@ -53,6 +71,20 @@ class BaseConnector(ABC):
53
71
  url="https://github.com/mcp-use/mcp-use",
54
72
  )
55
73
 
74
+ async def _internal_message_handler(self, message: Any) -> None:
75
+ """Wrap the user-provided message handler."""
76
+ if isinstance(message, ServerNotification):
77
+ if isinstance(message.root, ToolListChangedNotification):
78
+ logger.debug("Received tool list changed notification")
79
+ elif isinstance(message.root, ResourceListChangedNotification):
80
+ logger.debug("Received resource list changed notification")
81
+ elif isinstance(message.root, PromptListChangedNotification):
82
+ logger.debug("Received prompt list changed notification")
83
+
84
+ # Call the user's handler
85
+ if self.message_handler:
86
+ await self.message_handler(message)
87
+
56
88
  @abstractmethod
57
89
  async def connect(self) -> None:
58
90
  """Establish a connection to the MCP implementation."""
@@ -125,37 +157,37 @@ class BaseConnector(ABC):
125
157
  result = await self.client_session.initialize()
126
158
  self._initialized = True # Mark as initialized
127
159
 
128
- server_capabilities = result.capabilities
160
+ self.capabilities = result.capabilities
129
161
 
130
- if server_capabilities.tools:
162
+ if self.capabilities.tools:
131
163
  # Get available tools directly from client session
132
164
  try:
133
165
  tools_result = await self.client_session.list_tools()
134
166
  self._tools = tools_result.tools if tools_result else []
135
167
  except Exception as e:
136
- logger.error(f"Error listing tools: {e}")
168
+ logger.error(f"Error listing tools for connector {self.public_identifier}: {e}")
137
169
  self._tools = []
138
170
  else:
139
171
  self._tools = []
140
172
 
141
- if server_capabilities.resources:
173
+ if self.capabilities.resources:
142
174
  # Get available resources directly from client session
143
175
  try:
144
176
  resources_result = await self.client_session.list_resources()
145
177
  self._resources = resources_result.resources if resources_result else []
146
178
  except Exception as e:
147
- logger.error(f"Error listing resources: {e}")
179
+ logger.error(f"Error listing resources for connector {self.public_identifier}: {e}")
148
180
  self._resources = []
149
181
  else:
150
182
  self._resources = []
151
183
 
152
- if server_capabilities.prompts:
184
+ if self.capabilities.prompts:
153
185
  # Get available prompts directly from client session
154
186
  try:
155
187
  prompts_result = await self.client_session.list_prompts()
156
188
  self._prompts = prompts_result.prompts if prompts_result else []
157
189
  except Exception as e:
158
- logger.error(f"Error listing prompts: {e}")
190
+ logger.error(f"Error listing prompts for connector {self.public_identifier}: {e}")
159
191
  self._prompts = []
160
192
  else:
161
193
  self._prompts = []
@@ -170,21 +202,57 @@ class BaseConnector(ABC):
170
202
 
171
203
  @property
172
204
  def tools(self) -> list[Tool]:
173
- """Get the list of available tools."""
205
+ """Get the list of available tools.
206
+
207
+ .. deprecated::
208
+ This property is deprecated because it may return stale data when the server
209
+ sends list change notifications. Use `await list_tools()` instead to ensure
210
+ you always get the latest data.
211
+ """
212
+ warnings.warn(
213
+ "The 'tools' property is deprecated and may return stale data. "
214
+ "Use 'await list_tools()' instead to ensure fresh data.",
215
+ DeprecationWarning,
216
+ stacklevel=2,
217
+ )
174
218
  if self._tools is None:
175
219
  raise RuntimeError("MCP client is not initialized")
176
220
  return self._tools
177
221
 
178
222
  @property
179
223
  def resources(self) -> list[Resource]:
180
- """Get the list of available resources."""
224
+ """Get the list of available resources.
225
+
226
+ .. deprecated::
227
+ This property is deprecated because it may return stale data when the server
228
+ sends list change notifications. Use `await list_resources()` instead to ensure
229
+ you always get the latest data.
230
+ """
231
+ warnings.warn(
232
+ "The 'resources' property is deprecated and may return stale data. "
233
+ "Use 'await list_resources()' instead to ensure fresh data.",
234
+ DeprecationWarning,
235
+ stacklevel=2,
236
+ )
181
237
  if self._resources is None:
182
238
  raise RuntimeError("MCP client is not initialized")
183
239
  return self._resources
184
240
 
185
241
  @property
186
242
  def prompts(self) -> list[Prompt]:
187
- """Get the list of available prompts."""
243
+ """Get the list of available prompts.
244
+
245
+ .. deprecated::
246
+ This property is deprecated because it may return stale data when the server
247
+ sends list change notifications. Use `await list_prompts()' instead to ensure
248
+ you always get the latest data.
249
+ """
250
+ warnings.warn(
251
+ "The 'prompts' property is deprecated and may return stale data. "
252
+ "Use 'await list_prompts()' instead to ensure fresh data.",
253
+ DeprecationWarning,
254
+ stacklevel=2,
255
+ )
188
256
  if self._prompts is None:
189
257
  raise RuntimeError("MCP client is not initialized")
190
258
  return self._prompts
@@ -303,28 +371,39 @@ class BaseConnector(ABC):
303
371
  async def list_tools(self) -> list[Tool]:
304
372
  """List all available tools from the MCP implementation."""
305
373
 
374
+ if self.capabilities and not self.capabilities.tools:
375
+ logger.debug(f"Server {self.public_identifier} does not support tools")
376
+ return []
377
+
306
378
  # Ensure we're connected
307
379
  await self._ensure_connected()
308
380
 
309
381
  logger.debug("Listing tools")
310
382
  try:
311
383
  result = await self.client_session.list_tools()
384
+ self._tools = result.tools
312
385
  return result.tools
313
386
  except McpError as e:
314
- logger.error(f"Error listing tools: {e}")
387
+ logger.error(f"Error listing tools for connector {self.public_identifier}: {e}")
315
388
  return []
316
389
 
317
390
  async def list_resources(self) -> list[Resource]:
318
391
  """List all available resources from the MCP implementation."""
392
+
393
+ if self.capabilities and not self.capabilities.resources:
394
+ logger.debug(f"Server {self.public_identifier} does not support resources")
395
+ return []
396
+
319
397
  # Ensure we're connected
320
398
  await self._ensure_connected()
321
399
 
322
400
  logger.debug("Listing resources")
323
401
  try:
324
402
  result = await self.client_session.list_resources()
403
+ self._resources = result.resources
325
404
  return result.resources
326
405
  except McpError as e:
327
- logger.error(f"Error listing resources: {e}")
406
+ logger.warning(f"Error listing resources for connector {self.public_identifier}: {e}")
328
407
  return []
329
408
 
330
409
  async def read_resource(self, uri: AnyUrl) -> ReadResourceResult:
@@ -337,14 +416,20 @@ class BaseConnector(ABC):
337
416
 
338
417
  async def list_prompts(self) -> list[Prompt]:
339
418
  """List all available prompts from the MCP implementation."""
419
+
420
+ if self.capabilities and not self.capabilities.prompts:
421
+ logger.debug(f"Server {self.public_identifier} does not support prompts")
422
+ return []
423
+
340
424
  await self._ensure_connected()
341
425
 
342
426
  logger.debug("Listing prompts")
343
427
  try:
344
428
  result = await self.client_session.list_prompts()
429
+ self._prompts = result.prompts
345
430
  return result.prompts
346
431
  except McpError as e:
347
- logger.error(f"Error listing prompts: {e}")
432
+ logger.error(f"Error listing prompts for connector {self.public_identifier}: {e}")
348
433
  return []
349
434
 
350
435
  async def get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> GetPromptResult:
@@ -7,7 +7,7 @@ through HTTP APIs with SSE or Streamable HTTP for transport.
7
7
 
8
8
  import httpx
9
9
  from mcp import ClientSession
10
- from mcp.client.session import ElicitationFnT, SamplingFnT
10
+ from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
11
11
 
12
12
  from ..logging import logger
13
13
  from ..task_managers import SseConnectionManager, StreamableHttpConnectionManager
@@ -30,6 +30,8 @@ class HttpConnector(BaseConnector):
30
30
  sse_read_timeout: float = 60 * 5,
31
31
  sampling_callback: SamplingFnT | None = None,
32
32
  elicitation_callback: ElicitationFnT | None = None,
33
+ message_handler: MessageHandlerFnT | None = None,
34
+ logging_callback: LoggingFnT | None = None,
33
35
  ):
34
36
  """Initialize a new HTTP connector.
35
37
 
@@ -42,7 +44,12 @@ class HttpConnector(BaseConnector):
42
44
  sampling_callback: Optional sampling callback.
43
45
  elicitation_callback: Optional elicitation callback.
44
46
  """
45
- super().__init__(sampling_callback=sampling_callback, elicitation_callback=elicitation_callback)
47
+ super().__init__(
48
+ sampling_callback=sampling_callback,
49
+ elicitation_callback=elicitation_callback,
50
+ message_handler=message_handler,
51
+ logging_callback=logging_callback,
52
+ )
46
53
  self.base_url = base_url.rstrip("/")
47
54
  self.auth_token = auth_token
48
55
  self.headers = headers or {}
@@ -78,6 +85,8 @@ class HttpConnector(BaseConnector):
78
85
  write_stream,
79
86
  sampling_callback=self.sampling_callback,
80
87
  elicitation_callback=self.elicitation_callback,
88
+ message_handler=self._internal_message_handler,
89
+ logging_callback=self.logging_callback,
81
90
  client_info=self.client_info,
82
91
  )
83
92
  await test_client.__aenter__()
@@ -162,6 +171,8 @@ class HttpConnector(BaseConnector):
162
171
  write_stream,
163
172
  sampling_callback=self.sampling_callback,
164
173
  elicitation_callback=self.elicitation_callback,
174
+ message_handler=self._internal_message_handler,
175
+ logging_callback=self.logging_callback,
165
176
  client_info=self.client_info,
166
177
  )
167
178
  await self.client_session.__aenter__()
@@ -12,7 +12,7 @@ import time
12
12
 
13
13
  import aiohttp
14
14
  from mcp import ClientSession
15
- from mcp.client.session import ElicitationFnT, SamplingFnT
15
+ from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
16
16
 
17
17
  from ..logging import logger
18
18
  from ..task_managers import SseConnectionManager
@@ -20,10 +20,7 @@ from ..task_managers import SseConnectionManager
20
20
  # Import E2B SDK components (optional dependency)
21
21
  try:
22
22
  logger.debug("Attempting to import e2b_code_interpreter...")
23
- from e2b_code_interpreter import (
24
- CommandHandle,
25
- Sandbox,
26
- )
23
+ from e2b_code_interpreter import CommandHandle, Sandbox
27
24
 
28
25
  logger.debug("Successfully imported e2b_code_interpreter")
29
26
  except ImportError as e:
@@ -53,6 +50,8 @@ class SandboxConnector(BaseConnector):
53
50
  sse_read_timeout: float = 60 * 5,
54
51
  sampling_callback: SamplingFnT | None = None,
55
52
  elicitation_callback: ElicitationFnT | None = None,
53
+ message_handler: MessageHandlerFnT | None = None,
54
+ logging_callback: LoggingFnT | None = None,
56
55
  ):
57
56
  """Initialize a new sandbox connector.
58
57
 
@@ -67,7 +66,12 @@ class SandboxConnector(BaseConnector):
67
66
  sampling_callback: Optional sampling callback.
68
67
  elicitation_callback: Optional elicitation callback.
69
68
  """
70
- super().__init__(sampling_callback=sampling_callback, elicitation_callback=elicitation_callback)
69
+ super().__init__(
70
+ sampling_callback=sampling_callback,
71
+ elicitation_callback=elicitation_callback,
72
+ message_handler=message_handler,
73
+ logging_callback=logging_callback,
74
+ )
71
75
  if Sandbox is None:
72
76
  raise ImportError(
73
77
  "E2B SDK (e2b-code-interpreter) not found. Please install it with "
@@ -227,6 +231,8 @@ class SandboxConnector(BaseConnector):
227
231
  write_stream,
228
232
  sampling_callback=self.sampling_callback,
229
233
  elicitation_callback=self.elicitation_callback,
234
+ message_handler=self._internal_message_handler,
235
+ logging_callback=self.logging_callback,
230
236
  client_info=self.client_info,
231
237
  )
232
238
  await self.client_session.__aenter__()
@@ -8,7 +8,7 @@ through the standard input/output streams.
8
8
  import sys
9
9
 
10
10
  from mcp import ClientSession, StdioServerParameters
11
- from mcp.client.session import ElicitationFnT, SamplingFnT
11
+ from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
12
12
 
13
13
  from ..logging import logger
14
14
  from ..task_managers import StdioConnectionManager
@@ -31,6 +31,8 @@ class StdioConnector(BaseConnector):
31
31
  errlog=sys.stderr,
32
32
  sampling_callback: SamplingFnT | None = None,
33
33
  elicitation_callback: ElicitationFnT | None = None,
34
+ message_handler: MessageHandlerFnT | None = None,
35
+ logging_callback: LoggingFnT | None = None,
34
36
  ):
35
37
  """Initialize a new stdio connector.
36
38
 
@@ -42,7 +44,12 @@ class StdioConnector(BaseConnector):
42
44
  sampling_callback: Optional callback to sample the client.
43
45
  elicitation_callback: Optional callback to elicit the client.
44
46
  """
45
- super().__init__(sampling_callback=sampling_callback, elicitation_callback=elicitation_callback)
47
+ super().__init__(
48
+ sampling_callback=sampling_callback,
49
+ elicitation_callback=elicitation_callback,
50
+ message_handler=message_handler,
51
+ logging_callback=logging_callback,
52
+ )
46
53
  self.command = command
47
54
  self.args = args or [] # Ensure args is never None
48
55
  self.env = env
@@ -69,6 +76,8 @@ class StdioConnector(BaseConnector):
69
76
  write_stream,
70
77
  sampling_callback=self.sampling_callback,
71
78
  elicitation_callback=self.elicitation_callback,
79
+ message_handler=self._internal_message_handler,
80
+ logging_callback=self.logging_callback,
72
81
  client_info=self.client_info,
73
82
  )
74
83
  await self.client_session.__aenter__()
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,29 @@
1
+ import traceback
2
+
3
+ from ..logging import logger
4
+
5
+ retryable_exceptions = (TimeoutError, ConnectionError) # We can add more exceptions here
6
+
7
+
8
+ def format_error(error: Exception, **context) -> dict:
9
+ """
10
+ Formats an exception into a structured format that can be understood by LLMs.
11
+
12
+ Args:
13
+ error: The exception to format.
14
+ **context: Additional context to include in the formatted error.
15
+
16
+ Returns:
17
+ A dictionary containing the formatted error.
18
+ """
19
+ formatted_context = {
20
+ "error": type(error).__name__,
21
+ "details": str(error),
22
+ "isRetryable": isinstance(error, retryable_exceptions),
23
+ "stack": traceback.format_exc(),
24
+ "code": getattr(error, "code", "UNKNOWN"),
25
+ }
26
+ formatted_context.update(context)
27
+
28
+ logger.error(f"Structured error: {formatted_context}") # For observability (maybe remove later)
29
+ return formatted_context
mcp_use/logging.py CHANGED
@@ -80,6 +80,9 @@ class Logger:
80
80
 
81
81
  root_logger.setLevel(level)
82
82
 
83
+ # Set propagate to True to ensure child loggers inherit settings
84
+ root_logger.propagate = True
85
+
83
86
  # Clear existing handlers
84
87
  for handler in root_logger.handlers[:]:
85
88
  root_logger.removeHandler(handler)
@@ -91,6 +94,7 @@ class Logger:
91
94
  if log_to_console:
92
95
  console_handler = logging.StreamHandler(sys.stdout)
93
96
  console_handler.setFormatter(formatter)
97
+ console_handler.setLevel(level) # Ensure handler respects the level
94
98
  root_logger.addHandler(console_handler)
95
99
 
96
100
  # Add file handler if requested
@@ -102,6 +106,7 @@ class Logger:
102
106
 
103
107
  file_handler = logging.FileHandler(log_to_file)
104
108
  file_handler.setFormatter(formatter)
109
+ file_handler.setLevel(level) # Ensure handler respects the level
105
110
  root_logger.addHandler(file_handler)
106
111
 
107
112
  @classmethod
@@ -114,24 +119,34 @@ class Logger:
114
119
  global MCP_USE_DEBUG
115
120
  MCP_USE_DEBUG = debug_level
116
121
 
117
- # Update log level for existing loggers
122
+ # Determine the target level
118
123
  if debug_level == 2:
119
- for logger in cls._loggers.values():
120
- logger.setLevel(logging.DEBUG)
121
- langchain_set_debug(True)
124
+ target_level = logging.DEBUG
125
+ langchain_set_debug(True)
122
126
  elif debug_level == 1:
123
- for logger in cls._loggers.values():
124
- logger.setLevel(logging.INFO)
125
- langchain_set_debug(False)
127
+ target_level = logging.INFO
128
+ langchain_set_debug(False)
126
129
  else:
127
- # Reset to default (WARNING)
128
- for logger in cls._loggers.values():
129
- logger.setLevel(logging.WARNING)
130
- langchain_set_debug(False)
130
+ target_level = logging.WARNING
131
+ langchain_set_debug(False)
132
+
133
+ # Update log level for existing loggers in our registry
134
+ for logger in cls._loggers.values():
135
+ logger.setLevel(target_level)
136
+ # Also update handler levels
137
+ for handler in logger.handlers:
138
+ handler.setLevel(target_level)
139
+
140
+ # Also update all mcp_use child loggers that might exist
141
+ # This ensures loggers created with logging.getLogger() are also updated
142
+ base_logger = logging.getLogger("mcp_use")
143
+ base_logger.setLevel(target_level)
144
+ for handler in base_logger.handlers:
145
+ handler.setLevel(target_level)
131
146
 
132
147
 
133
148
  # Check environment variable for debug flag
134
- debug_env = os.environ.get("DEBUG", "").lower()
149
+ debug_env = os.environ.get("MCP_USE_DEBUG", "").lower() or os.environ.get("DEBUG", "").lower()
135
150
  if debug_env == "2":
136
151
  MCP_USE_DEBUG = 2
137
152
  elif debug_env == "1":
@@ -0,0 +1,36 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from langchain_core.tools import BaseTool
4
+
5
+
6
+ class BaseServerManager(ABC):
7
+ """Abstract base class for server managers.
8
+
9
+ This class defines the interface for server managers that can be used with MCPAgent.
10
+ Custom server managers should inherit from this class and implement the required methods.
11
+ """
12
+
13
+ @abstractmethod
14
+ async def initialize(self) -> None:
15
+ """Initialize the server manager."""
16
+ raise NotImplementedError
17
+
18
+ @property
19
+ @abstractmethod
20
+ def tools(self) -> list[BaseTool]:
21
+ """Get all server management tools and tools from the active server.
22
+
23
+ Returns:
24
+ list of LangChain tools for server management plus tools from active server
25
+ """
26
+ raise NotImplementedError
27
+
28
+ @abstractmethod
29
+ def has_tool_changes(self, current_tool_names: set[str]) -> bool:
30
+ """Check if the available tools have changed.
31
+ Args:
32
+ current_tool_names: Set of currently known tool names
33
+ Returns:
34
+ True if tools have changed, False otherwise
35
+ """
36
+ raise NotImplementedError
@@ -4,16 +4,11 @@ from mcp_use.client import MCPClient
4
4
  from mcp_use.logging import logger
5
5
 
6
6
  from ..adapters.base import BaseAdapter
7
- from .tools import (
8
- ConnectServerTool,
9
- DisconnectServerTool,
10
- GetActiveServerTool,
11
- ListServersTool,
12
- SearchToolsTool,
13
- )
7
+ from .base import BaseServerManager
8
+ from .tools import ConnectServerTool, DisconnectServerTool, GetActiveServerTool, ListServersTool, SearchToolsTool
14
9
 
15
10
 
16
- class ServerManager:
11
+ class ServerManager(BaseServerManager):
17
12
  """Manages MCP servers and provides tools for server selection and management.
18
13
 
19
14
  This class allows an agent to discover and select which MCP server to use,
@@ -2,6 +2,7 @@ from typing import ClassVar
2
2
 
3
3
  from pydantic import BaseModel, Field
4
4
 
5
+ from mcp_use.errors.error_formatting import format_error
5
6
  from mcp_use.logging import logger
6
7
 
7
8
  from .base_tool import MCPServerTool
@@ -62,7 +63,7 @@ class ConnectServerTool(MCPServerTool):
62
63
 
63
64
  except Exception as e:
64
65
  logger.error(f"Error connecting to server '{server_name}': {e}")
65
- return f"Failed to connect to server '{server_name}': {str(e)}"
66
+ return format_error(e, server_name=server_name)
66
67
 
67
68
  def _run(self, server_name: str) -> str:
68
69
  """Synchronous version that raises a NotImplementedError - use _arun instead."""
@@ -2,6 +2,7 @@ from typing import ClassVar
2
2
 
3
3
  from pydantic import BaseModel
4
4
 
5
+ from mcp_use.errors.error_formatting import format_error
5
6
  from mcp_use.logging import logger
6
7
 
7
8
  from .base_tool import MCPServerTool
@@ -36,7 +37,7 @@ class DisconnectServerTool(MCPServerTool):
36
37
  return f"Successfully disconnected from MCP server '{server_name}'."
37
38
  except Exception as e:
38
39
  logger.error(f"Error disconnecting from server '{server_name}': {e}")
39
- return f"Failed to disconnect from server '{server_name}': {str(e)}"
40
+ return format_error(e, server_name=server_name)
40
41
 
41
42
  async def _arun(self, **kwargs) -> str:
42
43
  """Async implementation of _run."""
@@ -2,6 +2,7 @@ from typing import ClassVar
2
2
 
3
3
  from pydantic import BaseModel
4
4
 
5
+ from mcp_use.errors.error_formatting import format_error
5
6
  from mcp_use.logging import logger
6
7
 
7
8
  from .base_tool import MCPServerTool
@@ -44,6 +45,7 @@ class ListServersTool(MCPServerTool):
44
45
  result += f" {tool_count} tools available for this server\n"
45
46
  except Exception as e:
46
47
  logger.error(f"Unexpected error listing tools for server '{server_name}': {e}")
48
+ return format_error(e, server=server_name, operation="list_tools")
47
49
 
48
50
  return result
49
51
 
@@ -4,5 +4,6 @@ from dotenv import load_dotenv
4
4
  load_dotenv()
5
5
 
6
6
  from . import laminar, langfuse # noqa
7
+ from .callbacks_manager import ObservabilityManager, get_default_manager, create_manager # noqa
7
8
 
8
- __all__ = ["laminar", "langfuse"]
9
+ __all__ = ["laminar", "langfuse", "ObservabilityManager", "get_default_manager", "create_manager"]