mcp-use 0.1.0__py3-none-any.whl → 1.0.1__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 +6 -6
- mcp_use/agents/langchain_agent.py +88 -64
- mcp_use/agents/mcpagent.py +216 -52
- mcp_use/agents/prompts/default.py +22 -0
- mcp_use/client.py +93 -68
- mcp_use/config.py +3 -57
- mcp_use/connectors/base.py +94 -16
- mcp_use/connectors/http.py +51 -95
- mcp_use/connectors/stdio.py +38 -84
- mcp_use/connectors/websocket.py +124 -21
- mcp_use/session.py +0 -55
- mcp_use/task_managers/__init__.py +19 -0
- mcp_use/task_managers/base.py +151 -0
- mcp_use/task_managers/sse.py +82 -0
- mcp_use/task_managers/stdio.py +73 -0
- mcp_use/task_managers/websocket.py +63 -0
- mcp_use-1.0.1.dist-info/METADATA +383 -0
- mcp_use-1.0.1.dist-info/RECORD +24 -0
- {mcp_use-0.1.0.dist-info → mcp_use-1.0.1.dist-info}/WHEEL +1 -2
- mcp_use/tools/__init__.py +0 -11
- mcp_use/tools/converter.py +0 -108
- mcp_use/tools/formats.py +0 -181
- mcp_use/types.py +0 -33
- mcp_use-0.1.0.dist-info/METADATA +0 -287
- mcp_use-0.1.0.dist-info/RECORD +0 -23
- mcp_use-0.1.0.dist-info/top_level.txt +0 -1
- {mcp_use-0.1.0.dist-info → mcp_use-1.0.1.dist-info}/licenses/LICENSE +0 -0
mcp_use/__init__.py
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
"""
|
|
2
|
-
mcp_use -
|
|
2
|
+
mcp_use - An MCP library for LLMs.
|
|
3
3
|
|
|
4
4
|
This library provides a unified interface for connecting different LLMs
|
|
5
5
|
to MCP tools through existing LangChain adapters.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from importlib.metadata import version
|
|
9
|
+
|
|
8
10
|
from .agents.mcpagent import MCPAgent
|
|
9
11
|
from .client import MCPClient
|
|
10
|
-
from .config import
|
|
12
|
+
from .config import load_config_file
|
|
11
13
|
from .connectors import BaseConnector, HttpConnector, StdioConnector, WebSocketConnector
|
|
12
14
|
from .logging import logger
|
|
13
15
|
from .session import MCPSession
|
|
14
|
-
from .tools.converter import ModelProvider, ToolConverter
|
|
15
16
|
|
|
16
|
-
__version__ = "
|
|
17
|
+
__version__ = version("mcp-use")
|
|
18
|
+
|
|
17
19
|
__all__ = [
|
|
18
20
|
"MCPAgent",
|
|
19
21
|
"MCPClient",
|
|
@@ -22,8 +24,6 @@ __all__ = [
|
|
|
22
24
|
"StdioConnector",
|
|
23
25
|
"WebSocketConnector",
|
|
24
26
|
"HttpConnector",
|
|
25
|
-
"ModelProvider",
|
|
26
|
-
"ToolConverter",
|
|
27
27
|
"create_session_from_config",
|
|
28
28
|
"load_config_file",
|
|
29
29
|
"logger",
|
|
@@ -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
|
+
connectors: list[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
|
-
self.
|
|
94
|
+
self.connectors = connectors
|
|
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()
|
|
@@ -117,74 +139,77 @@ class LangChainAgent:
|
|
|
117
139
|
Returns:
|
|
118
140
|
A list of LangChain tools created from MCP tools.
|
|
119
141
|
"""
|
|
120
|
-
tools = self.connector.tools
|
|
121
|
-
local_connector = self.connector
|
|
122
|
-
|
|
123
|
-
# Wrap MCP tools into LangChain tools
|
|
124
142
|
langchain_tools: list[BaseTool] = []
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
143
|
+
|
|
144
|
+
for connector in self.connectors:
|
|
145
|
+
tools = connector.tools
|
|
146
|
+
local_connector = connector
|
|
147
|
+
|
|
148
|
+
# Wrap MCP tools into LangChain tools
|
|
149
|
+
for tool in tools:
|
|
150
|
+
# Define adapter class to convert MCP tool to LangChain format
|
|
151
|
+
class McpToLangChainAdapter(BaseTool):
|
|
152
|
+
name: str = tool.name or "NO NAME"
|
|
153
|
+
description: str = tool.description or ""
|
|
154
|
+
# Convert JSON schema to Pydantic model for argument validation
|
|
155
|
+
args_schema: type[BaseModel] = jsonschema_to_pydantic(
|
|
156
|
+
self.fix_schema(tool.inputSchema) # Apply schema conversion
|
|
157
|
+
)
|
|
158
|
+
connector: BaseConnector = local_connector
|
|
159
|
+
handle_tool_error: bool = True
|
|
160
|
+
|
|
161
|
+
def _run(self, **kwargs: Any) -> NoReturn:
|
|
162
|
+
"""Synchronous run method that always raises an error.
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
NotImplementedError: Always raises this error because MCP tools
|
|
166
|
+
only support async operations.
|
|
167
|
+
"""
|
|
168
|
+
raise NotImplementedError("MCP tools only support async operations")
|
|
169
|
+
|
|
170
|
+
async def _arun(self, **kwargs: Any) -> Any:
|
|
171
|
+
"""Asynchronously execute the tool with given arguments.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
kwargs: The arguments to pass to the tool.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
The result of the tool execution.
|
|
178
|
+
|
|
179
|
+
Raises:
|
|
180
|
+
ToolException: If tool execution fails.
|
|
181
|
+
"""
|
|
182
|
+
logger.info(f'MCP tool: "{self.name}" received input: {kwargs}')
|
|
183
|
+
|
|
164
184
|
try:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
except Exception as e:
|
|
168
|
-
# Log the exception for debugging
|
|
169
|
-
logger.error(f"Error parsing tool result: {e}")
|
|
170
|
-
# Shortened line:
|
|
171
|
-
return (
|
|
172
|
-
f"Error parsing result: {e!s}; Raw content: {tool_result.content!r}"
|
|
185
|
+
tool_result: CallToolResult = await self.connector.call_tool(
|
|
186
|
+
self.name, kwargs
|
|
173
187
|
)
|
|
188
|
+
try:
|
|
189
|
+
# Use the helper function to parse the result
|
|
190
|
+
return _parse_mcp_tool_result(tool_result)
|
|
191
|
+
except Exception as e:
|
|
192
|
+
# Log the exception for debugging
|
|
193
|
+
logger.error(f"Error parsing tool result: {e}")
|
|
194
|
+
# Shortened line:
|
|
195
|
+
return (
|
|
196
|
+
f"Error parsing result: {e!s};"
|
|
197
|
+
f" Raw content: {tool_result.content!r}"
|
|
198
|
+
)
|
|
174
199
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
200
|
+
except Exception as e:
|
|
201
|
+
if self.handle_tool_error:
|
|
202
|
+
return f"Error executing MCP tool: {str(e)}"
|
|
203
|
+
raise
|
|
179
204
|
|
|
180
|
-
|
|
205
|
+
langchain_tools.append(McpToLangChainAdapter())
|
|
181
206
|
|
|
182
207
|
# Log available tools for debugging
|
|
183
208
|
logger.info(f"Available tools: {[tool.name for tool in langchain_tools]}")
|
|
184
209
|
return langchain_tools
|
|
185
210
|
|
|
186
211
|
def _create_agent(self) -> AgentExecutor:
|
|
187
|
-
"""Create the LangChain agent.
|
|
212
|
+
"""Create the LangChain agent with the configured system message.
|
|
188
213
|
|
|
189
214
|
Returns:
|
|
190
215
|
An initialized AgentExecutor.
|
|
@@ -193,7 +218,7 @@ class LangChainAgent:
|
|
|
193
218
|
[
|
|
194
219
|
(
|
|
195
220
|
"system",
|
|
196
|
-
|
|
221
|
+
self.system_message,
|
|
197
222
|
),
|
|
198
223
|
MessagesPlaceholder(variable_name="chat_history"),
|
|
199
224
|
("human", "{input}"),
|
|
@@ -201,9 +226,8 @@ class LangChainAgent:
|
|
|
201
226
|
]
|
|
202
227
|
)
|
|
203
228
|
agent = create_tool_calling_agent(llm=self.llm, tools=self.tools, prompt=prompt)
|
|
204
|
-
print(self.tools)
|
|
205
229
|
return AgentExecutor(
|
|
206
|
-
agent=agent, tools=self.tools, max_iterations=self.max_steps, verbose=
|
|
230
|
+
agent=agent, tools=self.tools, max_iterations=self.max_steps, verbose=False
|
|
207
231
|
)
|
|
208
232
|
|
|
209
233
|
async def run(
|
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,96 +14,186 @@ 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,
|
|
28
33
|
client: MCPClient | None = None,
|
|
29
|
-
|
|
34
|
+
connectors: list[BaseConnector] | None = None,
|
|
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
|
|
|
36
45
|
Args:
|
|
37
46
|
llm: The LangChain LLM to use.
|
|
38
47
|
client: The MCPClient to use. If provided, connector is ignored.
|
|
39
|
-
|
|
48
|
+
connectors: A list of MCP connectors to use if client is not provided.
|
|
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
|
|
46
|
-
self.
|
|
59
|
+
self.connectors = connectors
|
|
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
|
-
if not client and
|
|
73
|
+
if not client and len(connectors) == 0:
|
|
54
74
|
raise ValueError("Either client or connector must be provided")
|
|
55
75
|
|
|
56
76
|
self._agent: LangChainAgent | None = None
|
|
57
|
-
self.
|
|
77
|
+
self._sessions: dict[str, 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."""
|
|
61
82
|
# If using client, get or create a session
|
|
62
83
|
if self.client:
|
|
63
|
-
try
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
84
|
+
# First try to get existing sessions
|
|
85
|
+
self._sessions = self.client.get_all_active_sessions()
|
|
86
|
+
|
|
87
|
+
# If no active sessions exist, create new ones
|
|
88
|
+
if not self._sessions:
|
|
89
|
+
self._sessions = await self.client.create_all_sessions()
|
|
90
|
+
connectors_to_use = [session.connector for session in self._sessions.values()]
|
|
68
91
|
else:
|
|
69
92
|
# Using direct connector
|
|
70
|
-
|
|
71
|
-
await
|
|
72
|
-
await
|
|
93
|
+
connectors_to_use = self.connectors
|
|
94
|
+
await [c_to_use.connect() for c_to_use in connectors_to_use]
|
|
95
|
+
await [c_to_use.initialize() for c_to_use in connectors_to_use]
|
|
96
|
+
# Create the system message based on available tools
|
|
97
|
+
await self._create_system_message(connectors_to_use)
|
|
73
98
|
|
|
74
99
|
# Create the agent
|
|
75
100
|
self._agent = LangChainAgent(
|
|
76
|
-
|
|
101
|
+
connectors=connectors_to_use,
|
|
102
|
+
llm=self.llm,
|
|
103
|
+
max_steps=self.max_steps,
|
|
104
|
+
system_message=(self._system_message.content if self._system_message else None),
|
|
77
105
|
)
|
|
78
106
|
|
|
79
107
|
# Initialize the agent
|
|
80
108
|
await self._agent.initialize()
|
|
81
109
|
self._initialized = True
|
|
82
110
|
|
|
83
|
-
async def
|
|
84
|
-
"""
|
|
85
|
-
try:
|
|
86
|
-
if self._agent:
|
|
87
|
-
# Clean up the agent first
|
|
88
|
-
self._agent = None
|
|
111
|
+
async def _create_system_message(self, connectors: list[BaseConnector]) -> None:
|
|
112
|
+
"""Create the system message based on available tools.
|
|
89
113
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
114
|
+
Args:
|
|
115
|
+
connector: The connector with available tools.
|
|
116
|
+
"""
|
|
117
|
+
# If a complete system prompt was provided, use it directly
|
|
118
|
+
if self.system_prompt:
|
|
119
|
+
self._system_message = SystemMessage(content=self.system_prompt)
|
|
120
|
+
return
|
|
96
121
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
122
|
+
# Otherwise, build the system prompt from the template and tool descriptions
|
|
123
|
+
tool_descriptions = []
|
|
124
|
+
for connector in connectors:
|
|
125
|
+
tools = connector.tools
|
|
126
|
+
# Generate tool descriptions
|
|
127
|
+
for tool in tools:
|
|
128
|
+
# Escape curly braces in the description by doubling them
|
|
129
|
+
# (sometimes e.g. blender mcp they are used in the description)
|
|
130
|
+
description = (
|
|
131
|
+
f"- {tool.name}: {tool.description.replace('{', '{{').replace('}', '}}')}"
|
|
132
|
+
)
|
|
133
|
+
tool_descriptions.append(description)
|
|
134
|
+
|
|
135
|
+
# Format the system prompt template with tool descriptions
|
|
136
|
+
system_prompt = self.system_prompt_template.format(
|
|
137
|
+
tool_descriptions="\n".join(tool_descriptions)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Add any additional instructions
|
|
141
|
+
if self.additional_instructions:
|
|
142
|
+
system_prompt += f"\n\n{self.additional_instructions}"
|
|
143
|
+
|
|
144
|
+
# Create the system message
|
|
145
|
+
self._system_message = SystemMessage(content=system_prompt)
|
|
146
|
+
|
|
147
|
+
def get_conversation_history(self) -> list[BaseMessage]:
|
|
148
|
+
"""Get the current conversation history.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
The list of conversation messages.
|
|
152
|
+
"""
|
|
153
|
+
return self._conversation_history
|
|
154
|
+
|
|
155
|
+
def clear_conversation_history(self) -> None:
|
|
156
|
+
"""Clear the conversation history."""
|
|
157
|
+
self._conversation_history = []
|
|
158
|
+
|
|
159
|
+
# Re-add the system message if it exists
|
|
160
|
+
if self._system_message:
|
|
161
|
+
self._conversation_history = [self._system_message]
|
|
162
|
+
|
|
163
|
+
def add_to_history(self, message: BaseMessage) -> None:
|
|
164
|
+
"""Add a message to the conversation history.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
message: The message to add.
|
|
168
|
+
"""
|
|
169
|
+
self._conversation_history.append(message)
|
|
170
|
+
|
|
171
|
+
def get_system_message(self) -> SystemMessage | None:
|
|
172
|
+
"""Get the current system message.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
The current system message, or None if not set.
|
|
176
|
+
"""
|
|
177
|
+
return self._system_message
|
|
178
|
+
|
|
179
|
+
def set_system_message(self, message: str) -> None:
|
|
180
|
+
"""Set a new system message.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
message: The new system message content.
|
|
184
|
+
"""
|
|
185
|
+
self._system_message = SystemMessage(content=message)
|
|
186
|
+
|
|
187
|
+
# Update the agent if initialized
|
|
188
|
+
if self._agent:
|
|
189
|
+
self._agent.set_system_message(message)
|
|
103
190
|
|
|
104
191
|
async def run(
|
|
105
|
-
self,
|
|
192
|
+
self,
|
|
193
|
+
query: str,
|
|
194
|
+
max_steps: int | None = None,
|
|
195
|
+
manage_connector: bool = True,
|
|
196
|
+
external_history: list[BaseMessage] | None = None,
|
|
106
197
|
) -> str:
|
|
107
198
|
"""Run a query using the MCP tools.
|
|
108
199
|
|
|
@@ -116,34 +207,107 @@ class MCPAgent:
|
|
|
116
207
|
If True, this method will connect, initialize, and disconnect from
|
|
117
208
|
the connector automatically. If False, the caller is responsible
|
|
118
209
|
for managing the connector lifecycle.
|
|
210
|
+
external_history: Optional external history to use instead of the
|
|
211
|
+
internal conversation history.
|
|
119
212
|
|
|
120
213
|
Returns:
|
|
121
214
|
The result of running the query.
|
|
122
215
|
"""
|
|
123
216
|
result = ""
|
|
217
|
+
initialized_here = False
|
|
218
|
+
|
|
124
219
|
try:
|
|
125
|
-
if
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
220
|
+
# Initialize if needed
|
|
221
|
+
if manage_connector and (not self._initialized or not self._agent):
|
|
222
|
+
logger.info("Initializing agent before running query")
|
|
223
|
+
await self.initialize()
|
|
224
|
+
initialized_here = True
|
|
225
|
+
elif not self._initialized and self.auto_initialize:
|
|
226
|
+
logger.info("Auto-initializing agent before running query")
|
|
227
|
+
await self.initialize()
|
|
228
|
+
initialized_here = True
|
|
229
|
+
|
|
230
|
+
# Check if initialization succeeded
|
|
231
|
+
if not self._agent:
|
|
232
|
+
raise RuntimeError("MCP agent failed to initialize")
|
|
233
|
+
|
|
234
|
+
# Add the user query to conversation history if memory is enabled
|
|
235
|
+
if self.memory_enabled:
|
|
236
|
+
self.add_to_history(HumanMessage(content=query))
|
|
129
237
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
238
|
+
# Use the provided history or the internal history
|
|
239
|
+
history_to_use = (
|
|
240
|
+
external_history if external_history is not None else self._conversation_history
|
|
241
|
+
)
|
|
133
242
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if
|
|
138
|
-
|
|
243
|
+
# Convert messages to format expected by LangChain
|
|
244
|
+
langchain_history = []
|
|
245
|
+
for msg in history_to_use:
|
|
246
|
+
if isinstance(msg, HumanMessage):
|
|
247
|
+
langchain_history.append({"type": "human", "content": msg.content})
|
|
248
|
+
elif isinstance(msg, AIMessage):
|
|
249
|
+
langchain_history.append({"type": "ai", "content": msg.content})
|
|
250
|
+
elif isinstance(msg, SystemMessage) and msg != self._system_message:
|
|
251
|
+
# Include system messages other than the main one
|
|
252
|
+
# which is already included in the agent's prompt
|
|
253
|
+
langchain_history.append({"type": "system", "content": msg.content})
|
|
254
|
+
# Other message types can be handled here if needed
|
|
139
255
|
|
|
140
|
-
|
|
141
|
-
|
|
256
|
+
# Run the query with the specified max_steps or default
|
|
257
|
+
logger.info(f"Running query with max_steps={max_steps or self.max_steps}")
|
|
258
|
+
result = await self._agent.run(
|
|
259
|
+
query=query,
|
|
260
|
+
max_steps=max_steps,
|
|
261
|
+
chat_history=langchain_history,
|
|
262
|
+
)
|
|
142
263
|
|
|
143
|
-
|
|
264
|
+
# Add the response to conversation history if memory is enabled
|
|
265
|
+
if self.memory_enabled:
|
|
266
|
+
self.add_to_history(AIMessage(content=result))
|
|
144
267
|
|
|
145
268
|
return result
|
|
269
|
+
|
|
270
|
+
except Exception as e:
|
|
271
|
+
logger.error(f"Error running query: {e}")
|
|
272
|
+
# If we initialized in this method and there was an error,
|
|
273
|
+
# make sure to clean up
|
|
274
|
+
if initialized_here and manage_connector:
|
|
275
|
+
logger.info("Cleaning up resources after initialization error")
|
|
276
|
+
await self.close()
|
|
277
|
+
raise
|
|
278
|
+
|
|
146
279
|
finally:
|
|
147
|
-
#
|
|
148
|
-
|
|
280
|
+
# Clean up resources if we're managing the connector and
|
|
281
|
+
# we're not using a client that manages sessions
|
|
282
|
+
if manage_connector and not self.client and not initialized_here:
|
|
283
|
+
logger.info("Closing agent after query completion")
|
|
149
284
|
await self.close()
|
|
285
|
+
|
|
286
|
+
async def close(self) -> None:
|
|
287
|
+
"""Close the MCP connection with improved error handling."""
|
|
288
|
+
try:
|
|
289
|
+
if self._agent:
|
|
290
|
+
# Clean up the agent first
|
|
291
|
+
logger.debug("Cleaning up agent")
|
|
292
|
+
self._agent = None
|
|
293
|
+
|
|
294
|
+
# If using client with session, close the session through client
|
|
295
|
+
if self.client and self._sessions:
|
|
296
|
+
logger.debug("Closing session through client")
|
|
297
|
+
await self.client.close_all_sessions()
|
|
298
|
+
self._session = None
|
|
299
|
+
# If using direct connector, disconnect
|
|
300
|
+
elif self.connectors:
|
|
301
|
+
for connector in self.connectors:
|
|
302
|
+
logger.debug("Disconnecting connector")
|
|
303
|
+
await connector.disconnect()
|
|
304
|
+
|
|
305
|
+
self._initialized = False
|
|
306
|
+
logger.info("Agent closed successfully")
|
|
307
|
+
|
|
308
|
+
except Exception as e:
|
|
309
|
+
logger.error(f"Error during agent closure: {e}")
|
|
310
|
+
# Still try to clean up references even if there was an error
|
|
311
|
+
self._agent = None
|
|
312
|
+
self._session = None
|
|
313
|
+
self._initialized = False
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
- Retrieve and analyze information relevant to user requests
|
|
7
|
+
- Process and transform data in various formats
|
|
8
|
+
- Perform computations and generate insights
|
|
9
|
+
- Execute multi-step workflows by combining tools as needed
|
|
10
|
+
- Interact with external systems when authorized
|
|
11
|
+
|
|
12
|
+
When appropriate, use available tools rather than relying on your built-in knowledge alone.
|
|
13
|
+
Your tools enable you to perform tasks that would otherwise be beyond your capabilities.
|
|
14
|
+
|
|
15
|
+
For optimal assistance:
|
|
16
|
+
1. Identify when a tool can help address the user's request
|
|
17
|
+
2. Select the most appropriate tool(s) for the task
|
|
18
|
+
3. Apply tools in the correct sequence when multiple tools are needed
|
|
19
|
+
4. Clearly communicate your process and findings
|
|
20
|
+
|
|
21
|
+
Remember that you have real capabilities through your tools - use them confidently when needed.
|
|
22
|
+
"""
|