mcp-use 1.1.4__py3-none-any.whl → 1.2.5__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/__init__.py +14 -1
- mcp_use/adapters/__init__.py +5 -0
- mcp_use/adapters/langchain_adapter.py +212 -0
- mcp_use/agents/__init__.py +6 -2
- mcp_use/agents/mcpagent.py +291 -115
- mcp_use/agents/prompts/system_prompt_builder.py +105 -0
- mcp_use/agents/prompts/templates.py +43 -0
- mcp_use/agents/server_manager.py +280 -0
- mcp_use/client.py +3 -3
- mcp_use/connectors/base.py +5 -4
- mcp_use/connectors/http.py +2 -2
- mcp_use/connectors/stdio.py +2 -2
- mcp_use/connectors/websocket.py +6 -6
- mcp_use/logging.py +51 -4
- {mcp_use-1.1.4.dist-info → mcp_use-1.2.5.dist-info}/METADATA +92 -8
- mcp_use-1.2.5.dist-info/RECORD +27 -0
- mcp_use/agents/langchain_agent.py +0 -267
- mcp_use/agents/prompts/default.py +0 -22
- mcp_use-1.1.4.dist-info/RECORD +0 -24
- {mcp_use-1.1.4.dist-info → mcp_use-1.2.5.dist-info}/WHEEL +0 -0
- {mcp_use-1.1.4.dist-info → mcp_use-1.2.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -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,280 @@
|
|
|
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_langchain_tools([connector])
|
|
147
|
+
self._server_tools[server_name] = fetched_tools # Cache tools
|
|
148
|
+
self.initialized_servers[server_name] = True # Mark as initialized
|
|
149
|
+
tools = fetched_tools
|
|
150
|
+
logger.debug(f"Fetched {len(tools)} tools for server '{server_name}'.")
|
|
151
|
+
except Exception as e:
|
|
152
|
+
logger.warning(f"Could not fetch tools for server '{server_name}': {e}")
|
|
153
|
+
# Keep tools as empty list if fetching failed
|
|
154
|
+
|
|
155
|
+
# Append tool names to the result string
|
|
156
|
+
if tools:
|
|
157
|
+
tool_names = ", ".join([tool.name for tool in tools])
|
|
158
|
+
result += f" Tools: {tool_names}\n"
|
|
159
|
+
else:
|
|
160
|
+
result += " Tools: (Could not retrieve or none available)\n"
|
|
161
|
+
|
|
162
|
+
except Exception as e:
|
|
163
|
+
logger.error(f"Unexpected error listing tools for server '{server_name}': {e}")
|
|
164
|
+
result += " Tools: (Error retrieving tools)\n"
|
|
165
|
+
|
|
166
|
+
return result
|
|
167
|
+
|
|
168
|
+
async def connect_to_server(self, server_name: str) -> str:
|
|
169
|
+
"""Connect to a specific MCP server.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
server_name: The name of the server to connect to
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Status message about the connection
|
|
176
|
+
"""
|
|
177
|
+
# Check if server exists
|
|
178
|
+
servers = self.client.get_server_names()
|
|
179
|
+
if server_name not in servers:
|
|
180
|
+
available = ", ".join(servers) if servers else "none"
|
|
181
|
+
return f"Server '{server_name}' not found. Available servers: {available}"
|
|
182
|
+
|
|
183
|
+
# If we're already connected to this server, just return
|
|
184
|
+
if self.active_server == server_name:
|
|
185
|
+
return f"Already connected to MCP server '{server_name}'"
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
# Create or get session for this server
|
|
189
|
+
try:
|
|
190
|
+
session = self.client.get_session(server_name)
|
|
191
|
+
logger.debug(f"Using existing session for server '{server_name}'")
|
|
192
|
+
except ValueError:
|
|
193
|
+
logger.debug(f"Creating new session for server '{server_name}'")
|
|
194
|
+
session = await self.client.create_session(server_name)
|
|
195
|
+
|
|
196
|
+
# Set as active server
|
|
197
|
+
self.active_server = server_name
|
|
198
|
+
|
|
199
|
+
# Initialize server tools if not already initialized
|
|
200
|
+
if server_name not in self._server_tools:
|
|
201
|
+
connector = session.connector
|
|
202
|
+
self._server_tools[server_name] = await self.adapter.create_langchain_tools(
|
|
203
|
+
[connector]
|
|
204
|
+
)
|
|
205
|
+
self.initialized_servers[server_name] = True
|
|
206
|
+
|
|
207
|
+
server_tools = self._server_tools.get(server_name, [])
|
|
208
|
+
num_tools = len(server_tools)
|
|
209
|
+
|
|
210
|
+
tool_descriptions = "\nAvailable tools for this server:\n"
|
|
211
|
+
for i, tool in enumerate(server_tools):
|
|
212
|
+
tool_descriptions += f"{i + 1}. {tool.name}: {tool.description}\n"
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
f"Connected to MCP server '{server_name}'. "
|
|
216
|
+
f"{num_tools} tools are now available."
|
|
217
|
+
f"{tool_descriptions}"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.error(f"Error connecting to server '{server_name}': {e}")
|
|
222
|
+
return f"Failed to connect to server '{server_name}': {str(e)}"
|
|
223
|
+
|
|
224
|
+
async def get_active_server(self) -> str:
|
|
225
|
+
"""Get the currently active MCP server.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Name of the active server or message if none is active
|
|
229
|
+
"""
|
|
230
|
+
if not self.active_server:
|
|
231
|
+
return (
|
|
232
|
+
"No MCP server is currently active. "
|
|
233
|
+
"Use connect_to_mcp_server to connect to a server."
|
|
234
|
+
)
|
|
235
|
+
return f"Currently active MCP server: {self.active_server}"
|
|
236
|
+
|
|
237
|
+
async def disconnect_from_server(self) -> str:
|
|
238
|
+
"""Disconnect from the currently active MCP server.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
Status message about the disconnection
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
if not self.active_server:
|
|
245
|
+
return "No MCP server is currently active, so there's nothing to disconnect from."
|
|
246
|
+
|
|
247
|
+
server_name = self.active_server
|
|
248
|
+
try:
|
|
249
|
+
# Clear the active server
|
|
250
|
+
self.active_server = None
|
|
251
|
+
|
|
252
|
+
# Note: We're not actually closing the session here, just 'deactivating'
|
|
253
|
+
# This way we keep the session cache without requiring reconnection if needed again
|
|
254
|
+
# TODO: consider closing the sessions
|
|
255
|
+
|
|
256
|
+
return f"Successfully disconnected from MCP server '{server_name}'."
|
|
257
|
+
except Exception as e:
|
|
258
|
+
logger.error(f"Error disconnecting from server '{server_name}': {e}")
|
|
259
|
+
return f"Failed to disconnect from server '{server_name}': {str(e)}"
|
|
260
|
+
|
|
261
|
+
async def get_active_server_tools(self) -> list[BaseTool]:
|
|
262
|
+
"""Get the tools for the currently active server.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
List of LangChain tools for the active server or empty list if no active server
|
|
266
|
+
"""
|
|
267
|
+
if not self.active_server:
|
|
268
|
+
return []
|
|
269
|
+
|
|
270
|
+
return self._server_tools.get(self.active_server, [])
|
|
271
|
+
|
|
272
|
+
async def get_all_tools(self) -> list[BaseTool]:
|
|
273
|
+
"""Get all tools - both server management tools and tools for the active server.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Combined list of server management tools and active server tools
|
|
277
|
+
"""
|
|
278
|
+
management_tools = await self.get_server_management_tools()
|
|
279
|
+
active_server_tools = await self.get_active_server_tools()
|
|
280
|
+
return management_tools + active_server_tools
|
mcp_use/client.py
CHANGED
|
@@ -214,7 +214,7 @@ class MCPClient:
|
|
|
214
214
|
|
|
215
215
|
try:
|
|
216
216
|
# Disconnect from the session
|
|
217
|
-
logger.
|
|
217
|
+
logger.debug(f"Closing session for server '{server_name}'")
|
|
218
218
|
await session.disconnect()
|
|
219
219
|
except Exception as e:
|
|
220
220
|
logger.error(f"Error closing session for server '{server_name}': {e}")
|
|
@@ -237,7 +237,7 @@ class MCPClient:
|
|
|
237
237
|
|
|
238
238
|
for server_name in server_names:
|
|
239
239
|
try:
|
|
240
|
-
logger.
|
|
240
|
+
logger.debug(f"Closing session for server '{server_name}'")
|
|
241
241
|
await self.close_session(server_name)
|
|
242
242
|
except Exception as e:
|
|
243
243
|
error_msg = f"Failed to close session for server '{server_name}': {e}"
|
|
@@ -248,4 +248,4 @@ class MCPClient:
|
|
|
248
248
|
if errors:
|
|
249
249
|
logger.error(f"Encountered {len(errors)} errors while closing sessions")
|
|
250
250
|
else:
|
|
251
|
-
logger.
|
|
251
|
+
logger.debug("All sessions closed successfully")
|
mcp_use/connectors/base.py
CHANGED
|
@@ -39,10 +39,10 @@ class BaseConnector(ABC):
|
|
|
39
39
|
logger.debug("Not connected to MCP implementation")
|
|
40
40
|
return
|
|
41
41
|
|
|
42
|
-
logger.
|
|
42
|
+
logger.debug("Disconnecting from MCP implementation")
|
|
43
43
|
await self._cleanup_resources()
|
|
44
44
|
self._connected = False
|
|
45
|
-
logger.
|
|
45
|
+
logger.debug("Disconnected from MCP implementation")
|
|
46
46
|
|
|
47
47
|
async def _cleanup_resources(self) -> None:
|
|
48
48
|
"""Clean up all resources associated with this connector."""
|
|
@@ -83,7 +83,7 @@ class BaseConnector(ABC):
|
|
|
83
83
|
if not self.client:
|
|
84
84
|
raise RuntimeError("MCP client is not connected")
|
|
85
85
|
|
|
86
|
-
logger.
|
|
86
|
+
logger.debug("Initializing MCP session")
|
|
87
87
|
|
|
88
88
|
# Initialize the session
|
|
89
89
|
result = await self.client.initialize()
|
|
@@ -92,7 +92,7 @@ class BaseConnector(ABC):
|
|
|
92
92
|
tools_result = await self.client.list_tools()
|
|
93
93
|
self._tools = tools_result.tools
|
|
94
94
|
|
|
95
|
-
logger.
|
|
95
|
+
logger.debug(f"MCP session initialized with {len(self._tools)} tools")
|
|
96
96
|
|
|
97
97
|
return result
|
|
98
98
|
|
|
@@ -110,6 +110,7 @@ class BaseConnector(ABC):
|
|
|
110
110
|
|
|
111
111
|
logger.debug(f"Calling tool '{name}' with arguments: {arguments}")
|
|
112
112
|
result = await self.client.call_tool(name, arguments)
|
|
113
|
+
logger.debug(f"Tool '{name}' called with result: {result}")
|
|
113
114
|
return result
|
|
114
115
|
|
|
115
116
|
async def list_resources(self) -> list[dict[str, Any]]:
|
mcp_use/connectors/http.py
CHANGED
|
@@ -51,7 +51,7 @@ class HttpConnector(BaseConnector):
|
|
|
51
51
|
logger.debug("Already connected to MCP implementation")
|
|
52
52
|
return
|
|
53
53
|
|
|
54
|
-
logger.
|
|
54
|
+
logger.debug(f"Connecting to MCP implementation via HTTP/SSE: {self.base_url}")
|
|
55
55
|
try:
|
|
56
56
|
# Create the SSE connection URL
|
|
57
57
|
sse_url = f"{self.base_url}"
|
|
@@ -68,7 +68,7 @@ class HttpConnector(BaseConnector):
|
|
|
68
68
|
|
|
69
69
|
# Mark as connected
|
|
70
70
|
self._connected = True
|
|
71
|
-
logger.
|
|
71
|
+
logger.debug(
|
|
72
72
|
f"Successfully connected to MCP implementation via HTTP/SSE: {self.base_url}"
|
|
73
73
|
)
|
|
74
74
|
|
mcp_use/connectors/stdio.py
CHANGED
|
@@ -49,7 +49,7 @@ class StdioConnector(BaseConnector):
|
|
|
49
49
|
logger.debug("Already connected to MCP implementation")
|
|
50
50
|
return
|
|
51
51
|
|
|
52
|
-
logger.
|
|
52
|
+
logger.debug(f"Connecting to MCP implementation: {self.command}")
|
|
53
53
|
try:
|
|
54
54
|
# Create server parameters
|
|
55
55
|
server_params = StdioServerParameters(
|
|
@@ -66,7 +66,7 @@ class StdioConnector(BaseConnector):
|
|
|
66
66
|
|
|
67
67
|
# Mark as connected
|
|
68
68
|
self._connected = True
|
|
69
|
-
logger.
|
|
69
|
+
logger.debug(f"Successfully connected to MCP implementation: {self.command}")
|
|
70
70
|
|
|
71
71
|
except Exception as e:
|
|
72
72
|
logger.error(f"Failed to connect to MCP implementation: {e}")
|
mcp_use/connectors/websocket.py
CHANGED
|
@@ -57,7 +57,7 @@ class WebSocketConnector(BaseConnector):
|
|
|
57
57
|
logger.debug("Already connected to MCP implementation")
|
|
58
58
|
return
|
|
59
59
|
|
|
60
|
-
logger.
|
|
60
|
+
logger.debug(f"Connecting to MCP implementation via WebSocket: {self.url}")
|
|
61
61
|
try:
|
|
62
62
|
# Create and start the connection manager
|
|
63
63
|
self._connection_manager = WebSocketConnectionManager(self.url, self.headers)
|
|
@@ -70,7 +70,7 @@ class WebSocketConnector(BaseConnector):
|
|
|
70
70
|
|
|
71
71
|
# Mark as connected
|
|
72
72
|
self._connected = True
|
|
73
|
-
logger.
|
|
73
|
+
logger.debug(f"Successfully connected to MCP implementation via WebSocket: {self.url}")
|
|
74
74
|
|
|
75
75
|
except Exception as e:
|
|
76
76
|
logger.error(f"Failed to connect to MCP implementation via WebSocket: {e}")
|
|
@@ -117,10 +117,10 @@ class WebSocketConnector(BaseConnector):
|
|
|
117
117
|
logger.debug("Not connected to MCP implementation")
|
|
118
118
|
return
|
|
119
119
|
|
|
120
|
-
logger.
|
|
120
|
+
logger.debug("Disconnecting from MCP implementation")
|
|
121
121
|
await self._cleanup_resources()
|
|
122
122
|
self._connected = False
|
|
123
|
-
logger.
|
|
123
|
+
logger.debug("Disconnected from MCP implementation")
|
|
124
124
|
|
|
125
125
|
async def _cleanup_resources(self) -> None:
|
|
126
126
|
"""Clean up all resources associated with this connector."""
|
|
@@ -199,14 +199,14 @@ class WebSocketConnector(BaseConnector):
|
|
|
199
199
|
|
|
200
200
|
async def initialize(self) -> dict[str, Any]:
|
|
201
201
|
"""Initialize the MCP session and return session information."""
|
|
202
|
-
logger.
|
|
202
|
+
logger.debug("Initializing MCP session")
|
|
203
203
|
result = await self._send_request("initialize")
|
|
204
204
|
|
|
205
205
|
# Get available tools
|
|
206
206
|
tools_result = await self.list_tools()
|
|
207
207
|
self._tools = [Tool(**tool) for tool in tools_result]
|
|
208
208
|
|
|
209
|
-
logger.
|
|
209
|
+
logger.debug(f"MCP session initialized with {len(self._tools)} tools")
|
|
210
210
|
return result
|
|
211
211
|
|
|
212
212
|
async def list_tools(self) -> list[dict[str, Any]]:
|
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 =
|
|
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:
|
|
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
|
|
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()
|