mcp-use 1.3.12__py3-none-any.whl → 1.3.13__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 (101) hide show
  1. mcp_use/__init__.py +1 -1
  2. mcp_use/adapters/.deprecated +0 -0
  3. mcp_use/adapters/__init__.py +18 -7
  4. mcp_use/adapters/base.py +12 -185
  5. mcp_use/adapters/langchain_adapter.py +12 -219
  6. mcp_use/agents/adapters/__init__.py +10 -0
  7. mcp_use/agents/adapters/base.py +193 -0
  8. mcp_use/agents/adapters/langchain_adapter.py +228 -0
  9. mcp_use/agents/base.py +1 -1
  10. mcp_use/agents/managers/__init__.py +19 -0
  11. mcp_use/agents/managers/base.py +36 -0
  12. mcp_use/agents/managers/server_manager.py +131 -0
  13. mcp_use/agents/managers/tools/__init__.py +15 -0
  14. mcp_use/agents/managers/tools/base_tool.py +19 -0
  15. mcp_use/agents/managers/tools/connect_server.py +69 -0
  16. mcp_use/agents/managers/tools/disconnect_server.py +43 -0
  17. mcp_use/agents/managers/tools/get_active_server.py +29 -0
  18. mcp_use/agents/managers/tools/list_servers_tool.py +53 -0
  19. mcp_use/agents/managers/tools/search_tools.py +328 -0
  20. mcp_use/agents/mcpagent.py +16 -14
  21. mcp_use/agents/remote.py +14 -1
  22. mcp_use/auth/.deprecated +0 -0
  23. mcp_use/auth/__init__.py +19 -4
  24. mcp_use/auth/bearer.py +11 -12
  25. mcp_use/auth/oauth.py +11 -620
  26. mcp_use/auth/oauth_callback.py +16 -207
  27. mcp_use/client/__init__.py +1 -0
  28. mcp_use/client/auth/__init__.py +6 -0
  29. mcp_use/client/auth/bearer.py +23 -0
  30. mcp_use/client/auth/oauth.py +629 -0
  31. mcp_use/client/auth/oauth_callback.py +214 -0
  32. mcp_use/client/client.py +356 -0
  33. mcp_use/client/config.py +106 -0
  34. mcp_use/client/connectors/__init__.py +20 -0
  35. mcp_use/client/connectors/base.py +470 -0
  36. mcp_use/client/connectors/http.py +304 -0
  37. mcp_use/client/connectors/sandbox.py +332 -0
  38. mcp_use/client/connectors/stdio.py +109 -0
  39. mcp_use/client/connectors/utils.py +13 -0
  40. mcp_use/client/connectors/websocket.py +257 -0
  41. mcp_use/client/exceptions.py +31 -0
  42. mcp_use/client/middleware/__init__.py +50 -0
  43. mcp_use/client/middleware/logging.py +31 -0
  44. mcp_use/client/middleware/metrics.py +314 -0
  45. mcp_use/client/middleware/middleware.py +266 -0
  46. mcp_use/client/session.py +162 -0
  47. mcp_use/client/task_managers/__init__.py +20 -0
  48. mcp_use/client/task_managers/base.py +145 -0
  49. mcp_use/client/task_managers/sse.py +84 -0
  50. mcp_use/client/task_managers/stdio.py +69 -0
  51. mcp_use/client/task_managers/streamable_http.py +86 -0
  52. mcp_use/client/task_managers/websocket.py +68 -0
  53. mcp_use/client.py +12 -344
  54. mcp_use/config.py +20 -97
  55. mcp_use/connectors/.deprecated +0 -0
  56. mcp_use/connectors/__init__.py +46 -20
  57. mcp_use/connectors/base.py +12 -455
  58. mcp_use/connectors/http.py +13 -300
  59. mcp_use/connectors/sandbox.py +13 -306
  60. mcp_use/connectors/stdio.py +13 -104
  61. mcp_use/connectors/utils.py +15 -8
  62. mcp_use/connectors/websocket.py +13 -252
  63. mcp_use/exceptions.py +33 -18
  64. mcp_use/managers/.deprecated +0 -0
  65. mcp_use/managers/__init__.py +56 -17
  66. mcp_use/managers/base.py +13 -31
  67. mcp_use/managers/server_manager.py +13 -119
  68. mcp_use/managers/tools/__init__.py +45 -15
  69. mcp_use/managers/tools/base_tool.py +5 -16
  70. mcp_use/managers/tools/connect_server.py +5 -67
  71. mcp_use/managers/tools/disconnect_server.py +5 -41
  72. mcp_use/managers/tools/get_active_server.py +5 -26
  73. mcp_use/managers/tools/list_servers_tool.py +5 -51
  74. mcp_use/managers/tools/search_tools.py +17 -321
  75. mcp_use/middleware/.deprecated +0 -0
  76. mcp_use/middleware/__init__.py +89 -50
  77. mcp_use/middleware/logging.py +14 -26
  78. mcp_use/middleware/metrics.py +30 -303
  79. mcp_use/middleware/middleware.py +39 -246
  80. mcp_use/session.py +13 -149
  81. mcp_use/task_managers/.deprecated +0 -0
  82. mcp_use/task_managers/__init__.py +48 -20
  83. mcp_use/task_managers/base.py +13 -140
  84. mcp_use/task_managers/sse.py +13 -79
  85. mcp_use/task_managers/stdio.py +13 -64
  86. mcp_use/task_managers/streamable_http.py +15 -81
  87. mcp_use/task_managers/websocket.py +13 -63
  88. mcp_use/telemetry/events.py +58 -0
  89. mcp_use/telemetry/telemetry.py +71 -1
  90. mcp_use/types/.deprecated +0 -0
  91. mcp_use/types/sandbox.py +13 -18
  92. {mcp_use-1.3.12.dist-info → mcp_use-1.3.13.dist-info}/METADATA +59 -34
  93. mcp_use-1.3.13.dist-info/RECORD +109 -0
  94. mcp_use-1.3.12.dist-info/RECORD +0 -64
  95. mcp_use-1.3.12.dist-info/licenses/LICENSE +0 -21
  96. /mcp_use/{observability → agents/observability}/__init__.py +0 -0
  97. /mcp_use/{observability → agents/observability}/callbacks_manager.py +0 -0
  98. /mcp_use/{observability → agents/observability}/laminar.py +0 -0
  99. /mcp_use/{observability → agents/observability}/langfuse.py +0 -0
  100. {mcp_use-1.3.12.dist-info → mcp_use-1.3.13.dist-info}/WHEEL +0 -0
  101. {mcp_use-1.3.12.dist-info → mcp_use-1.3.13.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,228 @@
1
+ """
2
+ LangChain adapter for MCP tools.
3
+
4
+ This module provides utilities to convert MCP tools to LangChain tools.
5
+ """
6
+
7
+ import re
8
+ from typing import Any, NoReturn
9
+
10
+ from jsonschema_pydantic import jsonschema_to_pydantic
11
+ from langchain_core.tools import BaseTool
12
+ from mcp.types import (
13
+ CallToolResult,
14
+ Prompt,
15
+ ReadResourceRequestParams,
16
+ Resource,
17
+ )
18
+ from pydantic import BaseModel, Field, create_model
19
+
20
+ from mcp_use.agents.adapters.base import BaseAdapter
21
+ from mcp_use.client.connectors.base import BaseConnector
22
+ from mcp_use.errors.error_formatting import format_error
23
+ from mcp_use.logging import logger
24
+ from mcp_use.telemetry.telemetry import telemetry
25
+
26
+
27
+ class LangChainAdapter(BaseAdapter):
28
+ """Adapter for converting MCP tools to LangChain tools."""
29
+
30
+ def __init__(self, disallowed_tools: list[str] | None = None) -> None:
31
+ """Initialize a new LangChain adapter.
32
+
33
+ Args:
34
+ disallowed_tools: list of tool names that should not be available.
35
+ """
36
+ super().__init__(disallowed_tools)
37
+ self._connector_tool_map: dict[BaseConnector, list[BaseTool]] = {}
38
+
39
+ @telemetry("adapter_fix_schema")
40
+ def fix_schema(self, schema: dict) -> dict:
41
+ """Convert JSON Schema 'type': ['string', 'null'] to 'anyOf' format and fix enum handling.
42
+
43
+ Args:
44
+ schema: The JSON schema to fix.
45
+
46
+ Returns:
47
+ The fixed JSON schema.
48
+ """
49
+ if isinstance(schema, dict):
50
+ if "type" in schema and isinstance(schema["type"], list):
51
+ schema["anyOf"] = [{"type": t} for t in schema["type"]]
52
+ del schema["type"] # Remove 'type' and standardize to 'anyOf'
53
+
54
+ # Fix enum handling - ensure enum fields are properly typed as strings
55
+ if "enum" in schema and "type" not in schema:
56
+ schema["type"] = "string"
57
+
58
+ for key, value in schema.items():
59
+ schema[key] = self.fix_schema(value) # Apply recursively
60
+ return schema
61
+
62
+ @telemetry("adapter_convert_tool")
63
+ def _convert_tool(self, mcp_tool: dict[str, Any], connector: BaseConnector) -> BaseTool:
64
+ """Convert an MCP tool to LangChain's tool format.
65
+
66
+ Args:
67
+ mcp_tool: The MCP tool to convert.
68
+ connector: The connector that provides this tool.
69
+
70
+ Returns:
71
+ A LangChain BaseTool.
72
+ """
73
+ # Skip disallowed tools
74
+ if mcp_tool.name in self.disallowed_tools:
75
+ return None
76
+
77
+ # This is a dynamic class creation, we need to work with the self reference
78
+ adapter_self = self
79
+
80
+ class McpToLangChainAdapter(BaseTool):
81
+ name: str = mcp_tool.name or "NO NAME"
82
+ description: str = mcp_tool.description or ""
83
+ # Convert JSON schema to Pydantic model for argument validation
84
+ args_schema: type[BaseModel] = jsonschema_to_pydantic(
85
+ adapter_self.fix_schema(mcp_tool.inputSchema) # Apply schema conversion
86
+ )
87
+ tool_connector: BaseConnector = connector # Renamed variable to avoid name conflict
88
+ handle_tool_error: bool = True
89
+
90
+ def __repr__(self) -> str:
91
+ return f"MCP tool: {self.name}: {self.description}"
92
+
93
+ def _run(self, **kwargs: Any) -> NoReturn:
94
+ """Synchronous run method that always raises an error.
95
+
96
+ Raises:
97
+ NotImplementedError: Always raises this error because MCP tools
98
+ only support async operations.
99
+ """
100
+ raise NotImplementedError("MCP tools only support async operations")
101
+
102
+ async def _arun(self, **kwargs: Any) -> str | dict:
103
+ """Asynchronously execute the tool with given arguments.
104
+
105
+ Args:
106
+ kwargs: The arguments to pass to the tool.
107
+
108
+ Returns:
109
+ The result of the tool execution.
110
+
111
+ Raises:
112
+ ToolException: If tool execution fails.
113
+ """
114
+ logger.debug(f'MCP tool: "{self.name}" received input: {kwargs}')
115
+
116
+ try:
117
+ tool_result: CallToolResult = await self.tool_connector.call_tool(self.name, kwargs)
118
+ try:
119
+ return str(tool_result.content)
120
+ except Exception as e:
121
+ # Log the exception for debugging
122
+ logger.error(f"Error parsing tool result: {e}")
123
+ return format_error(e, tool=self.name, tool_content=tool_result.content)
124
+
125
+ except Exception as e:
126
+ if self.handle_tool_error:
127
+ return format_error(e, tool=self.name) # Format the error to make LLM understand it
128
+ raise
129
+
130
+ return McpToLangChainAdapter()
131
+
132
+ def _convert_resource(self, mcp_resource: Resource, connector: BaseConnector) -> BaseTool:
133
+ """Convert an MCP resource to LangChain's tool format.
134
+
135
+ Each resource becomes an async tool that returns its content when called.
136
+ The tool takes **no** arguments because the resource URI is fixed.
137
+ """
138
+
139
+ def _sanitize(name: str) -> str:
140
+ return re.sub(r"[^A-Za-z0-9_]+", "_", name).lower().strip("_")
141
+
142
+ class ResourceTool(BaseTool):
143
+ name: str = _sanitize(mcp_resource.name or f"resource_{mcp_resource.uri}")
144
+ description: str = (
145
+ mcp_resource.description or f"Return the content of the resource located at URI {mcp_resource.uri}."
146
+ )
147
+ args_schema: type[BaseModel] = ReadResourceRequestParams
148
+ tool_connector: BaseConnector = connector
149
+ handle_tool_error: bool = True
150
+
151
+ def _run(self, **kwargs: Any) -> NoReturn:
152
+ raise NotImplementedError("Resource tools only support async operations")
153
+
154
+ async def _arun(self, **kwargs: Any) -> Any:
155
+ logger.debug(f'Resource tool: "{self.name}" called')
156
+ try:
157
+ result = await self.tool_connector.read_resource(mcp_resource.uri)
158
+ for content in result.contents:
159
+ # Attempt to decode bytes if necessary
160
+ if isinstance(content, bytes):
161
+ content_decoded = content.decode()
162
+ else:
163
+ content_decoded = str(content)
164
+
165
+ return content_decoded
166
+ except Exception as e:
167
+ if self.handle_tool_error:
168
+ return format_error(e, tool=self.name) # Format the error to make LLM understand it
169
+ raise
170
+
171
+ return ResourceTool()
172
+
173
+ def _convert_prompt(self, mcp_prompt: Prompt, connector: BaseConnector) -> BaseTool:
174
+ """Convert an MCP prompt to LangChain's tool format.
175
+
176
+ The resulting tool executes `get_prompt` on the connector with the prompt's name and
177
+ the user-provided arguments (if any). The tool returns the decoded prompt content.
178
+ """
179
+ prompt_arguments = mcp_prompt.arguments
180
+
181
+ # Sanitize the prompt name to create a valid Python identifier for the model name
182
+ base_model_name = re.sub(r"[^a-zA-Z0-9_]", "_", mcp_prompt.name)
183
+ if not base_model_name or base_model_name[0].isdigit():
184
+ base_model_name = "PromptArgs_" + base_model_name
185
+ dynamic_model_name = f"{base_model_name}_InputSchema"
186
+
187
+ if prompt_arguments:
188
+ field_definitions_for_create: dict[str, Any] = {}
189
+ for arg in prompt_arguments:
190
+ param_type: type = getattr(arg, "type", str)
191
+ if arg.required:
192
+ field_definitions_for_create[arg.name] = (
193
+ param_type,
194
+ Field(description=arg.description),
195
+ )
196
+ else:
197
+ field_definitions_for_create[arg.name] = (
198
+ param_type | None,
199
+ Field(None, description=arg.description),
200
+ )
201
+
202
+ InputSchema = create_model(dynamic_model_name, **field_definitions_for_create, __base__=BaseModel)
203
+ else:
204
+ # Create an empty Pydantic model if there are no arguments
205
+ InputSchema = create_model(dynamic_model_name, __base__=BaseModel)
206
+
207
+ class PromptTool(BaseTool):
208
+ name: str = mcp_prompt.name
209
+ description: str = mcp_prompt.description
210
+
211
+ args_schema: type[BaseModel] = InputSchema
212
+ tool_connector: BaseConnector = connector
213
+ handle_tool_error: bool = True
214
+
215
+ def _run(self, **kwargs: Any) -> NoReturn:
216
+ raise NotImplementedError("Prompt tools only support async operations")
217
+
218
+ async def _arun(self, **kwargs: Any) -> Any:
219
+ logger.debug(f'Prompt tool: "{self.name}" called with args: {kwargs}')
220
+ try:
221
+ result = await self.tool_connector.get_prompt(self.name, kwargs)
222
+ return result.messages
223
+ except Exception as e:
224
+ if self.handle_tool_error:
225
+ return format_error(e, tool=self.name) # Format the error to make LLM understand it
226
+ raise
227
+
228
+ return PromptTool()
mcp_use/agents/base.py CHANGED
@@ -7,7 +7,7 @@ This module provides a base class for agents that use MCP tools.
7
7
  from abc import ABC, abstractmethod
8
8
  from typing import Any
9
9
 
10
- from ..session import MCPSession
10
+ from mcp_use.client.session import MCPSession
11
11
 
12
12
 
13
13
  class BaseAgent(ABC):
@@ -0,0 +1,19 @@
1
+ from .server_manager import ServerManager
2
+ from .tools import (
3
+ ConnectServerTool,
4
+ DisconnectServerTool,
5
+ GetActiveServerTool,
6
+ ListServersTool,
7
+ MCPServerTool,
8
+ SearchToolsTool,
9
+ )
10
+
11
+ __all__ = [
12
+ "ServerManager",
13
+ "MCPServerTool",
14
+ "ConnectServerTool",
15
+ "DisconnectServerTool",
16
+ "GetActiveServerTool",
17
+ "ListServersTool",
18
+ "SearchToolsTool",
19
+ ]
@@ -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
@@ -0,0 +1,131 @@
1
+ from langchain_core.tools import BaseTool
2
+
3
+ from mcp_use.agents.adapters.base import BaseAdapter
4
+ from mcp_use.agents.managers.base import BaseServerManager
5
+ from mcp_use.agents.managers.tools import (
6
+ ConnectServerTool,
7
+ DisconnectServerTool,
8
+ GetActiveServerTool,
9
+ ListServersTool,
10
+ SearchToolsTool,
11
+ )
12
+ from mcp_use.client import MCPClient
13
+ from mcp_use.logging import logger
14
+ from mcp_use.telemetry.telemetry import telemetry
15
+
16
+
17
+ class ServerManager(BaseServerManager):
18
+ """Manages MCP servers and provides tools for server selection and management.
19
+
20
+ This class allows an agent to discover and select which MCP server to use,
21
+ dynamically activating the tools for the selected server.
22
+ """
23
+
24
+ def __init__(self, client: MCPClient, adapter: BaseAdapter) -> None:
25
+ """Initialize the server manager.
26
+
27
+ Args:
28
+ client: The MCPClient instance managing server connections
29
+ adapter: The LangChainAdapter for converting MCP tools to LangChain tools
30
+ """
31
+ self.client = client
32
+ self.adapter = adapter
33
+ self.active_server: str | None = None
34
+ self.initialized_servers: dict[str, bool] = {}
35
+ self._server_tools: dict[str, list[BaseTool]] = {}
36
+
37
+ @telemetry("server_manager_initialize")
38
+ async def initialize(self) -> None:
39
+ """Initialize the server manager and prepare server management tools."""
40
+ # Make sure we have server configurations
41
+ if not self.client.get_server_names():
42
+ logger.warning("No MCP servers defined in client configuration")
43
+
44
+ async def _prefetch_server_tools(self) -> None:
45
+ """Pre-fetch tools for all servers to populate the tool search index."""
46
+ servers = self.client.get_server_names()
47
+ for server_name in servers:
48
+ try:
49
+ # Only create session if needed, don't set active
50
+ session = None
51
+ try:
52
+ session = self.client.get_session(server_name)
53
+ logger.debug(f"Using existing session for server '{server_name}' to prefetch tools.")
54
+ except ValueError:
55
+ try:
56
+ session = await self.client.create_session(server_name)
57
+ logger.debug(f"Temporarily created session for '{server_name}' to prefetch tools")
58
+ except Exception:
59
+ logger.warning(f"Could not create session for '{server_name}' during prefetch")
60
+ continue
61
+
62
+ # Fetch tools if session is available
63
+ if session:
64
+ connector = session.connector
65
+ tools = await self.adapter._create_tools_from_connectors([connector])
66
+
67
+ # Check if this server's tools have changed
68
+ if server_name not in self._server_tools or self._server_tools[server_name] != tools:
69
+ self._server_tools[server_name] = tools # Cache tools
70
+ self.initialized_servers[server_name] = True # Mark as initialized
71
+ logger.debug(f"Prefetched {len(tools)} tools for server '{server_name}'.")
72
+ else:
73
+ logger.debug(f"Tools for server '{server_name}' unchanged, using cached version.")
74
+ except Exception as e:
75
+ logger.error(f"Error prefetching tools for server '{server_name}': {e}")
76
+
77
+ def get_active_server_tools(self) -> list[BaseTool]:
78
+ """Get tools from the currently active server.
79
+
80
+ Returns:
81
+ List of tools from the active server, or empty list if no server is active
82
+ """
83
+ if self.active_server and self.active_server in self._server_tools:
84
+ return self._server_tools[self.active_server]
85
+ return []
86
+
87
+ def get_management_tools(self) -> list[BaseTool]:
88
+ """Get the server management tools.
89
+
90
+ Returns:
91
+ List of server management tools
92
+ """
93
+ return [
94
+ ListServersTool(self),
95
+ ConnectServerTool(self),
96
+ GetActiveServerTool(self),
97
+ DisconnectServerTool(self),
98
+ SearchToolsTool(self),
99
+ ]
100
+
101
+ def has_tool_changes(self, current_tool_names: set[str]) -> bool:
102
+ """Check if the available tools have changed.
103
+
104
+ Args:
105
+ current_tool_names: Set of currently known tool names
106
+
107
+ Returns:
108
+ True if tools have changed, False otherwise
109
+ """
110
+ new_tool_names = {tool.name for tool in self.tools}
111
+ return new_tool_names != current_tool_names
112
+
113
+ @property
114
+ def tools(self) -> list[BaseTool]:
115
+ """Get all server management tools and tools from the active server.
116
+
117
+ Returns:
118
+ list of LangChain tools for server management plus tools from active server
119
+ """
120
+ management_tools = self.get_management_tools()
121
+
122
+ # Add tools from the active server if available
123
+ if self.active_server and self.active_server in self._server_tools:
124
+ server_tools = self._server_tools[self.active_server]
125
+ logger.debug(f"Including {len(server_tools)} tools from active server '{self.active_server}'")
126
+ logger.debug(f"Server tools: {[tool.name for tool in server_tools]}")
127
+ return management_tools + server_tools
128
+ else:
129
+ logger.debug("No active server - returning only management tools")
130
+
131
+ return management_tools
@@ -0,0 +1,15 @@
1
+ from .base_tool import MCPServerTool
2
+ from .connect_server import ConnectServerTool
3
+ from .disconnect_server import DisconnectServerTool
4
+ from .get_active_server import GetActiveServerTool
5
+ from .list_servers_tool import ListServersTool
6
+ from .search_tools import SearchToolsTool
7
+
8
+ __all__ = [
9
+ "MCPServerTool",
10
+ "ConnectServerTool",
11
+ "DisconnectServerTool",
12
+ "GetActiveServerTool",
13
+ "ListServersTool",
14
+ "SearchToolsTool",
15
+ ]
@@ -0,0 +1,19 @@
1
+ from typing import ClassVar
2
+
3
+ from langchain_core.tools import BaseTool
4
+
5
+
6
+ class MCPServerTool(BaseTool):
7
+ """Base tool for MCP server operations."""
8
+
9
+ name: ClassVar[str] = "mcp_server_tool"
10
+ description: ClassVar[str] = "Base tool for MCP server operations."
11
+
12
+ def __init__(self, server_manager):
13
+ """Initialize with server manager."""
14
+ super().__init__()
15
+ self._server_manager = server_manager
16
+
17
+ @property
18
+ def server_manager(self):
19
+ return self._server_manager
@@ -0,0 +1,69 @@
1
+ from typing import ClassVar
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from mcp_use.agents.managers.tools.base_tool import MCPServerTool
6
+ from mcp_use.errors.error_formatting import format_error
7
+ from mcp_use.logging import logger
8
+
9
+
10
+ class ServerActionInput(BaseModel):
11
+ """Base input for server-related actions"""
12
+
13
+ server_name: str = Field(description="The name of the MCP server")
14
+
15
+
16
+ class ConnectServerTool(MCPServerTool):
17
+ """Tool for connecting to a specific MCP server."""
18
+
19
+ name: ClassVar[str] = "connect_to_mcp_server"
20
+ description: ClassVar[str] = (
21
+ "Connect to a specific MCP (Model Context Protocol) server to use its "
22
+ "tools. Use this tool to connect to a specific server and use its tools."
23
+ )
24
+ args_schema: ClassVar[type[BaseModel]] = ServerActionInput
25
+
26
+ async def _arun(self, server_name: str) -> str:
27
+ """Connect to a specific MCP server."""
28
+ # Check if server exists
29
+ servers = self.server_manager.client.get_server_names()
30
+ if server_name not in servers:
31
+ available = ", ".join(servers) if servers else "none"
32
+ return f"Server '{server_name}' not found. Available servers: {available}"
33
+
34
+ # If we're already connected to this server, just return
35
+ if self.server_manager.active_server == server_name:
36
+ return f"Already connected to MCP server '{server_name}'"
37
+
38
+ try:
39
+ # Create or get session for this server
40
+ try:
41
+ session = self.server_manager.client.get_session(server_name)
42
+ logger.debug(f"Using existing session for server '{server_name}'")
43
+ except ValueError:
44
+ logger.debug(f"Creating new session for server '{server_name}'")
45
+ session = await self.server_manager.client.create_session(server_name)
46
+
47
+ # Set as active server
48
+ self.server_manager.active_server = server_name
49
+
50
+ # Initialize server tools if not already initialized
51
+ if server_name not in self.server_manager._server_tools:
52
+ connector = session.connector
53
+ self.server_manager._server_tools[
54
+ server_name
55
+ ] = await self.server_manager.adapter._create_tools_from_connectors([connector])
56
+ self.server_manager.initialized_servers[server_name] = True
57
+
58
+ server_tools = self.server_manager._server_tools.get(server_name, [])
59
+ num_tools = len(server_tools)
60
+
61
+ return f"Connected to MCP server '{server_name}'. {num_tools} tools are now available."
62
+
63
+ except Exception as e:
64
+ logger.error(f"Error connecting to server '{server_name}': {e}")
65
+ return format_error(e, server_name=server_name)
66
+
67
+ def _run(self, server_name: str) -> str:
68
+ """Synchronous version that raises a NotImplementedError - use _arun instead."""
69
+ raise NotImplementedError("ConnectServerTool requires async execution. Use _arun instead.")
@@ -0,0 +1,43 @@
1
+ from typing import ClassVar
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from mcp_use.agents.managers.tools.base_tool import MCPServerTool
6
+ from mcp_use.errors.error_formatting import format_error
7
+ from mcp_use.logging import logger
8
+
9
+
10
+ class DisconnectServerInput(BaseModel):
11
+ """Empty input for disconnecting from the current server"""
12
+
13
+ pass
14
+
15
+
16
+ class DisconnectServerTool(MCPServerTool):
17
+ """Tool for disconnecting from the currently active MCP server."""
18
+
19
+ name: ClassVar[str] = "disconnect_from_mcp_server"
20
+ description: ClassVar[str] = "Disconnect from the currently active MCP (Model Context Protocol) server"
21
+ args_schema: ClassVar[type[BaseModel]] = DisconnectServerInput
22
+
23
+ def _run(self, **kwargs) -> str:
24
+ """Disconnect from the currently active MCP server."""
25
+ if not self.server_manager.active_server:
26
+ return "No MCP server is currently active, so there's nothing to disconnect from."
27
+
28
+ server_name = self.server_manager.active_server
29
+ try:
30
+ # Clear the active server
31
+ self.server_manager.active_server = None
32
+
33
+ # Note: We're not actually closing the session here, just 'deactivating'
34
+ # This way we keep the session cache without requiring reconnection if needed again
35
+
36
+ return f"Successfully disconnected from MCP server '{server_name}'."
37
+ except Exception as e:
38
+ logger.error(f"Error disconnecting from server '{server_name}': {e}")
39
+ return format_error(e, server_name=server_name)
40
+
41
+ async def _arun(self, **kwargs) -> str:
42
+ """Async implementation of _run."""
43
+ return self._run(**kwargs)
@@ -0,0 +1,29 @@
1
+ from typing import ClassVar
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from mcp_use.agents.managers.tools.base_tool import MCPServerTool
6
+
7
+
8
+ class CurrentServerInput(BaseModel):
9
+ """Empty input for checking current server"""
10
+
11
+ pass
12
+
13
+
14
+ class GetActiveServerTool(MCPServerTool):
15
+ """Tool for getting the currently active MCP server."""
16
+
17
+ name: ClassVar[str] = "get_active_mcp_server"
18
+ description: ClassVar[str] = "Get the currently active MCP (Model Context Protocol) server"
19
+ args_schema: ClassVar[type[BaseModel]] = CurrentServerInput
20
+
21
+ def _run(self, **kwargs) -> str:
22
+ """Get the currently active MCP server."""
23
+ if not self.server_manager.active_server:
24
+ return "No MCP server is currently active. Use connect_to_mcp_server to connect to a server."
25
+ return f"Currently active MCP server: {self.server_manager.active_server}"
26
+
27
+ async def _arun(self, **kwargs) -> str:
28
+ """Async implementation of _run."""
29
+ return self._run(**kwargs)
@@ -0,0 +1,53 @@
1
+ from typing import ClassVar
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from mcp_use.agents.managers.tools.base_tool import MCPServerTool
6
+ from mcp_use.errors.error_formatting import format_error
7
+ from mcp_use.logging import logger
8
+
9
+
10
+ class listServersInput(BaseModel):
11
+ """Empty input for listing available servers"""
12
+
13
+ pass
14
+
15
+
16
+ class ListServersTool(MCPServerTool):
17
+ """Tool for listing available MCP servers."""
18
+
19
+ name: ClassVar[str] = "list_mcp_servers"
20
+ description: ClassVar[str] = (
21
+ "Lists all available MCP (Model Context Protocol) servers that can be "
22
+ "connected to, along with the tools available on each server. "
23
+ "Use this tool to discover servers and see what functionalities they offer."
24
+ )
25
+ args_schema: ClassVar[type[BaseModel]] = listServersInput
26
+
27
+ def _run(self, **kwargs) -> str:
28
+ """List all available MCP servers along with their available tools."""
29
+ servers = self.server_manager.client.get_server_names()
30
+ if not servers:
31
+ return "No MCP servers are currently defined."
32
+
33
+ result = "Available MCP servers:\n"
34
+ for i, server_name in enumerate(servers):
35
+ active_marker = " (ACTIVE)" if server_name == self.server_manager.active_server else ""
36
+ result += f"{i + 1}. {server_name}{active_marker}\n"
37
+
38
+ tools: list = []
39
+ try:
40
+ # Check cache first
41
+ if server_name in self.server_manager._server_tools:
42
+ tools = self.server_manager._server_tools[server_name]
43
+ tool_count = len(tools)
44
+ result += f" {tool_count} tools available for this server\n"
45
+ except Exception as e:
46
+ logger.error(f"Unexpected error listing tools for server '{server_name}': {e}")
47
+ return format_error(e, server=server_name, operation="list_tools")
48
+
49
+ return result
50
+
51
+ async def _arun(self, **kwargs) -> str:
52
+ """Async implementation of _run - calls the synchronous version."""
53
+ return self._run(**kwargs)