mcp-use 1.2.6__py3-none-any.whl → 1.2.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mcp-use might be problematic. Click here for more details.
- mcp_use/adapters/langchain_adapter.py +3 -0
- mcp_use/agents/__init__.py +0 -2
- mcp_use/agents/mcpagent.py +4 -7
- mcp_use/managers/__init__.py +21 -0
- mcp_use/managers/server_manager.py +101 -0
- mcp_use/managers/tools/__init__.py +17 -0
- mcp_use/managers/tools/base_tool.py +19 -0
- mcp_use/managers/tools/connect_server.py +69 -0
- mcp_use/managers/tools/disconnect_server.py +45 -0
- mcp_use/managers/tools/get_active_server.py +32 -0
- mcp_use/managers/tools/list_servers_tool.py +52 -0
- mcp_use/managers/tools/search_tools.py +303 -0
- mcp_use/managers/tools/use_tool.py +167 -0
- {mcp_use-1.2.6.dist-info → mcp_use-1.2.8.dist-info}/METADATA +12 -10
- {mcp_use-1.2.6.dist-info → mcp_use-1.2.8.dist-info}/RECORD +17 -8
- mcp_use/agents/server_manager.py +0 -282
- {mcp_use-1.2.6.dist-info → mcp_use-1.2.8.dist-info}/WHEEL +0 -0
- {mcp_use-1.2.6.dist-info → mcp_use-1.2.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -118,6 +118,9 @@ class LangChainAdapter(BaseAdapter):
|
|
|
118
118
|
tool_connector: BaseConnector = connector # Renamed variable to avoid name conflict
|
|
119
119
|
handle_tool_error: bool = True
|
|
120
120
|
|
|
121
|
+
def __repr__(self) -> str:
|
|
122
|
+
return f"MCP tool: {self.name}: {self.description}"
|
|
123
|
+
|
|
121
124
|
def _run(self, **kwargs: Any) -> NoReturn:
|
|
122
125
|
"""Synchronous run method that always raises an error.
|
|
123
126
|
|
mcp_use/agents/__init__.py
CHANGED
mcp_use/agents/mcpagent.py
CHANGED
|
@@ -23,9 +23,9 @@ from mcp_use.session import MCPSession
|
|
|
23
23
|
|
|
24
24
|
from ..adapters.langchain_adapter import LangChainAdapter
|
|
25
25
|
from ..logging import logger
|
|
26
|
+
from ..managers.server_manager import ServerManager
|
|
26
27
|
from .prompts.system_prompt_builder import create_system_message
|
|
27
28
|
from .prompts.templates import DEFAULT_SYSTEM_PROMPT_TEMPLATE, SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE
|
|
28
|
-
from .server_manager import ServerManager
|
|
29
29
|
|
|
30
30
|
set_debug(logger.level == logging.DEBUG)
|
|
31
31
|
|
|
@@ -42,7 +42,6 @@ class MCPAgent:
|
|
|
42
42
|
llm: BaseLanguageModel,
|
|
43
43
|
client: MCPClient | None = None,
|
|
44
44
|
connectors: list[BaseConnector] | None = None,
|
|
45
|
-
server_name: str | None = None,
|
|
46
45
|
max_steps: int = 5,
|
|
47
46
|
auto_initialize: bool = False,
|
|
48
47
|
memory_enabled: bool = True,
|
|
@@ -59,7 +58,6 @@ class MCPAgent:
|
|
|
59
58
|
llm: The LangChain LLM to use.
|
|
60
59
|
client: The MCPClient to use. If provided, connector is ignored.
|
|
61
60
|
connectors: A list of MCP connectors to use if client is not provided.
|
|
62
|
-
server_name: The name of the server to use if client is provided.
|
|
63
61
|
max_steps: The maximum number of steps to take.
|
|
64
62
|
auto_initialize: Whether to automatically initialize the agent when run is called.
|
|
65
63
|
memory_enabled: Whether to maintain conversation history for context.
|
|
@@ -72,7 +70,6 @@ class MCPAgent:
|
|
|
72
70
|
self.llm = llm
|
|
73
71
|
self.client = client
|
|
74
72
|
self.connectors = connectors or []
|
|
75
|
-
self.server_name = server_name
|
|
76
73
|
self.max_steps = max_steps
|
|
77
74
|
self.auto_initialize = auto_initialize
|
|
78
75
|
self.memory_enabled = memory_enabled
|
|
@@ -114,7 +111,7 @@ class MCPAgent:
|
|
|
114
111
|
if self.use_server_manager and self.server_manager:
|
|
115
112
|
await self.server_manager.initialize()
|
|
116
113
|
# Get server management tools
|
|
117
|
-
management_tools =
|
|
114
|
+
management_tools = self.server_manager.tools
|
|
118
115
|
self._tools = management_tools
|
|
119
116
|
logger.info(
|
|
120
117
|
f"🔧 Server manager mode active with {len(management_tools)} management tools"
|
|
@@ -391,7 +388,7 @@ class MCPAgent:
|
|
|
391
388
|
for step_num in range(steps):
|
|
392
389
|
# --- Check for tool updates if using server manager ---
|
|
393
390
|
if self.use_server_manager and self.server_manager:
|
|
394
|
-
current_tools =
|
|
391
|
+
current_tools = self.server_manager.tools
|
|
395
392
|
current_tool_names = {tool.name for tool in current_tools}
|
|
396
393
|
existing_tool_names = {tool.name for tool in self._tools}
|
|
397
394
|
|
|
@@ -412,7 +409,7 @@ class MCPAgent:
|
|
|
412
409
|
[tool.name for tool in self._tools], excluded_colors=["green", "red"]
|
|
413
410
|
)
|
|
414
411
|
|
|
415
|
-
logger.info(f"
|
|
412
|
+
logger.info(f"👣 Step {step_num + 1}/{steps}")
|
|
416
413
|
|
|
417
414
|
# --- Plan and execute the next step ---
|
|
418
415
|
try:
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .server_manager import ServerManager
|
|
2
|
+
from .tools import (
|
|
3
|
+
ConnectServerTool,
|
|
4
|
+
DisconnectServerTool,
|
|
5
|
+
GetActiveServerTool,
|
|
6
|
+
ListServersTool,
|
|
7
|
+
MCPServerTool,
|
|
8
|
+
SearchToolsTool,
|
|
9
|
+
UseToolFromServerTool,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"ServerManager",
|
|
14
|
+
"ListServersTool",
|
|
15
|
+
"ConnectServerTool",
|
|
16
|
+
"GetActiveServerTool",
|
|
17
|
+
"DisconnectServerTool",
|
|
18
|
+
"SearchToolsTool",
|
|
19
|
+
"MCPServerTool",
|
|
20
|
+
"UseToolFromServerTool",
|
|
21
|
+
]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from langchain_core.tools import BaseTool
|
|
2
|
+
|
|
3
|
+
from mcp_use.client import MCPClient
|
|
4
|
+
from mcp_use.logging import logger
|
|
5
|
+
|
|
6
|
+
from ..adapters.base import BaseAdapter
|
|
7
|
+
from .tools import (
|
|
8
|
+
ConnectServerTool,
|
|
9
|
+
DisconnectServerTool,
|
|
10
|
+
GetActiveServerTool,
|
|
11
|
+
ListServersTool,
|
|
12
|
+
SearchToolsTool,
|
|
13
|
+
UseToolFromServerTool,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ServerManager:
|
|
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
|
+
async def initialize(self) -> None:
|
|
38
|
+
"""Initialize the server manager and prepare server management tools."""
|
|
39
|
+
# Make sure we have server configurations
|
|
40
|
+
if not self.client.get_server_names():
|
|
41
|
+
logger.warning("No MCP servers defined in client configuration")
|
|
42
|
+
|
|
43
|
+
async def _prefetch_server_tools(self) -> None:
|
|
44
|
+
"""Pre-fetch tools for all servers to populate the tool search index."""
|
|
45
|
+
servers = self.client.get_server_names()
|
|
46
|
+
for server_name in servers:
|
|
47
|
+
try:
|
|
48
|
+
# Only create session if needed, don't set active
|
|
49
|
+
session = None
|
|
50
|
+
try:
|
|
51
|
+
session = self.client.get_session(server_name)
|
|
52
|
+
logger.debug(
|
|
53
|
+
f"Using existing session for server '{server_name}' to prefetch tools."
|
|
54
|
+
)
|
|
55
|
+
except ValueError:
|
|
56
|
+
try:
|
|
57
|
+
session = await self.client.create_session(server_name)
|
|
58
|
+
logger.debug(
|
|
59
|
+
f"Temporarily created session for '{server_name}' to prefetch tools"
|
|
60
|
+
)
|
|
61
|
+
except Exception:
|
|
62
|
+
logger.warning(
|
|
63
|
+
f"Could not create session for '{server_name}' during prefetch"
|
|
64
|
+
)
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
# Fetch tools if session is available
|
|
68
|
+
if session:
|
|
69
|
+
connector = session.connector
|
|
70
|
+
tools = await self.adapter._create_tools_from_connectors([connector])
|
|
71
|
+
|
|
72
|
+
# 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
|
+
):
|
|
77
|
+
self._server_tools[server_name] = tools # Cache tools
|
|
78
|
+
self.initialized_servers[server_name] = True # Mark as initialized
|
|
79
|
+
logger.debug(f"Prefetched {len(tools)} tools for server '{server_name}'.")
|
|
80
|
+
else:
|
|
81
|
+
logger.debug(
|
|
82
|
+
f"Tools for server '{server_name}' unchanged, using cached version."
|
|
83
|
+
)
|
|
84
|
+
except Exception as e:
|
|
85
|
+
logger.error(f"Error prefetching tools for server '{server_name}': {e}")
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def tools(self) -> list[BaseTool]:
|
|
89
|
+
"""Get all server management tools.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
list of LangChain tools for server management
|
|
93
|
+
"""
|
|
94
|
+
return [
|
|
95
|
+
ListServersTool(self),
|
|
96
|
+
ConnectServerTool(self),
|
|
97
|
+
GetActiveServerTool(self),
|
|
98
|
+
DisconnectServerTool(self),
|
|
99
|
+
SearchToolsTool(self),
|
|
100
|
+
UseToolFromServerTool(self),
|
|
101
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
from .use_tool import UseToolFromServerTool
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"MCPServerTool",
|
|
11
|
+
"ListServersTool",
|
|
12
|
+
"ConnectServerTool",
|
|
13
|
+
"GetActiveServerTool",
|
|
14
|
+
"DisconnectServerTool",
|
|
15
|
+
"SearchToolsTool",
|
|
16
|
+
"UseToolFromServerTool",
|
|
17
|
+
]
|
|
@@ -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.logging import logger
|
|
6
|
+
|
|
7
|
+
from .base_tool import MCPServerTool
|
|
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 f"Failed to connect to server '{server_name}': {str(e)}"
|
|
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,45 @@
|
|
|
1
|
+
from typing import ClassVar
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from mcp_use.logging import logger
|
|
6
|
+
|
|
7
|
+
from .base_tool import MCPServerTool
|
|
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] = (
|
|
21
|
+
"Disconnect from the currently active MCP (Model Context Protocol) server"
|
|
22
|
+
)
|
|
23
|
+
args_schema: ClassVar[type[BaseModel]] = DisconnectServerInput
|
|
24
|
+
|
|
25
|
+
def _run(self, **kwargs) -> str:
|
|
26
|
+
"""Disconnect from the currently active MCP server."""
|
|
27
|
+
if not self.server_manager.active_server:
|
|
28
|
+
return "No MCP server is currently active, so there's nothing to disconnect from."
|
|
29
|
+
|
|
30
|
+
server_name = self.server_manager.active_server
|
|
31
|
+
try:
|
|
32
|
+
# Clear the active server
|
|
33
|
+
self.server_manager.active_server = None
|
|
34
|
+
|
|
35
|
+
# Note: We're not actually closing the session here, just 'deactivating'
|
|
36
|
+
# This way we keep the session cache without requiring reconnection if needed again
|
|
37
|
+
|
|
38
|
+
return f"Successfully disconnected from MCP server '{server_name}'."
|
|
39
|
+
except Exception as e:
|
|
40
|
+
logger.error(f"Error disconnecting from server '{server_name}': {e}")
|
|
41
|
+
return f"Failed to disconnect from server '{server_name}': {str(e)}"
|
|
42
|
+
|
|
43
|
+
async def _arun(self, **kwargs) -> str:
|
|
44
|
+
"""Async implementation of _run."""
|
|
45
|
+
return self._run(**kwargs)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import ClassVar
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from .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 (
|
|
25
|
+
"No MCP server is currently active. "
|
|
26
|
+
"Use connect_to_mcp_server to connect to a server."
|
|
27
|
+
)
|
|
28
|
+
return f"Currently active MCP server: {self.server_manager.active_server}"
|
|
29
|
+
|
|
30
|
+
async def _arun(self, **kwargs) -> str:
|
|
31
|
+
"""Async implementation of _run."""
|
|
32
|
+
return self._run(**kwargs)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import ClassVar
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from mcp_use.logging import logger
|
|
6
|
+
|
|
7
|
+
from .base_tool import MCPServerTool
|
|
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
|
+
|
|
48
|
+
return result
|
|
49
|
+
|
|
50
|
+
async def _arun(self, **kwargs) -> str:
|
|
51
|
+
"""Async implementation of _run - calls the synchronous version."""
|
|
52
|
+
return self._run(**kwargs)
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import time
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from fastembed import TextEmbedding
|
|
7
|
+
from langchain_core.tools import BaseTool
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from ...logging import logger
|
|
11
|
+
from .base_tool import MCPServerTool
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ToolSearchInput(BaseModel):
|
|
15
|
+
"""Input for searching for tools across MCP servers"""
|
|
16
|
+
|
|
17
|
+
query: str = Field(description="The search query to find relevant tools")
|
|
18
|
+
top_k: int = Field(
|
|
19
|
+
default=100,
|
|
20
|
+
description="The maximum number of tools to return (defaults to 100)",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SearchToolsTool(MCPServerTool):
|
|
25
|
+
"""Tool for searching for tools across all MCP servers using semantic search."""
|
|
26
|
+
|
|
27
|
+
name: ClassVar[str] = "search_mcp_tools"
|
|
28
|
+
description: ClassVar[str] = (
|
|
29
|
+
"Search for relevant tools across all MCP servers using semantic search. "
|
|
30
|
+
"Provide a description of the tool you think you might need to be able to perform "
|
|
31
|
+
"the task you are assigned. Do not be too specific, the search will give you many "
|
|
32
|
+
"options. It is important you search for the tool, not for the goal. "
|
|
33
|
+
"If your first search doesn't yield relevant results, try using different keywords "
|
|
34
|
+
"or more general terms."
|
|
35
|
+
)
|
|
36
|
+
args_schema: ClassVar[type[BaseModel]] = ToolSearchInput
|
|
37
|
+
|
|
38
|
+
def __init__(self, server_manager):
|
|
39
|
+
"""Initialize with server manager and create a search tool."""
|
|
40
|
+
super().__init__(server_manager)
|
|
41
|
+
self._search_tool = ToolSearchEngine(server_manager=server_manager)
|
|
42
|
+
|
|
43
|
+
async def _arun(self, query: str, top_k: int = 100) -> str:
|
|
44
|
+
"""Search for tools across all MCP servers using semantic search."""
|
|
45
|
+
# Make sure the index is ready, and if not, allow the search_tools method to handle it
|
|
46
|
+
# No need to manually check or build the index here as the search_tools method will do that
|
|
47
|
+
|
|
48
|
+
# Perform search using our search tool instance
|
|
49
|
+
results = await self._search_tool.search_tools(
|
|
50
|
+
query, top_k=top_k, active_server=self.server_manager.active_server
|
|
51
|
+
)
|
|
52
|
+
return self.format_search_results(results)
|
|
53
|
+
|
|
54
|
+
def _run(self, query: str, top_k: int = 100) -> str:
|
|
55
|
+
"""Synchronous version that raises a NotImplementedError - use _arun instead."""
|
|
56
|
+
raise NotImplementedError("SearchToolsTool requires async execution. Use _arun instead.")
|
|
57
|
+
|
|
58
|
+
def format_search_results(self, results: list[tuple[BaseTool, str, float]]) -> str:
|
|
59
|
+
"""Format search results in a consistent format."""
|
|
60
|
+
|
|
61
|
+
# Only show top_k results
|
|
62
|
+
results = results
|
|
63
|
+
|
|
64
|
+
formatted_output = "Search results\n\n"
|
|
65
|
+
|
|
66
|
+
for i, (tool, server_name, score) in enumerate(results):
|
|
67
|
+
# Format score as percentage
|
|
68
|
+
if i < 5:
|
|
69
|
+
score_pct = f"{score * 100:.1f}%"
|
|
70
|
+
logger.info(f"{i}: {tool.name} ({score_pct} match)")
|
|
71
|
+
formatted_output += f"[{i + 1}] Tool: {tool.name} ({score_pct} match)\n"
|
|
72
|
+
formatted_output += f" Server: {server_name}\n"
|
|
73
|
+
formatted_output += f" Description: {tool.description}\n\n"
|
|
74
|
+
|
|
75
|
+
# Add footer with information about how to use the results
|
|
76
|
+
formatted_output += (
|
|
77
|
+
"\nTo use a tool, connect to the appropriate server first, then invoke the tool."
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return formatted_output
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ToolSearchEngine:
|
|
84
|
+
"""
|
|
85
|
+
Provides semantic search capabilities for MCP tools.
|
|
86
|
+
Uses vector similarity for semantic search with optional result caching.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(self, server_manager=None, use_caching: bool = True):
|
|
90
|
+
"""
|
|
91
|
+
Initialize the tool search engine.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
server_manager: The ServerManager instance to get tools from
|
|
95
|
+
use_caching: Whether to cache query results
|
|
96
|
+
"""
|
|
97
|
+
self.server_manager = server_manager
|
|
98
|
+
self.use_caching = use_caching
|
|
99
|
+
self.is_indexed = False
|
|
100
|
+
|
|
101
|
+
# Initialize model components (loaded on demand)
|
|
102
|
+
self.model = None
|
|
103
|
+
self.embedding_function = None
|
|
104
|
+
|
|
105
|
+
# Data storage
|
|
106
|
+
self.tool_embeddings = {} # Maps tool name to embedding vector
|
|
107
|
+
self.tools_by_name = {} # Maps tool name to tool instance
|
|
108
|
+
self.server_by_tool = {} # Maps tool name to server name
|
|
109
|
+
self.tool_texts = {} # Maps tool name to searchable text
|
|
110
|
+
self.query_cache = {} # Caches search results by query
|
|
111
|
+
|
|
112
|
+
def _load_model(self) -> bool:
|
|
113
|
+
"""Load the embedding model for semantic search if not already loaded."""
|
|
114
|
+
if self.model is not None:
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
self.model = TextEmbedding(model_name="BAAI/bge-small-en-v1.5")
|
|
119
|
+
self.embedding_function = lambda texts: list(self.model.embed(texts))
|
|
120
|
+
return True
|
|
121
|
+
except Exception:
|
|
122
|
+
return False
|
|
123
|
+
|
|
124
|
+
async def start_indexing(self) -> None:
|
|
125
|
+
"""Index the tools from the server manager."""
|
|
126
|
+
if not self.server_manager:
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
# Get tools from server manager
|
|
130
|
+
server_tools = self.server_manager._server_tools
|
|
131
|
+
|
|
132
|
+
if not server_tools:
|
|
133
|
+
# Try to prefetch tools first
|
|
134
|
+
if hasattr(self.server_manager, "_prefetch_server_tools"):
|
|
135
|
+
await self.server_manager._prefetch_server_tools()
|
|
136
|
+
server_tools = self.server_manager._server_tools
|
|
137
|
+
|
|
138
|
+
if server_tools:
|
|
139
|
+
await self.index_tools(server_tools)
|
|
140
|
+
|
|
141
|
+
async def index_tools(self, server_tools: dict[str, list[BaseTool]]) -> None:
|
|
142
|
+
"""
|
|
143
|
+
Index all tools from all servers for search.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
server_tools: dictionary mapping server names to their tools
|
|
147
|
+
"""
|
|
148
|
+
# Clear previous indexes
|
|
149
|
+
self.tool_embeddings = {}
|
|
150
|
+
self.tools_by_name = {}
|
|
151
|
+
self.server_by_tool = {}
|
|
152
|
+
self.tool_texts = {}
|
|
153
|
+
self.query_cache = {}
|
|
154
|
+
self.is_indexed = False
|
|
155
|
+
|
|
156
|
+
# Collect all tools and their descriptions
|
|
157
|
+
for server_name, tools in server_tools.items():
|
|
158
|
+
for tool in tools:
|
|
159
|
+
# Create text representation for search
|
|
160
|
+
tool_text = f"{tool.name}: {tool.description}"
|
|
161
|
+
|
|
162
|
+
# Store tool information
|
|
163
|
+
self.tools_by_name[tool.name] = tool
|
|
164
|
+
self.server_by_tool[tool.name] = server_name
|
|
165
|
+
self.tool_texts[tool.name] = tool_text.lower() # For case-insensitive search
|
|
166
|
+
|
|
167
|
+
if not self.tool_texts:
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
# Generate embeddings
|
|
171
|
+
if self._load_model():
|
|
172
|
+
tool_names = list(self.tool_texts.keys())
|
|
173
|
+
tool_texts = [self.tool_texts[name] for name in tool_names]
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
embeddings = self.embedding_function(tool_texts)
|
|
177
|
+
for name, embedding in zip(tool_names, embeddings, strict=True):
|
|
178
|
+
self.tool_embeddings[name] = embedding
|
|
179
|
+
|
|
180
|
+
# Mark as indexed if we successfully embedded tools
|
|
181
|
+
self.is_indexed = len(self.tool_embeddings) > 0
|
|
182
|
+
except Exception:
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
def search(self, query: str, top_k: int = 5) -> list[tuple[BaseTool, str, float]]:
|
|
186
|
+
"""
|
|
187
|
+
Search for tools that match the query using semantic search.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
query: The search query
|
|
191
|
+
top_k: Number of top results to return
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
list of tuples containing (tool, server_name, score)
|
|
195
|
+
"""
|
|
196
|
+
if not self.is_indexed:
|
|
197
|
+
return []
|
|
198
|
+
|
|
199
|
+
# Check cache first
|
|
200
|
+
cache_key = f"semantic:{query}:{top_k}"
|
|
201
|
+
if self.use_caching and cache_key in self.query_cache:
|
|
202
|
+
return self.query_cache[cache_key]
|
|
203
|
+
|
|
204
|
+
# Ensure model and embeddings exist
|
|
205
|
+
if not self._load_model() or not self.tool_embeddings:
|
|
206
|
+
return []
|
|
207
|
+
|
|
208
|
+
# Generate embedding for the query
|
|
209
|
+
try:
|
|
210
|
+
query_embedding = self.embedding_function([query])[0]
|
|
211
|
+
except Exception:
|
|
212
|
+
return []
|
|
213
|
+
|
|
214
|
+
# Calculate similarity scores
|
|
215
|
+
scores = {}
|
|
216
|
+
for tool_name, embedding in self.tool_embeddings.items():
|
|
217
|
+
# Calculate cosine similarity
|
|
218
|
+
similarity = np.dot(query_embedding, embedding) / (
|
|
219
|
+
np.linalg.norm(query_embedding) * np.linalg.norm(embedding)
|
|
220
|
+
)
|
|
221
|
+
scores[tool_name] = float(similarity)
|
|
222
|
+
|
|
223
|
+
# Sort by score and get top_k results
|
|
224
|
+
sorted_results = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:top_k]
|
|
225
|
+
|
|
226
|
+
# Format results
|
|
227
|
+
results = []
|
|
228
|
+
for tool_name, score in sorted_results:
|
|
229
|
+
tool = self.tools_by_name.get(tool_name)
|
|
230
|
+
server_name = self.server_by_tool.get(tool_name)
|
|
231
|
+
if tool and server_name:
|
|
232
|
+
results.append((tool, server_name, score))
|
|
233
|
+
|
|
234
|
+
# Cache results
|
|
235
|
+
if self.use_caching:
|
|
236
|
+
self.query_cache[cache_key] = results
|
|
237
|
+
|
|
238
|
+
return results
|
|
239
|
+
|
|
240
|
+
async def search_tools(self, query: str, top_k: int = 100, active_server: str = None) -> str:
|
|
241
|
+
"""
|
|
242
|
+
Search for tools across all MCP servers using semantic search.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
query: The search query to find relevant tools
|
|
246
|
+
top_k: Number of top results to return
|
|
247
|
+
active_server: Name of the currently active server (for highlighting)
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
String with formatted search results
|
|
251
|
+
"""
|
|
252
|
+
# Ensure the index is built or build it
|
|
253
|
+
if not self.is_indexed:
|
|
254
|
+
# Try to build the index
|
|
255
|
+
if self.server_manager and self.server_manager._server_tools:
|
|
256
|
+
await self.index_tools(self.server_manager._server_tools)
|
|
257
|
+
else:
|
|
258
|
+
# If we don't have server_manager or tools, try to index directly
|
|
259
|
+
await self.start_indexing()
|
|
260
|
+
|
|
261
|
+
# Wait for indexing to complete (maximum 10 seconds)
|
|
262
|
+
start_time = time.time()
|
|
263
|
+
timeout = 10 # seconds
|
|
264
|
+
while not self.is_indexed and (time.time() - start_time) < timeout:
|
|
265
|
+
await asyncio.sleep(0.5)
|
|
266
|
+
|
|
267
|
+
# If still not indexed, return a friendly message
|
|
268
|
+
if not self.is_indexed:
|
|
269
|
+
return (
|
|
270
|
+
"I'm still preparing the tool index. Please try your search again in a moment. "
|
|
271
|
+
"This usually takes just a few seconds to complete."
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# If the server manager has an active server but it wasn't provided, use it
|
|
275
|
+
if (
|
|
276
|
+
active_server is None
|
|
277
|
+
and self.server_manager
|
|
278
|
+
and hasattr(self.server_manager, "active_server")
|
|
279
|
+
):
|
|
280
|
+
active_server = self.server_manager.active_server
|
|
281
|
+
|
|
282
|
+
results = self.search(query, top_k=top_k)
|
|
283
|
+
if not results:
|
|
284
|
+
return (
|
|
285
|
+
"No relevant tools found. The search provided no results. "
|
|
286
|
+
"You can try searching again with different keywords. "
|
|
287
|
+
"Try using more general terms or focusing on the capability you need."
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# If there's an active server, mark it in the results
|
|
291
|
+
if active_server:
|
|
292
|
+
# Create a new results list with marked active server
|
|
293
|
+
marked_results = []
|
|
294
|
+
for tool, server_name, score in results:
|
|
295
|
+
# If this is the active server, add "(ACTIVE)" marker
|
|
296
|
+
display_server = (
|
|
297
|
+
f"{server_name} (ACTIVE)" if server_name == active_server else server_name
|
|
298
|
+
)
|
|
299
|
+
marked_results.append((tool, display_server, score))
|
|
300
|
+
results = marked_results
|
|
301
|
+
|
|
302
|
+
# Format and return the results
|
|
303
|
+
return results
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, ClassVar
|
|
3
|
+
|
|
4
|
+
from langchain_core.tools import BaseTool
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from mcp_use.logging import logger
|
|
8
|
+
|
|
9
|
+
from .base_tool import MCPServerTool
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UseToolInput(BaseModel):
|
|
13
|
+
"""Input for using a tool from a specific server"""
|
|
14
|
+
|
|
15
|
+
server_name: str = Field(description="The name of the MCP server containing the tool")
|
|
16
|
+
tool_name: str = Field(description="The name of the tool to execute")
|
|
17
|
+
tool_input: dict[str, Any] | str = Field(
|
|
18
|
+
description="The input to pass to the tool. Can be a dictionary of parameters or a string"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class UseToolFromServerTool(MCPServerTool):
|
|
23
|
+
"""Tool for directly executing a tool from a specific server."""
|
|
24
|
+
|
|
25
|
+
name: ClassVar[str] = "use_tool_from_server"
|
|
26
|
+
description: ClassVar[str] = (
|
|
27
|
+
"Execute a specific tool on a specific server without first connecting to it. "
|
|
28
|
+
"This is a direct execution shortcut that combines connection and tool execution "
|
|
29
|
+
"into a single step. Specify the server name, tool name, and the input to the tool."
|
|
30
|
+
)
|
|
31
|
+
args_schema: ClassVar[type[BaseModel]] = UseToolInput
|
|
32
|
+
|
|
33
|
+
async def _arun(
|
|
34
|
+
self, server_name: str, tool_name: str, tool_input: dict[str, Any] | str
|
|
35
|
+
) -> str:
|
|
36
|
+
"""Execute a tool from a specific server."""
|
|
37
|
+
# Check if server exists
|
|
38
|
+
servers = self.server_manager.client.get_server_names()
|
|
39
|
+
if server_name not in servers:
|
|
40
|
+
available = ", ".join(servers) if servers else "none"
|
|
41
|
+
return f"Server '{server_name}' not found. Available servers: {available}"
|
|
42
|
+
|
|
43
|
+
# Connect to the server if not already connected or not the active server
|
|
44
|
+
is_connected = server_name == self.server_manager.active_server
|
|
45
|
+
|
|
46
|
+
if not is_connected:
|
|
47
|
+
try:
|
|
48
|
+
# Create or get session for this server
|
|
49
|
+
try:
|
|
50
|
+
session = self.server_manager.client.get_session(server_name)
|
|
51
|
+
logger.debug(f"Using existing session for server '{server_name}'")
|
|
52
|
+
except ValueError:
|
|
53
|
+
logger.debug(f"Creating new session for server '{server_name}' for tool use")
|
|
54
|
+
session = await self.server_manager.client.create_session(server_name)
|
|
55
|
+
|
|
56
|
+
# Check if we have tools for this server, if not get them
|
|
57
|
+
if server_name not in self.server_manager._server_tools:
|
|
58
|
+
connector = session.connector
|
|
59
|
+
self.server_manager._server_tools[
|
|
60
|
+
server_name
|
|
61
|
+
] = await self.server_manager.adapter._create_tools_from_connectors([connector])
|
|
62
|
+
self.server_manager.initialized_servers[server_name] = True
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.error(f"Error connecting to server '{server_name}' for tool use: {e}")
|
|
65
|
+
return f"Failed to connect to server '{server_name}': {str(e)}"
|
|
66
|
+
|
|
67
|
+
# Get tools for the server
|
|
68
|
+
server_tools = self.server_manager._server_tools.get(server_name, [])
|
|
69
|
+
if not server_tools:
|
|
70
|
+
return f"No tools found for server '{server_name}'"
|
|
71
|
+
|
|
72
|
+
# Find the requested tool
|
|
73
|
+
target_tool = None
|
|
74
|
+
for tool in server_tools:
|
|
75
|
+
if tool.name == tool_name:
|
|
76
|
+
target_tool = tool
|
|
77
|
+
break
|
|
78
|
+
|
|
79
|
+
if not target_tool:
|
|
80
|
+
tool_names = [t.name for t in server_tools]
|
|
81
|
+
return (
|
|
82
|
+
f"Tool '{tool_name}' not found on server '{server_name}'. "
|
|
83
|
+
f"Available tools: {', '.join(tool_names)}"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Execute the tool with the provided input
|
|
87
|
+
try:
|
|
88
|
+
# Parse the input based on target tool's schema
|
|
89
|
+
structured_input = self._parse_tool_input(target_tool, tool_input)
|
|
90
|
+
if structured_input is None:
|
|
91
|
+
return (
|
|
92
|
+
f"Could not parse input for tool '{tool_name}'."
|
|
93
|
+
" Please check the input format and try again."
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Store the previous active server
|
|
97
|
+
previous_active = self.server_manager.active_server
|
|
98
|
+
|
|
99
|
+
# Temporarily set this server as active
|
|
100
|
+
self.server_manager.active_server = server_name
|
|
101
|
+
|
|
102
|
+
# Execute the tool
|
|
103
|
+
logger.info(
|
|
104
|
+
f"Executing tool '{tool_name}' on server '{server_name}'"
|
|
105
|
+
"with input: {structured_input}"
|
|
106
|
+
)
|
|
107
|
+
result = await target_tool._arun(**structured_input)
|
|
108
|
+
|
|
109
|
+
# Restore the previous active server
|
|
110
|
+
self.server_manager.active_server = previous_active
|
|
111
|
+
|
|
112
|
+
return result
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.error(f"Error executing tool '{tool_name}' on server '{server_name}': {e}")
|
|
116
|
+
return (
|
|
117
|
+
f"Error executing tool '{tool_name}' on server '{server_name}': {str(e)}. "
|
|
118
|
+
f"Make sure the input format is correct for this tool."
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def _parse_tool_input(self, tool: BaseTool, input_data: dict[str, Any] | str) -> dict[str, Any]:
|
|
122
|
+
"""
|
|
123
|
+
Parse the input data according to the tool's schema.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
tool: The target tool
|
|
127
|
+
input_data: The input data, either a dictionary or a string
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
A dictionary with properly structured input for the tool
|
|
131
|
+
"""
|
|
132
|
+
# If input is already a dict, use it directly
|
|
133
|
+
if isinstance(input_data, dict):
|
|
134
|
+
return input_data
|
|
135
|
+
|
|
136
|
+
# Try to parse as JSON first
|
|
137
|
+
if isinstance(input_data, str):
|
|
138
|
+
try:
|
|
139
|
+
return json.loads(input_data)
|
|
140
|
+
except json.JSONDecodeError:
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
# For string input, we need to determine which parameter name to use
|
|
144
|
+
if hasattr(tool, "args_schema") and tool.args_schema:
|
|
145
|
+
schema_cls = tool.args_schema
|
|
146
|
+
field_names = list(schema_cls.__fields__.keys())
|
|
147
|
+
|
|
148
|
+
# If schema has only one field, use that
|
|
149
|
+
if len(field_names) == 1:
|
|
150
|
+
return {field_names[0]: input_data}
|
|
151
|
+
|
|
152
|
+
# Look for common input field names
|
|
153
|
+
for name in field_names:
|
|
154
|
+
if name.lower() in ["input", "query", "url", tool.name.lower()]:
|
|
155
|
+
return {name: input_data}
|
|
156
|
+
|
|
157
|
+
# Default to first field if we can't determine
|
|
158
|
+
return {field_names[0]: input_data}
|
|
159
|
+
|
|
160
|
+
# If we get here something went wrong
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
def _run(self, server_name: str, tool_name: str, tool_input: dict[str, Any] | str) -> str:
|
|
164
|
+
"""Synchronous version that raises a NotImplementedError."""
|
|
165
|
+
raise NotImplementedError(
|
|
166
|
+
"UseToolFromServerTool requires async execution. Use _arun instead."
|
|
167
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-use
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.8
|
|
4
4
|
Summary: MCP Library for LLMs
|
|
5
5
|
Author-email: Pietro Zullo <pietro.zullo@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -18,7 +18,7 @@ Requires-Dist: aiohttp>=3.9.0
|
|
|
18
18
|
Requires-Dist: jsonschema-pydantic>=0.1.0
|
|
19
19
|
Requires-Dist: langchain-community>=0.0.10
|
|
20
20
|
Requires-Dist: langchain>=0.1.0
|
|
21
|
-
Requires-Dist: mcp
|
|
21
|
+
Requires-Dist: mcp>=1.5.0
|
|
22
22
|
Requires-Dist: pydantic>=2.0.0
|
|
23
23
|
Requires-Dist: python-dotenv>=1.0.0
|
|
24
24
|
Requires-Dist: typing-extensions>=4.8.0
|
|
@@ -35,6 +35,8 @@ Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
|
35
35
|
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
36
36
|
Provides-Extra: openai
|
|
37
37
|
Requires-Dist: openai>=1.10.0; extra == 'openai'
|
|
38
|
+
Provides-Extra: search
|
|
39
|
+
Requires-Dist: fastembed>=0.0.1; extra == 'search'
|
|
38
40
|
Description-Content-Type: text/markdown
|
|
39
41
|
|
|
40
42
|
<picture>
|
|
@@ -63,13 +65,13 @@ Description-Content-Type: text/markdown
|
|
|
63
65
|
|
|
64
66
|
| Feature | Description |
|
|
65
67
|
|---------|-------------|
|
|
66
|
-
| 🔄 **Ease of use** | Create your first MCP capable agent you need only 6 lines of code |
|
|
67
|
-
| 🤖 **LLM Flexibility** | Works with any langchain supported LLM that supports tool calling (OpenAI, Anthropic, Groq, LLama etc.) |
|
|
68
|
-
| 🌐 **HTTP Support** | Direct connection to MCP servers running on specific HTTP ports |
|
|
69
|
-
| ⚙️ **Dynamic Server Selection** | Agents can dynamically choose the most appropriate MCP server for a given task from the available pool |
|
|
70
|
-
| 🧩 **Multi-Server Support** | Use multiple MCP servers simultaneously in a single agent |
|
|
71
|
-
| 🛡️ **Tool Restrictions** | Restrict potentially dangerous tools like file system or network access |
|
|
72
|
-
| 🔧 **Custom Agents** | Build your own agents with any framework using the LangChain adapter or create new adapters |
|
|
68
|
+
| 🔄 [**Ease of use**](#quick-start) | Create your first MCP capable agent you need only 6 lines of code |
|
|
69
|
+
| 🤖 [**LLM Flexibility**](#installing-langchain-providers) | Works with any langchain supported LLM that supports tool calling (OpenAI, Anthropic, Groq, LLama etc.) |
|
|
70
|
+
| 🌐 [**HTTP Support**](#http-connection-example) | Direct connection to MCP servers running on specific HTTP ports |
|
|
71
|
+
| ⚙️ [**Dynamic Server Selection**](#dynamic-server-selection-server-manager) | Agents can dynamically choose the most appropriate MCP server for a given task from the available pool |
|
|
72
|
+
| 🧩 [**Multi-Server Support**](#multi-server-support) | Use multiple MCP servers simultaneously in a single agent |
|
|
73
|
+
| 🛡️ [**Tool Restrictions**](#tool-access-control) | Restrict potentially dangerous tools like file system or network access |
|
|
74
|
+
| 🔧 [**Custom Agents**](#build-a-custom-agent) | Build your own agents with any framework using the LangChain adapter or create new adapters |
|
|
73
75
|
|
|
74
76
|
|
|
75
77
|
# Quick start
|
|
@@ -610,7 +612,7 @@ This is useful when you only need to see the agent's steps and decision-making p
|
|
|
610
612
|
|
|
611
613
|
# Contributing
|
|
612
614
|
|
|
613
|
-
We love contributions! Feel free to open issues for bugs or feature requests.
|
|
615
|
+
We love contributions! Feel free to open issues for bugs or feature requests. Look at [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
614
616
|
|
|
615
617
|
# Requirements
|
|
616
618
|
|
|
@@ -5,11 +5,10 @@ mcp_use/logging.py,sha256=UhQdMx0H0q08-ZPjY_hAJVErkEUAkU1oahHqwdfdK_U,4274
|
|
|
5
5
|
mcp_use/session.py,sha256=Z4EZTUnQUX0QyGMzkJIrMRTX4SDk6qQUoBld408LIJE,3449
|
|
6
6
|
mcp_use/adapters/__init__.py,sha256=-xCrgPThuX7x0PHGFDdjb7M-mgw6QV3sKu5PM7ShnRg,275
|
|
7
7
|
mcp_use/adapters/base.py,sha256=ixLHXp8FWdyZPx7Kh6s-4jEVs3qT4DWrApSLXfqzNws,6141
|
|
8
|
-
mcp_use/adapters/langchain_adapter.py,sha256=
|
|
9
|
-
mcp_use/agents/__init__.py,sha256=
|
|
8
|
+
mcp_use/adapters/langchain_adapter.py,sha256=s8IHPPtqqXMmWQfeBqwESs3SZA6_ECSiGRwdTOIWki0,6417
|
|
9
|
+
mcp_use/agents/__init__.py,sha256=N3eVYP2PxqNO2KcQv5fY8UMUX2W3eLTNkkzuFIJ1DUA,261
|
|
10
10
|
mcp_use/agents/base.py,sha256=bfuldi_89AbSbNc8KeTiCArRT9V62CNxHOWYkLHWjyA,1605
|
|
11
|
-
mcp_use/agents/mcpagent.py,sha256=
|
|
12
|
-
mcp_use/agents/server_manager.py,sha256=ShmjrvDtmU7dJtfVlw_srC3t5f_B-QtifzIiV4mfsRA,11315
|
|
11
|
+
mcp_use/agents/mcpagent.py,sha256=khzBFeW-mJxA4YeCA6v0jD7czu9MwOjtIXD0_WzNkQE,23795
|
|
13
12
|
mcp_use/agents/prompts/system_prompt_builder.py,sha256=GH5Pvl49IBpKpZA9YTI83xMsdYSkRN_hw4LFHkKtxbg,4122
|
|
14
13
|
mcp_use/agents/prompts/templates.py,sha256=AZKrGWuI516C-PmyOPvxDBibNdqJtN24sOHTGR06bi4,1933
|
|
15
14
|
mcp_use/connectors/__init__.py,sha256=jnd-7pPPJMb0UNJ6aD9lInj5Tlamc8lA_mFyG8RWJpo,385
|
|
@@ -17,12 +16,22 @@ mcp_use/connectors/base.py,sha256=5TcXB-I5zrwPtedB6dShceNucsK3wHBeGC2yDVq8X48,48
|
|
|
17
16
|
mcp_use/connectors/http.py,sha256=2ZG5JxcK1WZ4jkTfTir6bEQLMxXBTPHyi0s42RHGeFs,2837
|
|
18
17
|
mcp_use/connectors/stdio.py,sha256=MTzsqmVVihACUKngE-g5BignK3jAFds2CFv3aSzbJfs,2608
|
|
19
18
|
mcp_use/connectors/websocket.py,sha256=LeU53YI3zjbwKq5GzFRziqA_z9Dn5qACiNyxWDrn2ns,9540
|
|
19
|
+
mcp_use/managers/__init__.py,sha256=rzsJbOhtlmxNQLGcdmtmHaiExEXmiQiUuzPrAgKhAJw,439
|
|
20
|
+
mcp_use/managers/server_manager.py,sha256=YVl5ciNIQfVzP-BR9hA0ac6YSwq0WChpA_Lxvh2e9HE,3984
|
|
21
|
+
mcp_use/managers/tools/__init__.py,sha256=JrA5iTRdtbgwROJE8pQ7GH1sYnqBRcgj4NzFVADKbQ4,510
|
|
22
|
+
mcp_use/managers/tools/base_tool.py,sha256=Jbbp7SwmHKDk8jT_6yVIv7iNsn6KaV_PljWuhhLcbXg,509
|
|
23
|
+
mcp_use/managers/tools/connect_server.py,sha256=MGYQCl11q-w6gSIYuT44dDk7ILV3Oh7kGAJ4fsNXbso,2923
|
|
24
|
+
mcp_use/managers/tools/disconnect_server.py,sha256=4487QlLbXAh9JyfGioc6DMWd0n_dkaa8RLMvsoNZv3E,1602
|
|
25
|
+
mcp_use/managers/tools/get_active_server.py,sha256=LRcHbKZopMl1PiO4D4JS4s0fwtrvtMtvb4kpnoAE8fQ,1015
|
|
26
|
+
mcp_use/managers/tools/list_servers_tool.py,sha256=OPDSMNe-VuAhlUyhDnR4CiuZFpoMhnhWpAablwO5S0k,1897
|
|
27
|
+
mcp_use/managers/tools/search_tools.py,sha256=0BfFesCBJsdCAG2tPCM-c49tmBJLwLQoR_U-sj-rp2s,11628
|
|
28
|
+
mcp_use/managers/tools/use_tool.py,sha256=r7k7uMYzrk353qw7M5h1utu_IR2G85uMZkrNcg2RyZA,6824
|
|
20
29
|
mcp_use/task_managers/__init__.py,sha256=4dgW5N61iiPLpwjU2rrn_uqrL8mmDJFDaF9Lukzk65A,486
|
|
21
30
|
mcp_use/task_managers/base.py,sha256=ksNdxTwq8N-zqymxVoKGnWXq9iqkLYC61uB91o6Mh-4,4888
|
|
22
31
|
mcp_use/task_managers/sse.py,sha256=WysmjwqRI3meXMZY_F4y9tSBMvSiUZfTJQfitM5l6jQ,2529
|
|
23
32
|
mcp_use/task_managers/stdio.py,sha256=DEISpXv4mo3d5a-WT8lkWbrXJwUh7QW0nMT_IM3fHGg,2269
|
|
24
33
|
mcp_use/task_managers/websocket.py,sha256=ZbCqdGgzCRtsXzRGFws-f2OzH8cPAkN4sJNDwEpRmCc,1915
|
|
25
|
-
mcp_use-1.2.
|
|
26
|
-
mcp_use-1.2.
|
|
27
|
-
mcp_use-1.2.
|
|
28
|
-
mcp_use-1.2.
|
|
34
|
+
mcp_use-1.2.8.dist-info/METADATA,sha256=ld-cA_dlzxM-D5VkJMLqgr3WeyGzHVRx_pQ_sKQxRL8,18493
|
|
35
|
+
mcp_use-1.2.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
36
|
+
mcp_use-1.2.8.dist-info/licenses/LICENSE,sha256=7Pw7dbwJSBw8zH-WE03JnR5uXvitRtaGTP9QWPcexcs,1068
|
|
37
|
+
mcp_use-1.2.8.dist-info/RECORD,,
|
mcp_use/agents/server_manager.py
DELETED
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
from langchain_core.tools import BaseTool, StructuredTool
|
|
2
|
-
from pydantic import BaseModel, Field
|
|
3
|
-
|
|
4
|
-
from mcp_use.client import MCPClient
|
|
5
|
-
from mcp_use.logging import logger
|
|
6
|
-
|
|
7
|
-
from ..adapters.langchain_adapter import LangChainAdapter
|
|
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 DisconnectServerInput(BaseModel):
|
|
17
|
-
"""Empty input for disconnecting from the current server"""
|
|
18
|
-
|
|
19
|
-
pass
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class ListServersInput(BaseModel):
|
|
23
|
-
"""Empty input for listing available servers"""
|
|
24
|
-
|
|
25
|
-
pass
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class CurrentServerInput(BaseModel):
|
|
29
|
-
"""Empty input for checking current server"""
|
|
30
|
-
|
|
31
|
-
pass
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class ServerManager:
|
|
35
|
-
"""Manages MCP servers and provides tools for server selection and management.
|
|
36
|
-
|
|
37
|
-
This class allows an agent to discover and select which MCP server to use,
|
|
38
|
-
dynamically activating the tools for the selected server.
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
def __init__(self, client: MCPClient, adapter: LangChainAdapter) -> None:
|
|
42
|
-
"""Initialize the server manager.
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
client: The MCPClient instance managing server connections
|
|
46
|
-
adapter: The LangChainAdapter for converting MCP tools to LangChain tools
|
|
47
|
-
"""
|
|
48
|
-
self.client = client
|
|
49
|
-
self.adapter = adapter
|
|
50
|
-
self.active_server: str | None = None
|
|
51
|
-
self.initialized_servers: dict[str, bool] = {}
|
|
52
|
-
self._server_tools: dict[str, list[BaseTool]] = {}
|
|
53
|
-
|
|
54
|
-
async def initialize(self) -> None:
|
|
55
|
-
"""Initialize the server manager and prepare server management tools."""
|
|
56
|
-
# Make sure we have server configurations
|
|
57
|
-
if not self.client.get_server_names():
|
|
58
|
-
logger.warning("No MCP servers defined in client configuration")
|
|
59
|
-
|
|
60
|
-
async def get_server_management_tools(self) -> list[BaseTool]:
|
|
61
|
-
"""Get tools for managing server connections.
|
|
62
|
-
|
|
63
|
-
Returns:
|
|
64
|
-
List of LangChain tools for server management
|
|
65
|
-
"""
|
|
66
|
-
# Create structured tools for server management with direct parameter passing
|
|
67
|
-
list_servers_tool = StructuredTool.from_function(
|
|
68
|
-
coroutine=self.list_servers,
|
|
69
|
-
name="list_mcp_servers",
|
|
70
|
-
description="Lists all available MCP (Model Context Protocol) servers that can be "
|
|
71
|
-
"connected to, along with the tools available on each server. "
|
|
72
|
-
"Use this tool to discover servers and see what functionalities they offer.",
|
|
73
|
-
args_schema=ListServersInput,
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
connect_server_tool = StructuredTool.from_function(
|
|
77
|
-
coroutine=self.connect_to_server,
|
|
78
|
-
name="connect_to_mcp_server",
|
|
79
|
-
description="Connect to a specific MCP (Model Context Protocol) server to use its "
|
|
80
|
-
"tools. Use this tool to connect to a specific server and use its tools.",
|
|
81
|
-
args_schema=ServerActionInput,
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
get_active_server_tool = StructuredTool.from_function(
|
|
85
|
-
coroutine=self.get_active_server,
|
|
86
|
-
name="get_active_mcp_server",
|
|
87
|
-
description="Get the currently active MCP (Model Context Protocol) server",
|
|
88
|
-
args_schema=CurrentServerInput,
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
disconnect_server_tool = StructuredTool.from_function(
|
|
92
|
-
func=None,
|
|
93
|
-
coroutine=self.disconnect_from_server,
|
|
94
|
-
name="disconnect_from_mcp_server",
|
|
95
|
-
description="Disconnect from the currently active MCP (Model Context Protocol) server",
|
|
96
|
-
args_schema=DisconnectServerInput,
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
return [
|
|
100
|
-
list_servers_tool,
|
|
101
|
-
connect_server_tool,
|
|
102
|
-
get_active_server_tool,
|
|
103
|
-
disconnect_server_tool,
|
|
104
|
-
]
|
|
105
|
-
|
|
106
|
-
async def list_servers(self) -> str:
|
|
107
|
-
"""List all available MCP servers along with their available tools.
|
|
108
|
-
|
|
109
|
-
Returns:
|
|
110
|
-
String listing all available servers and their tools.
|
|
111
|
-
"""
|
|
112
|
-
servers = self.client.get_server_names()
|
|
113
|
-
if not servers:
|
|
114
|
-
return "No MCP servers are currently defined."
|
|
115
|
-
|
|
116
|
-
result = "Available MCP servers:\n"
|
|
117
|
-
for i, server_name in enumerate(servers):
|
|
118
|
-
active_marker = " (ACTIVE)" if server_name == self.active_server else ""
|
|
119
|
-
result += f"{i + 1}. {server_name}{active_marker}\n"
|
|
120
|
-
|
|
121
|
-
tools: list[BaseTool] = []
|
|
122
|
-
try:
|
|
123
|
-
# Check cache first
|
|
124
|
-
if server_name in self._server_tools:
|
|
125
|
-
tools = self._server_tools[server_name]
|
|
126
|
-
else:
|
|
127
|
-
# Attempt to get/create session without setting active
|
|
128
|
-
session = None
|
|
129
|
-
try:
|
|
130
|
-
session = self.client.get_session(server_name)
|
|
131
|
-
logger.debug(
|
|
132
|
-
f"Using existing session for server '{server_name}' to list tools."
|
|
133
|
-
)
|
|
134
|
-
except ValueError:
|
|
135
|
-
try:
|
|
136
|
-
# Only create session if needed, don't set active
|
|
137
|
-
session = await self.client.create_session(server_name)
|
|
138
|
-
logger.debug(f"Temporarily created session for server '{server_name}'")
|
|
139
|
-
except Exception:
|
|
140
|
-
logger.warning(f"Could not create session for server '{server_name}'")
|
|
141
|
-
|
|
142
|
-
# Fetch tools if session is available
|
|
143
|
-
if session:
|
|
144
|
-
try:
|
|
145
|
-
connector = session.connector
|
|
146
|
-
fetched_tools = await self.adapter._create_tools_from_connectors(
|
|
147
|
-
[connector]
|
|
148
|
-
)
|
|
149
|
-
self._server_tools[server_name] = fetched_tools # Cache tools
|
|
150
|
-
self.initialized_servers[server_name] = True # Mark as initialized
|
|
151
|
-
tools = fetched_tools
|
|
152
|
-
logger.debug(f"Fetched {len(tools)} tools for server '{server_name}'.")
|
|
153
|
-
except Exception as e:
|
|
154
|
-
logger.warning(f"Could not fetch tools for server '{server_name}': {e}")
|
|
155
|
-
# Keep tools as empty list if fetching failed
|
|
156
|
-
|
|
157
|
-
# Append tool names to the result string
|
|
158
|
-
if tools:
|
|
159
|
-
tool_names = ", ".join([tool.name for tool in tools])
|
|
160
|
-
result += f" Tools: {tool_names}\n"
|
|
161
|
-
else:
|
|
162
|
-
result += " Tools: (Could not retrieve or none available)\n"
|
|
163
|
-
|
|
164
|
-
except Exception as e:
|
|
165
|
-
logger.error(f"Unexpected error listing tools for server '{server_name}': {e}")
|
|
166
|
-
result += " Tools: (Error retrieving tools)\n"
|
|
167
|
-
|
|
168
|
-
return result
|
|
169
|
-
|
|
170
|
-
async def connect_to_server(self, server_name: str) -> str:
|
|
171
|
-
"""Connect to a specific MCP server.
|
|
172
|
-
|
|
173
|
-
Args:
|
|
174
|
-
server_name: The name of the server to connect to
|
|
175
|
-
|
|
176
|
-
Returns:
|
|
177
|
-
Status message about the connection
|
|
178
|
-
"""
|
|
179
|
-
# Check if server exists
|
|
180
|
-
servers = self.client.get_server_names()
|
|
181
|
-
if server_name not in servers:
|
|
182
|
-
available = ", ".join(servers) if servers else "none"
|
|
183
|
-
return f"Server '{server_name}' not found. Available servers: {available}"
|
|
184
|
-
|
|
185
|
-
# If we're already connected to this server, just return
|
|
186
|
-
if self.active_server == server_name:
|
|
187
|
-
return f"Already connected to MCP server '{server_name}'"
|
|
188
|
-
|
|
189
|
-
try:
|
|
190
|
-
# Create or get session for this server
|
|
191
|
-
try:
|
|
192
|
-
session = self.client.get_session(server_name)
|
|
193
|
-
logger.debug(f"Using existing session for server '{server_name}'")
|
|
194
|
-
except ValueError:
|
|
195
|
-
logger.debug(f"Creating new session for server '{server_name}'")
|
|
196
|
-
session = await self.client.create_session(server_name)
|
|
197
|
-
|
|
198
|
-
# Set as active server
|
|
199
|
-
self.active_server = server_name
|
|
200
|
-
|
|
201
|
-
# Initialize server tools if not already initialized
|
|
202
|
-
if server_name not in self._server_tools:
|
|
203
|
-
connector = session.connector
|
|
204
|
-
self._server_tools[server_name] = await self.adapter._create_tools_from_connectors(
|
|
205
|
-
[connector]
|
|
206
|
-
)
|
|
207
|
-
self.initialized_servers[server_name] = True
|
|
208
|
-
|
|
209
|
-
server_tools = self._server_tools.get(server_name, [])
|
|
210
|
-
num_tools = len(server_tools)
|
|
211
|
-
|
|
212
|
-
tool_descriptions = "\nAvailable tools for this server:\n"
|
|
213
|
-
for i, tool in enumerate(server_tools):
|
|
214
|
-
tool_descriptions += f"{i + 1}. {tool.name}: {tool.description}\n"
|
|
215
|
-
|
|
216
|
-
return (
|
|
217
|
-
f"Connected to MCP server '{server_name}'. "
|
|
218
|
-
f"{num_tools} tools are now available."
|
|
219
|
-
f"{tool_descriptions}"
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
except Exception as e:
|
|
223
|
-
logger.error(f"Error connecting to server '{server_name}': {e}")
|
|
224
|
-
return f"Failed to connect to server '{server_name}': {str(e)}"
|
|
225
|
-
|
|
226
|
-
async def get_active_server(self) -> str:
|
|
227
|
-
"""Get the currently active MCP server.
|
|
228
|
-
|
|
229
|
-
Returns:
|
|
230
|
-
Name of the active server or message if none is active
|
|
231
|
-
"""
|
|
232
|
-
if not self.active_server:
|
|
233
|
-
return (
|
|
234
|
-
"No MCP server is currently active. "
|
|
235
|
-
"Use connect_to_mcp_server to connect to a server."
|
|
236
|
-
)
|
|
237
|
-
return f"Currently active MCP server: {self.active_server}"
|
|
238
|
-
|
|
239
|
-
async def disconnect_from_server(self) -> str:
|
|
240
|
-
"""Disconnect from the currently active MCP server.
|
|
241
|
-
|
|
242
|
-
Returns:
|
|
243
|
-
Status message about the disconnection
|
|
244
|
-
"""
|
|
245
|
-
|
|
246
|
-
if not self.active_server:
|
|
247
|
-
return "No MCP server is currently active, so there's nothing to disconnect from."
|
|
248
|
-
|
|
249
|
-
server_name = self.active_server
|
|
250
|
-
try:
|
|
251
|
-
# Clear the active server
|
|
252
|
-
self.active_server = None
|
|
253
|
-
|
|
254
|
-
# Note: We're not actually closing the session here, just 'deactivating'
|
|
255
|
-
# This way we keep the session cache without requiring reconnection if needed again
|
|
256
|
-
# TODO: consider closing the sessions
|
|
257
|
-
|
|
258
|
-
return f"Successfully disconnected from MCP server '{server_name}'."
|
|
259
|
-
except Exception as e:
|
|
260
|
-
logger.error(f"Error disconnecting from server '{server_name}': {e}")
|
|
261
|
-
return f"Failed to disconnect from server '{server_name}': {str(e)}"
|
|
262
|
-
|
|
263
|
-
async def get_active_server_tools(self) -> list[BaseTool]:
|
|
264
|
-
"""Get the tools for the currently active server.
|
|
265
|
-
|
|
266
|
-
Returns:
|
|
267
|
-
List of LangChain tools for the active server or empty list if no active server
|
|
268
|
-
"""
|
|
269
|
-
if not self.active_server:
|
|
270
|
-
return []
|
|
271
|
-
|
|
272
|
-
return self._server_tools.get(self.active_server, [])
|
|
273
|
-
|
|
274
|
-
async def get_all_tools(self) -> list[BaseTool]:
|
|
275
|
-
"""Get all tools - both server management tools and tools for the active server.
|
|
276
|
-
|
|
277
|
-
Returns:
|
|
278
|
-
Combined list of server management tools and active server tools
|
|
279
|
-
"""
|
|
280
|
-
management_tools = await self.get_server_management_tools()
|
|
281
|
-
active_server_tools = await self.get_active_server_tools()
|
|
282
|
-
return management_tools + active_server_tools
|
|
File without changes
|
|
File without changes
|