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

@@ -0,0 +1,105 @@
1
+ from langchain.schema import SystemMessage
2
+ from langchain_core.tools import BaseTool
3
+
4
+
5
+ def generate_tool_descriptions(
6
+ tools: list[BaseTool], disallowed_tools: list[str] | None = None
7
+ ) -> list[str]:
8
+ """
9
+ Generates a list of formatted tool descriptions, excluding disallowed tools.
10
+
11
+ Args:
12
+ tools: The list of available BaseTool objects.
13
+ disallowed_tools: A list of tool names to exclude.
14
+
15
+ Returns:
16
+ A list of strings, each describing a tool in the format "- tool_name: description".
17
+ """
18
+ disallowed_set = set(disallowed_tools or [])
19
+ tool_descriptions_list = []
20
+ for tool in tools:
21
+ if tool.name in disallowed_set:
22
+ continue
23
+ # Escape curly braces for formatting
24
+ escaped_desc = tool.description.replace("{", "{{").replace("}", "}}")
25
+ description = f"- {tool.name}: {escaped_desc}"
26
+ tool_descriptions_list.append(description)
27
+ return tool_descriptions_list
28
+
29
+
30
+ def build_system_prompt_content(
31
+ template: str, tool_description_lines: list[str], additional_instructions: str | None = None
32
+ ) -> str:
33
+ """
34
+ Builds the final system prompt string using a template, tool descriptions,
35
+ and optional additional instructions.
36
+
37
+ Args:
38
+ template: The system prompt template string (must contain '{tool_descriptions}').
39
+ tool_description_lines: A list of formatted tool description strings.
40
+ additional_instructions: Optional extra instructions to append.
41
+
42
+ Returns:
43
+ The fully formatted system prompt content string.
44
+ """
45
+ tool_descriptions_block = "\n".join(tool_description_lines)
46
+ # Add a check for missing placeholder to prevent errors
47
+ if "{tool_descriptions}" not in template:
48
+ # Handle this case: maybe append descriptions at the end or raise an error
49
+ # For now, let's append if placeholder is missing
50
+ print("Warning: '{tool_descriptions}' placeholder not found in template.")
51
+ system_prompt_content = template + "\n\nAvailable tools:\n" + tool_descriptions_block
52
+ else:
53
+ system_prompt_content = template.format(tool_descriptions=tool_descriptions_block)
54
+
55
+ if additional_instructions:
56
+ system_prompt_content += f"\n\n{additional_instructions}"
57
+
58
+ return system_prompt_content
59
+
60
+
61
+ def create_system_message(
62
+ tools: list[BaseTool],
63
+ system_prompt_template: str,
64
+ server_manager_template: str,
65
+ use_server_manager: bool,
66
+ disallowed_tools: list[str] | None = None,
67
+ user_provided_prompt: str | None = None,
68
+ additional_instructions: str | None = None,
69
+ ) -> SystemMessage:
70
+ """
71
+ Creates the final SystemMessage object for the agent.
72
+
73
+ Handles selecting the correct template, generating tool descriptions,
74
+ and incorporating user overrides and additional instructions.
75
+
76
+ Args:
77
+ tools: List of available tools.
78
+ system_prompt_template: The default system prompt template.
79
+ server_manager_template: The template to use when server manager is active.
80
+ use_server_manager: Flag indicating if server manager mode is enabled.
81
+ disallowed_tools: List of tool names to exclude.
82
+ user_provided_prompt: A complete system prompt provided by the user, overriding templates.
83
+ additional_instructions: Extra instructions to append to the template-based prompt.
84
+
85
+ Returns:
86
+ A SystemMessage object containing the final prompt content.
87
+ """
88
+ # If a complete user prompt is given, use it directly
89
+ if user_provided_prompt:
90
+ return SystemMessage(content=user_provided_prompt)
91
+
92
+ # Select the appropriate template
93
+ template_to_use = server_manager_template if use_server_manager else system_prompt_template
94
+
95
+ # Generate tool descriptions
96
+ tool_description_lines = generate_tool_descriptions(tools, disallowed_tools)
97
+
98
+ # Build the final prompt content
99
+ final_prompt_content = build_system_prompt_content(
100
+ template=template_to_use,
101
+ tool_description_lines=tool_description_lines,
102
+ additional_instructions=additional_instructions,
103
+ )
104
+
105
+ return SystemMessage(content=final_prompt_content)
@@ -0,0 +1,43 @@
1
+ # mcp_use/agents/prompts/templates.py
2
+
3
+ DEFAULT_SYSTEM_PROMPT_TEMPLATE = """You are a helpful AI assistant.
4
+ You have access to the following tools:
5
+
6
+ {tool_descriptions}
7
+
8
+ Use the following format:
9
+
10
+ Question: the input question you must answer
11
+ Thought: you should always think about what to do
12
+ Action: the action to take, should be one of the available tools
13
+ Action Input: the input to the action
14
+ Observation: the result of the action
15
+ ... (this Thought/Action/Action Input/Observation can repeat N times)
16
+ Thought: I now know the final answer
17
+ Final Answer: the final answer to the original input question"""
18
+
19
+
20
+ SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE = """You are a helpful assistant designed to interact with MCP
21
+ (Model Context Protocol) servers. You can manage connections to different servers and use the tools
22
+ provided by the currently active server.
23
+
24
+ Important: The available tools change depending on which server is active.
25
+ If a request requires tools not listed below (e.g., file operations, web browsing,
26
+ image manipulation), you MUST first connect to the appropriate server using
27
+ 'connect_to_mcp_server'.
28
+ Use 'list_mcp_servers' to find the relevant server if you are unsure.
29
+ Only after successfully connecting and seeing the new tools listed in
30
+ the response should you attempt to use those server-specific tools.
31
+ Before attempting a task that requires specific tools, you should
32
+ ensure you are connected to the correct server and aware of its
33
+ available tools. If unsure, use 'list_mcp_servers' to see options
34
+ or 'get_active_mcp_server' to check the current connection.
35
+
36
+ When you connect to a server using 'connect_to_mcp_server',
37
+ you will be informed about the new tools that become available.
38
+ You can then use these server-specific tools in subsequent steps.
39
+
40
+ Here are the tools *currently* available to you (this list includes server management tools and will
41
+ change when you connect to a server):
42
+ {tool_descriptions}
43
+ """
@@ -0,0 +1,282 @@
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
mcp_use/logging.py CHANGED
@@ -9,6 +9,11 @@ import logging
9
9
  import os
