mcp-use 1.2.7__py3-none-any.whl → 1.2.9__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 +85 -7
- mcp_use/connectors/base.py +20 -0
- 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.7.dist-info → mcp_use-1.2.9.dist-info}/METADATA +51 -10
- {mcp_use-1.2.7.dist-info → mcp_use-1.2.9.dist-info}/RECORD +18 -9
- mcp_use/agents/server_manager.py +0 -282
- {mcp_use-1.2.7.dist-info → mcp_use-1.2.9.dist-info}/WHEEL +0 -0
- {mcp_use-1.2.7.dist-info → mcp_use-1.2.9.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
|
@@ -6,6 +6,7 @@ to provide a simple interface for using MCP tools with different LLMs.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
|
+
from collections.abc import AsyncIterator
|
|
9
10
|
|
|
10
11
|
from langchain.agents import AgentExecutor, create_tool_calling_agent
|
|
11
12
|
from langchain.globals import set_debug
|
|
@@ -23,9 +24,9 @@ from mcp_use.session import MCPSession
|
|
|
23
24
|
|
|
24
25
|
from ..adapters.langchain_adapter import LangChainAdapter
|
|
25
26
|
from ..logging import logger
|
|
27
|
+
from ..managers.server_manager import ServerManager
|
|
26
28
|
from .prompts.system_prompt_builder import create_system_message
|
|
27
29
|
from .prompts.templates import DEFAULT_SYSTEM_PROMPT_TEMPLATE, SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE
|
|
28
|
-
from .server_manager import ServerManager
|
|
29
30
|
|
|
30
31
|
set_debug(logger.level == logging.DEBUG)
|
|
31
32
|
|
|
@@ -42,7 +43,6 @@ class MCPAgent:
|
|
|
42
43
|
llm: BaseLanguageModel,
|
|
43
44
|
client: MCPClient | None = None,
|
|
44
45
|
connectors: list[BaseConnector] | None = None,
|
|
45
|
-
server_name: str | None = None,
|
|
46
46
|
max_steps: int = 5,
|
|
47
47
|
auto_initialize: bool = False,
|
|
48
48
|
memory_enabled: bool = True,
|
|
@@ -59,7 +59,6 @@ class MCPAgent:
|
|
|
59
59
|
llm: The LangChain LLM to use.
|
|
60
60
|
client: The MCPClient to use. If provided, connector is ignored.
|
|
61
61
|
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
62
|
max_steps: The maximum number of steps to take.
|
|
64
63
|
auto_initialize: Whether to automatically initialize the agent when run is called.
|
|
65
64
|
memory_enabled: Whether to maintain conversation history for context.
|
|
@@ -72,7 +71,6 @@ class MCPAgent:
|
|
|
72
71
|
self.llm = llm
|
|
73
72
|
self.client = client
|
|
74
73
|
self.connectors = connectors or []
|
|
75
|
-
self.server_name = server_name
|
|
76
74
|
self.max_steps = max_steps
|
|
77
75
|
self.auto_initialize = auto_initialize
|
|
78
76
|
self.memory_enabled = memory_enabled
|
|
@@ -114,7 +112,7 @@ class MCPAgent:
|
|
|
114
112
|
if self.use_server_manager and self.server_manager:
|
|
115
113
|
await self.server_manager.initialize()
|
|
116
114
|
# Get server management tools
|
|
117
|
-
management_tools =
|
|
115
|
+
management_tools = self.server_manager.tools
|
|
118
116
|
self._tools = management_tools
|
|
119
117
|
logger.info(
|
|
120
118
|
f"🔧 Server manager mode active with {len(management_tools)} management tools"
|
|
@@ -307,6 +305,86 @@ class MCPAgent:
|
|
|
307
305
|
"""
|
|
308
306
|
return self.disallowed_tools
|
|
309
307
|
|
|
308
|
+
async def _generate_response_chunks_async(
|
|
309
|
+
self,
|
|
310
|
+
query: str,
|
|
311
|
+
max_steps: int | None = None,
|
|
312
|
+
manage_connector: bool = True,
|
|
313
|
+
external_history: list[BaseMessage] | None = None,
|
|
314
|
+
) -> AsyncIterator[str]:
|
|
315
|
+
"""Internal async generator yielding response chunks.
|
|
316
|
+
|
|
317
|
+
The implementation purposefully keeps the logic compact:
|
|
318
|
+
1. Ensure the agent is initialised (optionally handling connector
|
|
319
|
+
lifecycle).
|
|
320
|
+
2. Forward the *same* inputs we use for ``run`` to LangChain's
|
|
321
|
+
``AgentExecutor.astream``.
|
|
322
|
+
3. Diff the growing ``output`` field coming from LangChain and yield
|
|
323
|
+
only the new part so the caller receives *incremental* chunks.
|
|
324
|
+
4. Persist conversation history when memory is enabled.
|
|
325
|
+
"""
|
|
326
|
+
|
|
327
|
+
# 1. Initialise on-demand ------------------------------------------------
|
|
328
|
+
initialised_here = False
|
|
329
|
+
if (manage_connector and not self._initialized) or (
|
|
330
|
+
not self._initialized and self.auto_initialize
|
|
331
|
+
):
|
|
332
|
+
await self.initialize()
|
|
333
|
+
initialised_here = True
|
|
334
|
+
|
|
335
|
+
if not self._agent_executor:
|
|
336
|
+
raise RuntimeError("MCP agent failed to initialise – call initialise() first?")
|
|
337
|
+
|
|
338
|
+
# 2. Build inputs --------------------------------------------------------
|
|
339
|
+
effective_max_steps = max_steps or self.max_steps
|
|
340
|
+
self._agent_executor.max_iterations = effective_max_steps
|
|
341
|
+
|
|
342
|
+
if self.memory_enabled:
|
|
343
|
+
self.add_to_history(HumanMessage(content=query))
|
|
344
|
+
|
|
345
|
+
history_to_use = (
|
|
346
|
+
external_history if external_history is not None else self._conversation_history
|
|
347
|
+
)
|
|
348
|
+
langchain_history: list[BaseMessage] = [
|
|
349
|
+
m for m in history_to_use if isinstance(m, HumanMessage | AIMessage)
|
|
350
|
+
]
|
|
351
|
+
inputs = {"input": query, "chat_history": langchain_history}
|
|
352
|
+
|
|
353
|
+
# 3. Stream & diff -------------------------------------------------------
|
|
354
|
+
accumulated = ""
|
|
355
|
+
async for event in self._agent_executor.astream(inputs):
|
|
356
|
+
yield event
|
|
357
|
+
|
|
358
|
+
# 4. Persist assistant message ------------------------------------------
|
|
359
|
+
if self.memory_enabled and accumulated:
|
|
360
|
+
self.add_to_history(AIMessage(content=accumulated))
|
|
361
|
+
|
|
362
|
+
# 5. House-keeping -------------------------------------------------------
|
|
363
|
+
if initialised_here and manage_connector:
|
|
364
|
+
await self.close()
|
|
365
|
+
|
|
366
|
+
async def astream(
|
|
367
|
+
self,
|
|
368
|
+
query: str,
|
|
369
|
+
max_steps: int | None = None,
|
|
370
|
+
manage_connector: bool = True,
|
|
371
|
+
external_history: list[BaseMessage] | None = None,
|
|
372
|
+
) -> AsyncIterator[str]:
|
|
373
|
+
"""Asynchronous streaming interface.
|
|
374
|
+
|
|
375
|
+
Example::
|
|
376
|
+
|
|
377
|
+
async for chunk in agent.astream("hello"):
|
|
378
|
+
print(chunk, end="|", flush=True)
|
|
379
|
+
"""
|
|
380
|
+
async for chunk in self._generate_response_chunks_async(
|
|
381
|
+
query=query,
|
|
382
|
+
max_steps=max_steps,
|
|
383
|
+
manage_connector=manage_connector,
|
|
384
|
+
external_history=external_history,
|
|
385
|
+
):
|
|
386
|
+
yield chunk
|
|
387
|
+
|
|
310
388
|
async def run(
|
|
311
389
|
self,
|
|
312
390
|
query: str,
|
|
@@ -391,7 +469,7 @@ class MCPAgent:
|
|
|
391
469
|
for step_num in range(steps):
|
|
392
470
|
# --- Check for tool updates if using server manager ---
|
|
393
471
|
if self.use_server_manager and self.server_manager:
|
|
394
|
-
current_tools =
|
|
472
|
+
current_tools = self.server_manager.tools
|
|
395
473
|
current_tool_names = {tool.name for tool in current_tools}
|
|
396
474
|
existing_tool_names = {tool.name for tool in self._tools}
|
|
397
475
|
|
|
@@ -412,7 +490,7 @@ class MCPAgent:
|
|
|
412
490
|
[tool.name for tool in self._tools], excluded_colors=["green", "red"]
|
|
413
491
|
)
|
|
414
492
|
|
|
415
|
-
logger.info(f"
|
|
493
|
+
logger.info(f"👣 Step {step_num + 1}/{steps}")
|
|
416
494
|
|
|
417
495
|
# --- Plan and execute the next step ---
|
|
418
496
|
try:
|
mcp_use/connectors/base.py
CHANGED
|
@@ -131,6 +131,26 @@ class BaseConnector(ABC):
|
|
|
131
131
|
resource = await self.client.read_resource(uri)
|
|
132
132
|
return resource.content, resource.mimeType
|
|
133
133
|
|
|
134
|
+
async def list_prompts(self) -> list[dict[str, Any]]:
|
|
135
|
+
"""List all available prompts from the MCP implementation."""
|
|
136
|
+
if not self.client:
|
|
137
|
+
raise RuntimeError("MCP client is not connected")
|
|
138
|
+
|
|
139
|
+
logger.debug("Listing prompts")
|
|
140
|
+
prompts = await self.client.list_prompts()
|
|
141
|
+
return prompts
|
|
142
|
+
|
|
143
|
+
async def get_prompt(
|
|
144
|
+
self, name: str, arguments: dict[str, Any] | None = None
|
|
145
|
+
) -> tuple[bytes, str]:
|
|
146
|
+
"""Get a prompt by name."""
|
|
147
|
+
if not self.client:
|
|
148
|
+
raise RuntimeError("MCP client is not connected")
|
|
149
|
+
|
|
150
|
+
logger.debug(f"Getting prompt: {name}")
|
|
151
|
+
prompt = await self.client.get_prompt(name, arguments)
|
|
152
|
+
return prompt
|
|
153
|
+
|
|
134
154
|
async def request(self, method: str, params: dict[str, Any] | None = None) -> Any:
|
|
135
155
|
"""Send a raw request to the MCP implementation."""
|
|
136
156
|
if not self.client:
|
|
@@ -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)
|