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.
- mcp_use/__init__.py +14 -1
- mcp_use/adapters/__init__.py +10 -0
- mcp_use/adapters/base.py +178 -0
- mcp_use/adapters/langchain_adapter.py +161 -0
- mcp_use/agents/__init__.py +6 -2
- mcp_use/agents/mcpagent.py +298 -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 +282 -0
- mcp_use/logging.py +51 -4
- {mcp_use-1.1.5.dist-info → mcp_use-1.2.6.dist-info}/METADATA +128 -7
- {mcp_use-1.1.5.dist-info → mcp_use-1.2.6.dist-info}/RECORD +14 -10
- mcp_use/agents/langchain_agent.py +0 -267
- mcp_use/agents/prompts/default.py +0 -22
- {mcp_use-1.1.5.dist-info → mcp_use-1.2.6.dist-info}/WHEEL +0 -0
- {mcp_use-1.1.5.dist-info → mcp_use-1.2.6.dist-info}/licenses/LICENSE +0 -0
mcp_use/agents/mcpagent.py
CHANGED
|
@@ -5,16 +5,29 @@ 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
|
+
import logging
|
|
9
|
+
|
|
10
|
+
from langchain.agents import AgentExecutor, create_tool_calling_agent
|
|
11
|
+
from langchain.globals import set_debug
|
|
12
|
+
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
|
|
8
13
|
from langchain.schema import AIMessage, BaseMessage, HumanMessage, SystemMessage
|
|
9
14
|
from langchain.schema.language_model import BaseLanguageModel
|
|
15
|
+
from langchain_core.agents import AgentAction, AgentFinish
|
|
16
|
+
from langchain_core.exceptions import OutputParserException
|
|
17
|
+
from langchain_core.tools import BaseTool
|
|
18
|
+
from langchain_core.utils.input import get_color_mapping
|
|
10
19
|
|
|
11
20
|
from mcp_use.client import MCPClient
|
|
12
21
|
from mcp_use.connectors.base import BaseConnector
|
|
13
22
|
from mcp_use.session import MCPSession
|
|
14
23
|
|
|
24
|
+
from ..adapters.langchain_adapter import LangChainAdapter
|
|
15
25
|
from ..logging import logger
|
|
16
|
-
from .
|
|
17
|
-
from .prompts.
|
|
26
|
+
from .prompts.system_prompt_builder import create_system_message
|
|
27
|
+
from .prompts.templates import DEFAULT_SYSTEM_PROMPT_TEMPLATE, SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE
|
|
28
|
+
from .server_manager import ServerManager
|
|
29
|
+
|
|
30
|
+
set_debug(logger.level == logging.DEBUG)
|
|
18
31
|
|
|
19
32
|
|
|
20
33
|
class MCPAgent:
|
|
@@ -24,9 +37,6 @@ class MCPAgent:
|
|
|
24
37
|
through LangChain's agent framework, with customizable system prompts and conversation memory.
|
|
25
38
|
"""
|
|
26
39
|
|
|
27
|
-
# Default system prompt template to use if none is provided
|
|
28
|
-
DEFAULT_SYSTEM_PROMPT_TEMPLATE = DEFAULT_SYSTEM_PROMPT_TEMPLATE
|
|
29
|
-
|
|
30
40
|
def __init__(
|
|
31
41
|
self,
|
|
32
42
|
llm: BaseLanguageModel,
|
|
@@ -37,9 +47,11 @@ class MCPAgent:
|
|
|
37
47
|
auto_initialize: bool = False,
|
|
38
48
|
memory_enabled: bool = True,
|
|
39
49
|
system_prompt: str | None = None,
|
|
40
|
-
system_prompt_template: str | None = None,
|
|
50
|
+
system_prompt_template: str | None = None, # User can still override the template
|
|
41
51
|
additional_instructions: str | None = None,
|
|
42
52
|
disallowed_tools: list[str] | None = None,
|
|
53
|
+
use_server_manager: bool = False,
|
|
54
|
+
verbose: bool = False,
|
|
43
55
|
):
|
|
44
56
|
"""Initialize a new MCPAgent instance.
|
|
45
57
|
|
|
@@ -55,10 +67,11 @@ class MCPAgent:
|
|
|
55
67
|
system_prompt_template: Template for system prompt with {tool_descriptions} placeholder.
|
|
56
68
|
additional_instructions: Extra instructions to append to the system prompt.
|
|
57
69
|
disallowed_tools: List of tool names that should not be available to the agent.
|
|
70
|
+
use_server_manager: Whether to use server manager mode instead of exposing all tools.
|
|
58
71
|
"""
|
|
59
72
|
self.llm = llm
|
|
60
73
|
self.client = client
|
|
61
|
-
self.connectors = connectors
|
|
74
|
+
self.connectors = connectors or []
|
|
62
75
|
self.server_name = server_name
|
|
63
76
|
self.max_steps = max_steps
|
|
64
77
|
self.auto_initialize = auto_initialize
|
|
@@ -66,91 +79,148 @@ class MCPAgent:
|
|
|
66
79
|
self._initialized = False
|
|
67
80
|
self._conversation_history: list[BaseMessage] = []
|
|
68
81
|
self.disallowed_tools = disallowed_tools or []
|
|
69
|
-
|
|
82
|
+
self.use_server_manager = use_server_manager
|
|
83
|
+
self.verbose = verbose
|
|
70
84
|
# System prompt configuration
|
|
71
|
-
self.system_prompt = system_prompt
|
|
72
|
-
|
|
85
|
+
self.system_prompt = system_prompt # User-provided full prompt override
|
|
86
|
+
# User can provide a template override, otherwise use the imported default
|
|
87
|
+
self.system_prompt_template_override = system_prompt_template
|
|
73
88
|
self.additional_instructions = additional_instructions
|
|
74
89
|
|
|
75
90
|
# Either client or connector must be provided
|
|
76
|
-
if not client and len(connectors) == 0:
|
|
91
|
+
if not client and len(self.connectors) == 0:
|
|
77
92
|
raise ValueError("Either client or connector must be provided")
|
|
78
93
|
|
|
79
|
-
|
|
80
|
-
self.
|
|
94
|
+
# Create the adapter for tool conversion
|
|
95
|
+
self.adapter = LangChainAdapter(disallowed_tools=self.disallowed_tools)
|
|
96
|
+
|
|
97
|
+
# Initialize server manager if requested
|
|
98
|
+
self.server_manager = None
|
|
99
|
+
if self.use_server_manager:
|
|
100
|
+
if not self.client:
|
|
101
|
+
raise ValueError("Client must be provided when using server manager")
|
|
102
|
+
self.server_manager = ServerManager(self.client, self.adapter)
|
|
103
|
+
|
|
104
|
+
# State tracking
|
|
105
|
+
self._agent_executor: AgentExecutor | None = None
|
|
106
|
+
self._sessions: dict[str, MCPSession] = {}
|
|
81
107
|
self._system_message: SystemMessage | None = None
|
|
108
|
+
self._tools: list[BaseTool] = []
|
|
82
109
|
|
|
83
110
|
async def initialize(self) -> None:
|
|
84
111
|
"""Initialize the MCP client and agent."""
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
112
|
+
logger.info("🚀 Initializing MCP agent and connecting to services...")
|
|
113
|
+
# If using server manager, initialize it
|
|
114
|
+
if self.use_server_manager and self.server_manager:
|
|
115
|
+
await self.server_manager.initialize()
|
|
116
|
+
# Get server management tools
|
|
117
|
+
management_tools = await self.server_manager.get_server_management_tools()
|
|
118
|
+
self._tools = management_tools
|
|
119
|
+
logger.info(
|
|
120
|
+
f"🔧 Server manager mode active with {len(management_tools)} management tools"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Create the system message based on available tools
|
|
124
|
+
await self._create_system_message_from_tools(self._tools)
|
|
94
125
|
else:
|
|
95
|
-
#
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
126
|
+
# Standard initialization - if using client, get or create sessions
|
|
127
|
+
if self.client:
|
|
128
|
+
# First try to get existing sessions
|
|
129
|
+
self._sessions = self.client.get_all_active_sessions()
|
|
130
|
+
logger.info(f"🔌 Found {len(self._sessions)} existing sessions")
|
|
131
|
+
|
|
132
|
+
# If no active sessions exist, create new ones
|
|
133
|
+
if not self._sessions:
|
|
134
|
+
logger.info("🔄 No active sessions found, creating new ones...")
|
|
135
|
+
self._sessions = await self.client.create_all_sessions()
|
|
136
|
+
logger.info(f"✅ Created {len(self._sessions)} new sessions")
|
|
137
|
+
|
|
138
|
+
# Create LangChain tools directly from the client using the adapter
|
|
139
|
+
self._tools = await self.adapter.create_tools(self.client)
|
|
140
|
+
logger.info(f"🛠️ Created {len(self._tools)} LangChain tools from client")
|
|
141
|
+
else:
|
|
142
|
+
# Using direct connector - only establish connection
|
|
143
|
+
# LangChainAdapter will handle initialization
|
|
144
|
+
connectors_to_use = self.connectors
|
|
145
|
+
logger.info(f"🔗 Connecting to {len(connectors_to_use)} direct connectors...")
|
|
146
|
+
for connector in connectors_to_use:
|
|
147
|
+
if not hasattr(connector, "client") or connector.client is None:
|
|
148
|
+
await connector.connect()
|
|
149
|
+
|
|
150
|
+
# Create LangChain tools using the adapter with connectors
|
|
151
|
+
self._tools = await self.adapter._create_tools_from_connectors(connectors_to_use)
|
|
152
|
+
logger.info(f"🛠️ Created {len(self._tools)} LangChain tools from connectors")
|
|
153
|
+
|
|
154
|
+
# Get all tools for system message generation
|
|
155
|
+
all_tools = self._tools
|
|
156
|
+
logger.info(f"🧰 Found {len(all_tools)} tools across all connectors")
|
|
157
|
+
|
|
158
|
+
# Create the system message based on available tools
|
|
159
|
+
await self._create_system_message_from_tools(all_tools)
|
|
101
160
|
|
|
102
161
|
# Create the agent
|
|
103
|
-
self.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
162
|
+
self._agent_executor = self._create_agent()
|
|
163
|
+
self._initialized = True
|
|
164
|
+
logger.info("✨ Agent initialization complete")
|
|
165
|
+
|
|
166
|
+
async def _create_system_message_from_tools(self, tools: list[BaseTool]) -> None:
|
|
167
|
+
"""Create the system message based on provided tools using the builder."""
|
|
168
|
+
# Use the override if provided, otherwise use the imported default
|
|
169
|
+
default_template = self.system_prompt_template_override or DEFAULT_SYSTEM_PROMPT_TEMPLATE
|
|
170
|
+
# Server manager template is now also imported
|
|
171
|
+
server_template = SERVER_MANAGER_SYSTEM_PROMPT_TEMPLATE
|
|
172
|
+
|
|
173
|
+
# Delegate creation to the imported function
|
|
174
|
+
self._system_message = create_system_message(
|
|
175
|
+
tools=tools,
|
|
176
|
+
system_prompt_template=default_template,
|
|
177
|
+
server_manager_template=server_template, # Pass the imported template
|
|
178
|
+
use_server_manager=self.use_server_manager,
|
|
108
179
|
disallowed_tools=self.disallowed_tools,
|
|
180
|
+
user_provided_prompt=self.system_prompt,
|
|
181
|
+
additional_instructions=self.additional_instructions,
|
|
109
182
|
)
|
|
110
183
|
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
184
|
+
# Update conversation history if memory is enabled
|
|
185
|
+
if self.memory_enabled:
|
|
186
|
+
history_without_system = [
|
|
187
|
+
msg for msg in self._conversation_history if not isinstance(msg, SystemMessage)
|
|
188
|
+
]
|
|
189
|
+
self._conversation_history = [self._system_message] + history_without_system
|
|
114
190
|
|
|
115
|
-
|
|
116
|
-
"""Create the
|
|
191
|
+
def _create_agent(self) -> AgentExecutor:
|
|
192
|
+
"""Create the LangChain agent with the configured system message.
|
|
117
193
|
|
|
118
|
-
|
|
119
|
-
|
|
194
|
+
Returns:
|
|
195
|
+
An initialized AgentExecutor.
|
|
120
196
|
"""
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
continue
|
|
135
|
-
|
|
136
|
-
# Escape curly braces in the description by doubling them
|
|
137
|
-
# (sometimes e.g. blender mcp they are used in the description)
|
|
138
|
-
description = (
|
|
139
|
-
f"- {tool.name}: {tool.description.replace('{', '{{').replace('}', '}}')}"
|
|
140
|
-
)
|
|
141
|
-
tool_descriptions.append(description)
|
|
142
|
-
|
|
143
|
-
# Format the system prompt template with tool descriptions
|
|
144
|
-
system_prompt = self.system_prompt_template.format(
|
|
145
|
-
tool_descriptions="\n".join(tool_descriptions)
|
|
197
|
+
logger.debug(f"Creating new agent with {len(self._tools)} tools")
|
|
198
|
+
|
|
199
|
+
system_content = "You are a helpful assistant"
|
|
200
|
+
if self._system_message:
|
|
201
|
+
system_content = self._system_message.content
|
|
202
|
+
|
|
203
|
+
prompt = ChatPromptTemplate.from_messages(
|
|
204
|
+
[
|
|
205
|
+
("system", system_content),
|
|
206
|
+
MessagesPlaceholder(variable_name="chat_history"),
|
|
207
|
+
("human", "{input}"),
|
|
208
|
+
MessagesPlaceholder(variable_name="agent_scratchpad"),
|
|
209
|
+
]
|
|
146
210
|
)
|
|
147
211
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
212
|
+
tool_names = [tool.name for tool in self._tools]
|
|
213
|
+
logger.info(f"🧠 Agent ready with tools: {', '.join(tool_names)}")
|
|
214
|
+
|
|
215
|
+
# Use the standard create_tool_calling_agent
|
|
216
|
+
agent = create_tool_calling_agent(llm=self.llm, tools=self._tools, prompt=prompt)
|
|
151
217
|
|
|
152
|
-
#
|
|
153
|
-
|
|
218
|
+
# Use the standard AgentExecutor
|
|
219
|
+
executor = AgentExecutor(
|
|
220
|
+
agent=agent, tools=self._tools, max_iterations=self.max_steps, verbose=self.verbose
|
|
221
|
+
)
|
|
222
|
+
logger.debug(f"Created agent executor with max_iterations={self.max_steps}")
|
|
223
|
+
return executor
|
|
154
224
|
|
|
155
225
|
def get_conversation_history(self) -> list[BaseMessage]:
|
|
156
226
|
"""Get the current conversation history.
|
|
@@ -165,7 +235,7 @@ class MCPAgent:
|
|
|
165
235
|
self._conversation_history = []
|
|
166
236
|
|
|
167
237
|
# Re-add the system message if it exists
|
|
168
|
-
if self._system_message:
|
|
238
|
+
if self._system_message and self.memory_enabled:
|
|
169
239
|
self._conversation_history = [self._system_message]
|
|
170
240
|
|
|
171
241
|
def add_to_history(self, message: BaseMessage) -> None:
|
|
@@ -174,7 +244,8 @@ class MCPAgent:
|
|
|
174
244
|
Args:
|
|
175
245
|
message: The message to add.
|
|
176
246
|
"""
|
|
177
|
-
self.
|
|
247
|
+
if self.memory_enabled:
|
|
248
|
+
self._conversation_history.append(message)
|
|
178
249
|
|
|
179
250
|
def get_system_message(self) -> SystemMessage | None:
|
|
180
251
|
"""Get the current system message.
|
|
@@ -192,9 +263,21 @@ class MCPAgent:
|
|
|
192
263
|
"""
|
|
193
264
|
self._system_message = SystemMessage(content=message)
|
|
194
265
|
|
|
195
|
-
# Update
|
|
196
|
-
if self.
|
|
197
|
-
|
|
266
|
+
# Update conversation history if memory is enabled
|
|
267
|
+
if self.memory_enabled:
|
|
268
|
+
# Remove old system message if it exists
|
|
269
|
+
history_without_system = [
|
|
270
|
+
msg for msg in self._conversation_history if not isinstance(msg, SystemMessage)
|
|
271
|
+
]
|
|
272
|
+
self._conversation_history = history_without_system
|
|
273
|
+
|
|
274
|
+
# Add new system message
|
|
275
|
+
self._conversation_history.insert(0, self._system_message)
|
|
276
|
+
|
|
277
|
+
# Recreate the agent with the new system message if initialized
|
|
278
|
+
if self._initialized and self._tools:
|
|
279
|
+
self._agent_executor = self._create_agent()
|
|
280
|
+
logger.debug("Agent recreated with new system message")
|
|
198
281
|
|
|
199
282
|
def set_disallowed_tools(self, disallowed_tools: list[str]) -> None:
|
|
200
283
|
"""Set the list of tools that should not be available to the agent.
|
|
@@ -205,6 +288,7 @@ class MCPAgent:
|
|
|
205
288
|
disallowed_tools: List of tool names that should not be available.
|
|
206
289
|
"""
|
|
207
290
|
self.disallowed_tools = disallowed_tools
|
|
291
|
+
self.adapter.disallowed_tools = disallowed_tools
|
|
208
292
|
|
|
209
293
|
# If the agent is already initialized, we need to reinitialize it
|
|
210
294
|
# to apply the changes to the available tools
|
|
@@ -230,7 +314,7 @@ class MCPAgent:
|
|
|
230
314
|
manage_connector: bool = True,
|
|
231
315
|
external_history: list[BaseMessage] | None = None,
|
|
232
316
|
) -> str:
|
|
233
|
-
"""Run a query using the MCP tools.
|
|
317
|
+
"""Run a query using the MCP tools with unified step-by-step execution.
|
|
234
318
|
|
|
235
319
|
This method handles connecting to the MCP server, initializing the agent,
|
|
236
320
|
running the query, and then cleaning up the connection.
|
|
@@ -253,19 +337,28 @@ class MCPAgent:
|
|
|
253
337
|
|
|
254
338
|
try:
|
|
255
339
|
# Initialize if needed
|
|
256
|
-
if manage_connector and
|
|
257
|
-
logger.debug("Initializing agent before running query")
|
|
340
|
+
if manage_connector and not self._initialized:
|
|
258
341
|
await self.initialize()
|
|
259
342
|
initialized_here = True
|
|
260
343
|
elif not self._initialized and self.auto_initialize:
|
|
261
|
-
logger.debug("Auto-initializing agent before running query")
|
|
262
344
|
await self.initialize()
|
|
263
345
|
initialized_here = True
|
|
264
346
|
|
|
265
347
|
# Check if initialization succeeded
|
|
266
|
-
if not self.
|
|
348
|
+
if not self._agent_executor:
|
|
267
349
|
raise RuntimeError("MCP agent failed to initialize")
|
|
268
350
|
|
|
351
|
+
steps = max_steps or self.max_steps
|
|
352
|
+
if self._agent_executor:
|
|
353
|
+
self._agent_executor.max_iterations = steps
|
|
354
|
+
|
|
355
|
+
display_query = (
|
|
356
|
+
query[:50].replace("\n", " ") + "..."
|
|
357
|
+
if len(query) > 50
|
|
358
|
+
else query.replace("\n", " ")
|
|
359
|
+
)
|
|
360
|
+
logger.info(f"💬 Received query: '{display_query}'")
|
|
361
|
+
|
|
269
362
|
# Add the user query to conversation history if memory is enabled
|
|
270
363
|
if self.memory_enabled:
|
|
271
364
|
self.add_to_history(HumanMessage(content=query))
|
|
@@ -275,74 +368,164 @@ class MCPAgent:
|
|
|
275
368
|
external_history if external_history is not None else self._conversation_history
|
|
276
369
|
)
|
|
277
370
|
|
|
278
|
-
# Convert messages to format expected by LangChain
|
|
371
|
+
# Convert messages to format expected by LangChain agent input
|
|
372
|
+
# Exclude the main system message as it's part of the agent's prompt
|
|
279
373
|
langchain_history = []
|
|
280
374
|
for msg in history_to_use:
|
|
281
375
|
if isinstance(msg, HumanMessage):
|
|
282
|
-
langchain_history.append(
|
|
376
|
+
langchain_history.append(msg)
|
|
283
377
|
elif isinstance(msg, AIMessage):
|
|
284
|
-
langchain_history.append(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
result = await self._agent.run(
|
|
294
|
-
query=query,
|
|
295
|
-
max_steps=max_steps,
|
|
296
|
-
chat_history=langchain_history,
|
|
378
|
+
langchain_history.append(msg)
|
|
379
|
+
|
|
380
|
+
intermediate_steps: list[tuple[AgentAction, str]] = []
|
|
381
|
+
inputs = {"input": query, "chat_history": langchain_history}
|
|
382
|
+
|
|
383
|
+
# Construct a mapping of tool name to tool for easy lookup
|
|
384
|
+
name_to_tool_map = {tool.name: tool for tool in self._tools}
|
|
385
|
+
color_mapping = get_color_mapping(
|
|
386
|
+
[tool.name for tool in self._tools], excluded_colors=["green", "red"]
|
|
297
387
|
)
|
|
298
388
|
|
|
299
|
-
|
|
389
|
+
logger.info(f"🏁 Starting agent execution with max_steps={steps}")
|
|
390
|
+
|
|
391
|
+
for step_num in range(steps):
|
|
392
|
+
# --- Check for tool updates if using server manager ---
|
|
393
|
+
if self.use_server_manager and self.server_manager:
|
|
394
|
+
current_tools = await self.server_manager.get_all_tools()
|
|
395
|
+
current_tool_names = {tool.name for tool in current_tools}
|
|
396
|
+
existing_tool_names = {tool.name for tool in self._tools}
|
|
397
|
+
|
|
398
|
+
if current_tool_names != existing_tool_names:
|
|
399
|
+
logger.info(
|
|
400
|
+
f"🔄 Tools changed before step {step_num + 1}, updating agent. "
|
|
401
|
+
f"New tools: {', '.join(current_tool_names)}"
|
|
402
|
+
)
|
|
403
|
+
self._tools = current_tools
|
|
404
|
+
# Regenerate system message with ALL current tools
|
|
405
|
+
await self._create_system_message_from_tools(self._tools)
|
|
406
|
+
# Recreate the agent executor with the new tools and system message
|
|
407
|
+
self._agent_executor = self._create_agent()
|
|
408
|
+
self._agent_executor.max_iterations = steps
|
|
409
|
+
# Update maps for this iteration
|
|
410
|
+
name_to_tool_map = {tool.name: tool for tool in self._tools}
|
|
411
|
+
color_mapping = get_color_mapping(
|
|
412
|
+
[tool.name for tool in self._tools], excluded_colors=["green", "red"]
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
logger.info(f"🔍 Step {step_num + 1}/{steps}")
|
|
416
|
+
|
|
417
|
+
# --- Plan and execute the next step ---
|
|
418
|
+
try:
|
|
419
|
+
# Use the internal _atake_next_step which handles planning and execution
|
|
420
|
+
# This requires providing the necessary context like maps and intermediate steps
|
|
421
|
+
next_step_output = await self._agent_executor._atake_next_step(
|
|
422
|
+
name_to_tool_map=name_to_tool_map,
|
|
423
|
+
color_mapping=color_mapping,
|
|
424
|
+
inputs=inputs,
|
|
425
|
+
intermediate_steps=intermediate_steps,
|
|
426
|
+
run_manager=None,
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
# Process the output
|
|
430
|
+
if isinstance(next_step_output, AgentFinish):
|
|
431
|
+
logger.info(f"✅ Agent finished at step {step_num + 1}")
|
|
432
|
+
result = next_step_output.return_values.get("output", "No output generated")
|
|
433
|
+
break
|
|
434
|
+
|
|
435
|
+
# If it's actions/steps, add to intermediate steps
|
|
436
|
+
intermediate_steps.extend(next_step_output)
|
|
437
|
+
|
|
438
|
+
# Log tool calls
|
|
439
|
+
for action, output in next_step_output:
|
|
440
|
+
tool_name = action.tool
|
|
441
|
+
tool_input_str = str(action.tool_input)
|
|
442
|
+
# Truncate long inputs for readability
|
|
443
|
+
if len(tool_input_str) > 100:
|
|
444
|
+
tool_input_str = tool_input_str[:97] + "..."
|
|
445
|
+
logger.info(f"🔧 Tool call: {tool_name} with input: {tool_input_str}")
|
|
446
|
+
# Truncate long outputs for readability
|
|
447
|
+
output_str = str(output)
|
|
448
|
+
if len(output_str) > 100:
|
|
449
|
+
output_str = output_str[:97] + "..."
|
|
450
|
+
output_str = output_str.replace("\n", " ")
|
|
451
|
+
logger.info(f"📄 Tool result: {output_str}")
|
|
452
|
+
|
|
453
|
+
# Check for return_direct on the last action taken
|
|
454
|
+
if len(next_step_output) > 0:
|
|
455
|
+
last_step: tuple[AgentAction, str] = next_step_output[-1]
|
|
456
|
+
tool_return = self._agent_executor._get_tool_return(last_step)
|
|
457
|
+
if tool_return is not None:
|
|
458
|
+
logger.info(f"🏆 Tool returned directly at step {step_num + 1}")
|
|
459
|
+
result = tool_return.return_values.get("output", "No output generated")
|
|
460
|
+
break
|
|
461
|
+
|
|
462
|
+
except OutputParserException as e:
|
|
463
|
+
logger.error(f"❌ Output parsing error during step {step_num + 1}: {e}")
|
|
464
|
+
result = f"Agent stopped due to a parsing error: {str(e)}"
|
|
465
|
+
break
|
|
466
|
+
except Exception as e:
|
|
467
|
+
logger.error(f"❌ Error during agent execution step {step_num + 1}: {e}")
|
|
468
|
+
import traceback
|
|
469
|
+
|
|
470
|
+
traceback.print_exc()
|
|
471
|
+
result = f"Agent stopped due to an error: {str(e)}"
|
|
472
|
+
break
|
|
473
|
+
|
|
474
|
+
# --- Loop finished ---
|
|
475
|
+
if not result:
|
|
476
|
+
logger.warning(f"⚠️ Agent stopped after reaching max iterations ({steps})")
|
|
477
|
+
result = f"Agent stopped after reaching the maximum number of steps ({steps})."
|
|
478
|
+
|
|
479
|
+
# Add the final response to conversation history if memory is enabled
|
|
300
480
|
if self.memory_enabled:
|
|
301
481
|
self.add_to_history(AIMessage(content=result))
|
|
302
482
|
|
|
483
|
+
logger.info("🎉 Agent execution complete")
|
|
303
484
|
return result
|
|
304
485
|
|
|
305
486
|
except Exception as e:
|
|
306
|
-
logger.error(f"Error running query: {e}")
|
|
307
|
-
# If we initialized in this method and there was an error,
|
|
308
|
-
# make sure to clean up
|
|
487
|
+
logger.error(f"❌ Error running query: {e}")
|
|
309
488
|
if initialized_here and manage_connector:
|
|
310
|
-
logger.
|
|
489
|
+
logger.info("🧹 Cleaning up resources after initialization error in run")
|
|
311
490
|
await self.close()
|
|
312
491
|
raise
|
|
313
492
|
|
|
314
493
|
finally:
|
|
315
|
-
# Clean up
|
|
316
|
-
# we're not using a client that manages sessions
|
|
494
|
+
# Clean up if necessary (e.g., if not using client-managed sessions)
|
|
317
495
|
if manage_connector and not self.client and not initialized_here:
|
|
318
|
-
logger.
|
|
496
|
+
logger.info("🧹 Closing agent after query completion")
|
|
319
497
|
await self.close()
|
|
320
498
|
|
|
321
499
|
async def close(self) -> None:
|
|
322
500
|
"""Close the MCP connection with improved error handling."""
|
|
501
|
+
logger.info("🔌 Closing agent and cleaning up resources...")
|
|
323
502
|
try:
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
self._agent = None
|
|
503
|
+
# Clean up the agent first
|
|
504
|
+
self._agent_executor = None
|
|
505
|
+
self._tools = []
|
|
328
506
|
|
|
329
507
|
# If using client with session, close the session through client
|
|
330
|
-
if self.client
|
|
331
|
-
logger.
|
|
508
|
+
if self.client:
|
|
509
|
+
logger.info("🔄 Closing sessions through client")
|
|
332
510
|
await self.client.close_all_sessions()
|
|
333
|
-
self.
|
|
511
|
+
self._sessions = {}
|
|
334
512
|
# If using direct connector, disconnect
|
|
335
513
|
elif self.connectors:
|
|
336
514
|
for connector in self.connectors:
|
|
337
|
-
logger.
|
|
515
|
+
logger.info("🔄 Disconnecting connector")
|
|
338
516
|
await connector.disconnect()
|
|
339
517
|
|
|
518
|
+
# Clear adapter tool cache
|
|
519
|
+
if hasattr(self.adapter, "_connector_tool_map"):
|
|
520
|
+
self.adapter._connector_tool_map = {}
|
|
521
|
+
|
|
340
522
|
self._initialized = False
|
|
341
|
-
logger.
|
|
523
|
+
logger.info("👋 Agent closed successfully")
|
|
342
524
|
|
|
343
525
|
except Exception as e:
|
|
344
|
-
logger.error(f"Error during agent closure: {e}")
|
|
526
|
+
logger.error(f"❌ Error during agent closure: {e}")
|
|
345
527
|
# Still try to clean up references even if there was an error
|
|
346
|
-
self.
|
|
347
|
-
self.
|
|
528
|
+
self._agent_executor = None
|
|
529
|
+
self._tools = []
|
|
530
|
+
self._sessions = {}
|
|
348
531
|
self._initialized = False
|