10
10
  import sys
11
11
 
12
+ from langchain.globals import set_debug as langchain_set_debug
13
+
14
+ # Global debug flag - can be set programmatically or from environment
15
+ MCP_USE_DEBUG = False
16
+
12
17
 
13
18
  class Logger:
14
19
  """Centralized logger for mcp_use.
@@ -45,7 +50,7 @@ class Logger:
45
50
  @classmethod
46
51
  def configure(
47
52
  cls,
48
- level: int | str = logging.INFO,
53
+ level: int | str = None,
49
54
  format_str: str | None = None,
50
55
  log_to_console: bool = True,
51
56
  log_to_file: str | None = None,
@@ -53,16 +58,26 @@ class Logger:
53
58
  """Configure the root mcp_use logger.
54
59
 
55
60
  Args:
56
- level: Log level (default: INFO)
61
+ level: Log level (default: DEBUG if MCP_USE_DEBUG is 2,
62
+ INFO if MCP_USE_DEBUG is 1,
63
+ otherwise WARNING)
57
64
  format_str: Log format string (default: DEFAULT_FORMAT)
58
65
  log_to_console: Whether to log to console (default: True)
59
66
  log_to_file: Path to log file (default: None)
60
67
  """
61
68
  root_logger = cls.get_logger()
62
69
 
63
- # Set level
64
- if isinstance(level, str):
70
+ # Set level based on debug settings if not explicitly provided
71
+ if level is None:
72
+ if MCP_USE_DEBUG == 2:
73
+ level = logging.DEBUG
74
+ elif MCP_USE_DEBUG == 1:
75
+ level = logging.INFO
76
+ else:
77
+ level = logging.WARNING
78
+ elif isinstance(level, str):
65
79
  level = getattr(logging, level.upper())
80
+
66
81
  root_logger.setLevel(level)
67
82
 
68
83
  # Clear existing handlers
@@ -89,6 +104,38 @@ class Logger:
89
104
  file_handler.setFormatter(formatter)
90
105
  root_logger.addHandler(file_handler)
91
106
 
107
+ @classmethod
108
+ def set_debug(cls, debug_level: int = 2) -> None:
109
+ """Set the debug flag and update the log level accordingly.
110
+
111
+ Args:
112
+ debug_level: Debug level (0=off, 1=info, 2=debug)
113
+ """
114
+ global MCP_USE_DEBUG
115
+ MCP_USE_DEBUG = debug_level
116
+
117
+ # Update log level for existing loggers
118
+ if debug_level == 2:
119
+ for logger in cls._loggers.values():
120
+ logger.setLevel(logging.DEBUG)
121
+ langchain_set_debug(True)
122
+ elif debug_level == 1:
123
+ for logger in cls._loggers.values():
124
+ logger.setLevel(logging.INFO)
125
+ langchain_set_debug(False)
126
+ else:
127
+ # Reset to default (WARNING)
128
+ for logger in cls._loggers.values():
129
+ logger.setLevel(logging.WARNING)
130
+ langchain_set_debug(False)
131
+
132
+
133
+ # Check environment variable for debug flag
134
+ debug_env = os.environ.get("DEBUG", "").lower()
135
+ if debug_env == "2":
136
+ MCP_USE_DEBUG = 2
137
+ elif debug_env == "1":
138
+ MCP_USE_DEBUG = 1
92
139
 
93
140
  # Configure default logger
94
141
  Logger.configure()