mcp-use 0.0.3__py3-none-any.whl → 0.0.4__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 +1 -1
- mcp_use/agents/langchain_agent.py +46 -6
- mcp_use/agents/mcpagent.py +198 -39
- mcp_use/agents/prompts/default.py +11 -0
- mcp_use/client.py +44 -28
- mcp_use/connectors/http.py +91 -7
- mcp_use/connectors/stdio.py +97 -33
- mcp_use/connectors/websocket.py +124 -21
- mcp_use/task_managers/__init__.py +18 -0
- mcp_use/task_managers/base.py +151 -0
- mcp_use/task_managers/http.py +62 -0
- mcp_use/task_managers/stdio.py +73 -0
- mcp_use/task_managers/websocket.py +63 -0
- {mcp_use-0.0.3.dist-info → mcp_use-0.0.4.dist-info}/METADATA +24 -36
- mcp_use-0.0.4.dist-info/RECORD +24 -0
- {mcp_use-0.0.3.dist-info → mcp_use-0.0.4.dist-info}/WHEEL +1 -2
- mcp_use-0.0.3.dist-info/RECORD +0 -19
- mcp_use-0.0.3.dist-info/top_level.txt +0 -1
- {mcp_use-0.0.3.dist-info → mcp_use-0.0.4.dist-info}/licenses/LICENSE +0 -0
mcp_use/__init__.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
LangChain agent implementation for MCP tools.
|
|
2
|
+
LangChain agent implementation for MCP tools with customizable system message.
|
|
3
3
|
|
|
4
4
|
This module provides a LangChain agent implementation that can use MCP tools
|
|
5
|
-
through a unified interface.
|
|
5
|
+
through a unified interface, with support for customizable system messages.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from typing import Any, NoReturn
|
|
@@ -73,8 +73,15 @@ class LangChainAgent:
|
|
|
73
73
|
through a unified interface.
|
|
74
74
|
"""
|
|
75
75
|
|
|
76
|
+
# Default system message if none is provided
|
|
77
|
+
DEFAULT_SYSTEM_MESSAGE = "You are a helpful AI assistant that can use tools to help users."
|
|
78
|
+
|
|
76
79
|
def __init__(
|
|
77
|
-
self,
|
|
80
|
+
self,
|
|
81
|
+
connector: BaseConnector,
|
|
82
|
+
llm: BaseLanguageModel,
|
|
83
|
+
max_steps: int = 5,
|
|
84
|
+
system_message: str | None = None,
|
|
78
85
|
) -> None:
|
|
79
86
|
"""Initialize a new LangChain agent.
|
|
80
87
|
|
|
@@ -82,13 +89,28 @@ class LangChainAgent:
|
|
|
82
89
|
connector: The MCP connector to use.
|
|
83
90
|
llm: The LangChain LLM to use.
|
|
84
91
|
max_steps: The maximum number of steps to take.
|
|
92
|
+
system_message: Optional custom system message to use.
|
|
85
93
|
"""
|
|
86
94
|
self.connector = connector
|
|
87
95
|
self.llm = llm
|
|
88
96
|
self.max_steps = max_steps
|
|
97
|
+
self.system_message = system_message or self.DEFAULT_SYSTEM_MESSAGE
|
|
89
98
|
self.tools: list[BaseTool] = []
|
|
90
99
|
self.agent: AgentExecutor | None = None
|
|
91
100
|
|
|
101
|
+
def set_system_message(self, message: str) -> None:
|
|
102
|
+
"""Set a new system message and recreate the agent.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
message: The new system message.
|
|
106
|
+
"""
|
|
107
|
+
self.system_message = message
|
|
108
|
+
|
|
109
|
+
# Recreate the agent with the new system message if it exists
|
|
110
|
+
if self.agent and self.tools:
|
|
111
|
+
self.agent = self._create_agent()
|
|
112
|
+
logger.info("Agent recreated with new system message")
|
|
113
|
+
|
|
92
114
|
async def initialize(self) -> None:
|
|
93
115
|
"""Initialize the agent and its tools."""
|
|
94
116
|
self.tools = await self._create_langchain_tools()
|
|
@@ -184,7 +206,7 @@ class LangChainAgent:
|
|
|
184
206
|
return langchain_tools
|
|
185
207
|
|
|
186
208
|
def _create_agent(self) -> AgentExecutor:
|
|
187
|
-
"""Create the LangChain agent.
|
|
209
|
+
"""Create the LangChain agent with the configured system message.
|
|
188
210
|
|
|
189
211
|
Returns:
|
|
190
212
|
An initialized AgentExecutor.
|
|
@@ -193,7 +215,7 @@ class LangChainAgent:
|
|
|
193
215
|
[
|
|
194
216
|
(
|
|
195
217
|
"system",
|
|
196
|
-
|
|
218
|
+
self.system_message,
|
|
197
219
|
),
|
|
198
220
|
MessagesPlaceholder(variable_name="chat_history"),
|
|
199
221
|
("human", "{input}"),
|
|
@@ -234,7 +256,25 @@ class LangChainAgent:
|
|
|
234
256
|
if chat_history is None:
|
|
235
257
|
chat_history = []
|
|
236
258
|
|
|
259
|
+
# Add a hint to use tools for queries about current information
|
|
260
|
+
enhanced_query = query
|
|
261
|
+
if any(
|
|
262
|
+
keyword in query.lower()
|
|
263
|
+
for keyword in [
|
|
264
|
+
"weather",
|
|
265
|
+
"current",
|
|
266
|
+
"today",
|
|
267
|
+
"now",
|
|
268
|
+
"latest",
|
|
269
|
+
"news",
|
|
270
|
+
"price",
|
|
271
|
+
"stock",
|
|
272
|
+
]
|
|
273
|
+
):
|
|
274
|
+
# Just log this, don't modify the query
|
|
275
|
+
logger.info("Query involves current information that may benefit from tool use")
|
|
276
|
+
|
|
237
277
|
# Invoke with all required variables
|
|
238
|
-
result = await self.agent.ainvoke({"input":
|
|
278
|
+
result = await self.agent.ainvoke({"input": enhanced_query, "chat_history": chat_history})
|
|
239
279
|
|
|
240
280
|
return result["output"]
|
mcp_use/agents/mcpagent.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
MCP: Main integration module with customizable system prompt.
|
|
3
3
|
|
|
4
4
|
This module provides the main MCPAgent class that integrates all components
|
|
5
5
|
to provide a simple interface for using MCP tools with different LLMs.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from langchain.schema import AIMessage, BaseMessage, HumanMessage, SystemMessage
|
|
8
9
|
from langchain.schema.language_model import BaseLanguageModel
|
|
9
10
|
|
|
10
11
|
from mcp_use.client import MCPClient
|
|
@@ -13,15 +14,19 @@ from mcp_use.session import MCPSession
|
|
|
13
14
|
|
|
14
15
|
from ..logging import logger
|
|
15
16
|
from .langchain_agent import LangChainAgent
|
|
17
|
+
from .prompts.default import DEFAULT_SYSTEM_PROMPT_TEMPLATE
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
class MCPAgent:
|
|
19
21
|
"""Main class for using MCP tools with various LLM providers.
|
|
20
22
|
|
|
21
23
|
This class provides a unified interface for using MCP tools with different LLM providers
|
|
22
|
-
through LangChain's agent framework.
|
|
24
|
+
through LangChain's agent framework, with customizable system prompts and conversation memory.
|
|
23
25
|
"""
|
|
24
26
|
|
|
27
|
+
# Default system prompt template to use if none is provided
|
|
28
|
+
DEFAULT_SYSTEM_PROMPT_TEMPLATE = DEFAULT_SYSTEM_PROMPT_TEMPLATE
|
|
29
|
+
|
|
25
30
|
def __init__(
|
|
26
31
|
self,
|
|
27
32
|
llm: BaseLanguageModel,
|
|
@@ -30,6 +35,10 @@ class MCPAgent:
|
|
|
30
35
|
server_name: str | None = None,
|
|
31
36
|
max_steps: int = 5,
|
|
32
37
|
auto_initialize: bool = False,
|
|
38
|
+
memory_enabled: bool = True,
|
|
39
|
+
system_prompt: str | None = None,
|
|
40
|
+
system_prompt_template: str | None = None,
|
|
41
|
+
additional_instructions: str | None = None,
|
|
33
42
|
):
|
|
34
43
|
"""Initialize a new MCPAgent instance.
|
|
35
44
|
|
|
@@ -40,6 +49,10 @@ class MCPAgent:
|
|
|
40
49
|
server_name: The name of the server to use if client is provided.
|
|
41
50
|
max_steps: The maximum number of steps to take.
|
|
42
51
|
auto_initialize: Whether to automatically initialize the agent when run is called.
|
|
52
|
+
memory_enabled: Whether to maintain conversation history for context.
|
|
53
|
+
system_prompt: Complete system prompt to use (overrides template if provided).
|
|
54
|
+
system_prompt_template: Template for system prompt with {tool_descriptions} placeholder.
|
|
55
|
+
additional_instructions: Extra instructions to append to the system prompt.
|
|
43
56
|
"""
|
|
44
57
|
self.llm = llm
|
|
45
58
|
self.client = client
|
|
@@ -47,7 +60,14 @@ class MCPAgent:
|
|
|
47
60
|
self.server_name = server_name
|
|
48
61
|
self.max_steps = max_steps
|
|
49
62
|
self.auto_initialize = auto_initialize
|
|
63
|
+
self.memory_enabled = memory_enabled
|
|
50
64
|
self._initialized = False
|
|
65
|
+
self._conversation_history: list[BaseMessage] = []
|
|
66
|
+
|
|
67
|
+
# System prompt configuration
|
|
68
|
+
self.system_prompt = system_prompt
|
|
69
|
+
self.system_prompt_template = system_prompt_template or self.DEFAULT_SYSTEM_PROMPT_TEMPLATE
|
|
70
|
+
self.additional_instructions = additional_instructions
|
|
51
71
|
|
|
52
72
|
# Either client or connector must be provided
|
|
53
73
|
if not client and not connector:
|
|
@@ -55,6 +75,7 @@ class MCPAgent:
|
|
|
55
75
|
|
|
56
76
|
self._agent: LangChainAgent | None = None
|
|
57
77
|
self._session: MCPSession | None = None
|
|
78
|
+
self._system_message: SystemMessage | None = None
|
|
58
79
|
|
|
59
80
|
async def initialize(self) -> None:
|
|
60
81
|
"""Initialize the MCP client and agent."""
|
|
@@ -71,38 +92,104 @@ class MCPAgent:
|
|
|
71
92
|
await connector_to_use.connect()
|
|
72
93
|
await connector_to_use.initialize()
|
|
73
94
|
|
|
95
|
+
# Create the system message based on available tools
|
|
96
|
+
await self._create_system_message(connector_to_use)
|
|
97
|
+
|
|
74
98
|
# Create the agent
|
|
75
99
|
self._agent = LangChainAgent(
|
|
76
|
-
connector=connector_to_use,
|
|
100
|
+
connector=connector_to_use,
|
|
101
|
+
llm=self.llm,
|
|
102
|
+
max_steps=self.max_steps,
|
|
103
|
+
system_message=(self._system_message.content if self._system_message else None),
|
|
77
104
|
)
|
|
78
105
|
|
|
79
106
|
# Initialize the agent
|
|
80
107
|
await self._agent.initialize()
|
|
81
108
|
self._initialized = True
|
|
82
109
|
|
|
83
|
-
async def
|
|
84
|
-
"""
|
|
85
|
-
try:
|
|
86
|
-
if self._agent:
|
|
87
|
-
# Clean up the agent first
|
|
88
|
-
self._agent = None
|
|
110
|
+
async def _create_system_message(self, connector: BaseConnector) -> None:
|
|
111
|
+
"""Create the system message based on available tools.
|
|
89
112
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
113
|
+
Args:
|
|
114
|
+
connector: The connector with available tools.
|
|
115
|
+
"""
|
|
116
|
+
# If a complete system prompt was provided, use it directly
|
|
117
|
+
if self.system_prompt:
|
|
118
|
+
self._system_message = SystemMessage(content=self.system_prompt)
|
|
119
|
+
return
|
|
96
120
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
121
|
+
# Otherwise, build the system prompt from the template and tool descriptions
|
|
122
|
+
tools = connector.tools
|
|
123
|
+
|
|
124
|
+
# Generate tool descriptions
|
|
125
|
+
tool_descriptions = []
|
|
126
|
+
for tool in tools:
|
|
127
|
+
description = f"- {tool.name}: {tool.description}"
|
|
128
|
+
tool_descriptions.append(description)
|
|
129
|
+
|
|
130
|
+
# Format the system prompt template with tool descriptions
|
|
131
|
+
system_prompt = self.system_prompt_template.format(
|
|
132
|
+
tool_descriptions="\n".join(tool_descriptions)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Add any additional instructions
|
|
136
|
+
if self.additional_instructions:
|
|
137
|
+
system_prompt += f"\n\n{self.additional_instructions}"
|
|
138
|
+
|
|
139
|
+
# Create the system message
|
|
140
|
+
self._system_message = SystemMessage(content=system_prompt)
|
|
141
|
+
logger.info(f"Created system message with {len(tools)} tool descriptions")
|
|
142
|
+
|
|
143
|
+
def get_conversation_history(self) -> list[BaseMessage]:
|
|
144
|
+
"""Get the current conversation history.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
The list of conversation messages.
|
|
148
|
+
"""
|
|
149
|
+
return self._conversation_history
|
|
150
|
+
|
|
151
|
+
def clear_conversation_history(self) -> None:
|
|
152
|
+
"""Clear the conversation history."""
|
|
153
|
+
self._conversation_history = []
|
|
154
|
+
|
|
155
|
+
# Re-add the system message if it exists
|
|
156
|
+
if self._system_message:
|
|
157
|
+
self._conversation_history = [self._system_message]
|
|
158
|
+
|
|
159
|
+
def add_to_history(self, message: BaseMessage) -> None:
|
|
160
|
+
"""Add a message to the conversation history.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
message: The message to add.
|
|
164
|
+
"""
|
|
165
|
+
self._conversation_history.append(message)
|
|
166
|
+
|
|
167
|
+
def get_system_message(self) -> SystemMessage | None:
|
|
168
|
+
"""Get the current system message.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
The current system message, or None if not set.
|
|
172
|
+
"""
|
|
173
|
+
return self._system_message
|
|
174
|
+
|
|
175
|
+
def set_system_message(self, message: str) -> None:
|
|
176
|
+
"""Set a new system message.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
message: The new system message content.
|
|
180
|
+
"""
|
|
181
|
+
self._system_message = SystemMessage(content=message)
|
|
182
|
+
|
|
183
|
+
# Update the agent if initialized
|
|
184
|
+
if self._agent:
|
|
185
|
+
self._agent.set_system_message(message)
|
|
103
186
|
|
|
104
187
|
async def run(
|
|
105
|
-
self,
|
|
188
|
+
self,
|
|
189
|
+
query: str,
|
|
190
|
+
max_steps: int | None = None,
|
|
191
|
+
manage_connector: bool = True,
|
|
192
|
+
external_history: list[BaseMessage] | None = None,
|
|
106
193
|
) -> str:
|
|
107
194
|
"""Run a query using the MCP tools.
|
|
108
195
|
|
|
@@ -116,34 +203,106 @@ class MCPAgent:
|
|
|
116
203
|
If True, this method will connect, initialize, and disconnect from
|
|
117
204
|
the connector automatically. If False, the caller is responsible
|
|
118
205
|
for managing the connector lifecycle.
|
|
206
|
+
external_history: Optional external history to use instead of the
|
|
207
|
+
internal conversation history.
|
|
119
208
|
|
|
120
209
|
Returns:
|
|
121
210
|
The result of running the query.
|
|
122
211
|
"""
|
|
123
212
|
result = ""
|
|
213
|
+
initialized_here = False
|
|
214
|
+
|
|
124
215
|
try:
|
|
125
|
-
if
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
216
|
+
# Initialize if needed
|
|
217
|
+
if manage_connector and (not self._initialized or not self._agent):
|
|
218
|
+
logger.info("Initializing agent before running query")
|
|
219
|
+
await self.initialize()
|
|
220
|
+
initialized_here = True
|
|
221
|
+
elif not self._initialized and self.auto_initialize:
|
|
222
|
+
logger.info("Auto-initializing agent before running query")
|
|
223
|
+
await self.initialize()
|
|
224
|
+
initialized_here = True
|
|
225
|
+
|
|
226
|
+
# Check if initialization succeeded
|
|
227
|
+
if not self._agent:
|
|
228
|
+
raise RuntimeError("MCP agent failed to initialize")
|
|
229
|
+
|
|
230
|
+
# Add the user query to conversation history if memory is enabled
|
|
231
|
+
if self.memory_enabled:
|
|
232
|
+
self.add_to_history(HumanMessage(content=query))
|
|
129
233
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
234
|
+
# Use the provided history or the internal history
|
|
235
|
+
history_to_use = (
|
|
236
|
+
external_history if external_history is not None else self._conversation_history
|
|
237
|
+
)
|
|
133
238
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if
|
|
138
|
-
|
|
239
|
+
# Convert messages to format expected by LangChain
|
|
240
|
+
langchain_history = []
|
|
241
|
+
for msg in history_to_use:
|
|
242
|
+
if isinstance(msg, HumanMessage):
|
|
243
|
+
langchain_history.append({"type": "human", "content": msg.content})
|
|
244
|
+
elif isinstance(msg, AIMessage):
|
|
245
|
+
langchain_history.append({"type": "ai", "content": msg.content})
|
|
246
|
+
elif isinstance(msg, SystemMessage) and msg != self._system_message:
|
|
247
|
+
# Include system messages other than the main one
|
|
248
|
+
# which is already included in the agent's prompt
|
|
249
|
+
langchain_history.append({"type": "system", "content": msg.content})
|
|
250
|
+
# Other message types can be handled here if needed
|
|
139
251
|
|
|
140
|
-
|
|
141
|
-
|
|
252
|
+
# Run the query with the specified max_steps or default
|
|
253
|
+
logger.info(f"Running query with max_steps={max_steps or self.max_steps}")
|
|
254
|
+
result = await self._agent.run(
|
|
255
|
+
query=query,
|
|
256
|
+
max_steps=max_steps,
|
|
257
|
+
chat_history=langchain_history,
|
|
258
|
+
)
|
|
142
259
|
|
|
143
|
-
|
|
260
|
+
# Add the response to conversation history if memory is enabled
|
|
261
|
+
if self.memory_enabled:
|
|
262
|
+
self.add_to_history(AIMessage(content=result))
|
|
144
263
|
|
|
145
264
|
return result
|
|
265
|
+
|
|
266
|
+
except Exception as e:
|
|
267
|
+
logger.error(f"Error running query: {e}")
|
|
268
|
+
# If we initialized in this method and there was an error,
|
|
269
|
+
# make sure to clean up
|
|
270
|
+
if initialized_here and manage_connector:
|
|
271
|
+
logger.info("Cleaning up resources after initialization error")
|
|
272
|
+
await self.close()
|
|
273
|
+
raise
|
|
274
|
+
|
|
146
275
|
finally:
|
|
147
|
-
#
|
|
148
|
-
|
|
276
|
+
# Clean up resources if we're managing the connector and
|
|
277
|
+
# we're not using a client that manages sessions
|
|
278
|
+
if manage_connector and not self.client and not initialized_here:
|
|
279
|
+
logger.info("Closing agent after query completion")
|
|
149
280
|
await self.close()
|
|
281
|
+
|
|
282
|
+
async def close(self) -> None:
|
|
283
|
+
"""Close the MCP connection with improved error handling."""
|
|
284
|
+
try:
|
|
285
|
+
if self._agent:
|
|
286
|
+
# Clean up the agent first
|
|
287
|
+
logger.debug("Cleaning up agent")
|
|
288
|
+
self._agent = None
|
|
289
|
+
|
|
290
|
+
# If using client with session, close the session through client
|
|
291
|
+
if self.client and self._session:
|
|
292
|
+
logger.debug("Closing session through client")
|
|
293
|
+
await self.client.close_session(self.server_name)
|
|
294
|
+
self._session = None
|
|
295
|
+
# If using direct connector, disconnect
|
|
296
|
+
elif self.connector:
|
|
297
|
+
logger.debug("Disconnecting connector")
|
|
298
|
+
await self.connector.disconnect()
|
|
299
|
+
|
|
300
|
+
self._initialized = False
|
|
301
|
+
logger.info("Agent closed successfully")
|
|
302
|
+
|
|
303
|
+
except Exception as e:
|
|
304
|
+
logger.error(f"Error during agent closure: {e}")
|
|
305
|
+
# Still try to clean up references even if there was an error
|
|
306
|
+
self._agent = None
|
|
307
|
+
self._session = None
|
|
308
|
+
self._initialized = False
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
DEFAULT_SYSTEM_PROMPT_TEMPLATE = """You are an assistant with access to these tools:
|
|
2
|
+
|
|
3
|
+
{tool_descriptions}
|
|
4
|
+
|
|
5
|
+
Proactively use these tools to:
|
|
6
|
+
- Find real-time information (weather, news, prices)
|
|
7
|
+
- Perform web searches and extract relevant data
|
|
8
|
+
- Execute multi-step tasks by combining tools
|
|
9
|
+
|
|
10
|
+
You CAN access current information using your tools. Never claim you lack access to real-time data.
|
|
11
|
+
"""
|
mcp_use/client.py
CHANGED
|
@@ -9,6 +9,7 @@ import json
|
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
11
|
from .config import create_connector_from_config, load_config_file
|
|
12
|
+
from .logging import logger
|
|
12
13
|
from .session import MCPSession
|
|
13
14
|
|
|
14
15
|
|
|
@@ -183,40 +184,55 @@ class MCPClient:
|
|
|
183
184
|
Raises:
|
|
184
185
|
ValueError: If no active session exists or the specified session doesn't exist.
|
|
185
186
|
"""
|
|
186
|
-
|
|
187
|
-
await session.disconnect()
|
|
188
|
-
|
|
189
|
-
# Remove the session
|
|
187
|
+
# Determine which server to close
|
|
190
188
|
if server_name is None:
|
|
189
|
+
if self.active_session is None:
|
|
190
|
+
logger.warning("No active session to close")
|
|
191
|
+
return
|
|
191
192
|
server_name = self.active_session
|
|
192
193
|
|
|
193
|
-
if
|
|
194
|
+
# Check if the session exists
|
|
195
|
+
if server_name not in self.sessions:
|
|
196
|
+
logger.warning(f"No session exists for server '{server_name}', nothing to close")
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
# Get the session
|
|
200
|
+
session = self.sessions[server_name]
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
# Disconnect from the session
|
|
204
|
+
logger.info(f"Closing session for server '{server_name}'")
|
|
205
|
+
await session.disconnect()
|
|
206
|
+
except Exception as e:
|
|
207
|
+
logger.error(f"Error closing session for server '{server_name}': {e}")
|
|
208
|
+
finally:
|
|
209
|
+
# Remove the session regardless of whether disconnect succeeded
|
|
194
210
|
del self.sessions[server_name]
|
|
195
211
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
212
|
+
# If we closed the active session, set active_session to None
|
|
213
|
+
if server_name == self.active_session:
|
|
214
|
+
self.active_session = None
|
|
199
215
|
|
|
200
216
|
async def close_all_sessions(self) -> None:
|
|
201
|
-
"""Close all active sessions.
|
|
202
|
-
for server_name in list(self.sessions.keys()):
|
|
203
|
-
await self.close_session(server_name)
|
|
204
|
-
|
|
205
|
-
async def __aenter__(self) -> "MCPClient":
|
|
206
|
-
"""Enter the async context manager.
|
|
207
|
-
|
|
208
|
-
Creates a session for the first available server if no sessions exist.
|
|
209
|
-
|
|
210
|
-
Returns:
|
|
211
|
-
The client instance.
|
|
212
|
-
"""
|
|
213
|
-
if not self.sessions and self.config.get("mcpServers"):
|
|
214
|
-
await self.create_session()
|
|
215
|
-
return self
|
|
216
|
-
|
|
217
|
-
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
218
|
-
"""Exit the async context manager.
|
|
217
|
+
"""Close all active sessions.
|
|
219
218
|
|
|
220
|
-
|
|
219
|
+
This method ensures all sessions are closed even if some fail.
|
|
221
220
|
"""
|
|
222
|
-
|
|
221
|
+
# Get a list of all session names first to avoid modification during iteration
|
|
222
|
+
server_names = list(self.sessions.keys())
|
|
223
|
+
errors = []
|
|
224
|
+
|
|
225
|
+
for server_name in server_names:
|
|
226
|
+
try:
|
|
227
|
+
logger.info(f"Closing session for server '{server_name}'")
|
|
228
|
+
await self.close_session(server_name)
|
|
229
|
+
except Exception as e:
|
|
230
|
+
error_msg = f"Failed to close session for server '{server_name}': {e}"
|
|
231
|
+
logger.error(error_msg)
|
|
232
|
+
errors.append(error_msg)
|
|
233
|
+
|
|
234
|
+
# Log summary if there were errors
|
|
235
|
+
if errors:
|
|
236
|
+
logger.error(f"Encountered {len(errors)} errors while closing sessions")
|
|
237
|
+
else:
|
|
238
|
+
logger.info("All sessions closed successfully")
|