mcp-use 0.1.0__py3-none-any.whl → 1.0.0__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 CHANGED
@@ -1,19 +1,21 @@
1
1
  """
2
- mcp_use - A model-agnostic MCP (Multi-Channel Platform) library for LLMs.
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 create_session_from_config, load_config_file
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__ = "0.1.0"
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, connector: BaseConnector, llm: BaseLanguageModel, max_steps: int = 5
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.connector = connector
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
- for tool in tools:
126
- # Define adapter class to convert MCP tool to LangChain format
127
- class McpToLangChainAdapter(BaseTool):
128
- name: str = tool.name or "NO NAME"
129
- description: str = tool.description or ""
130
- # Convert JSON schema to Pydantic model for argument validation
131
- args_schema: type[BaseModel] = jsonschema_to_pydantic(
132
- self.fix_schema(tool.inputSchema) # Apply schema conversion
133
- )
134
- connector: BaseConnector = local_connector
135
- handle_tool_error: bool = True
136
-
137
- def _run(self, **kwargs: Any) -> NoReturn:
138
- """Synchronous run method that always raises an error.
139
-
140
- Raises:
141
- NotImplementedError: Always raises this error because MCP tools
142
- only support async operations.
143
- """
144
- raise NotImplementedError("MCP tools only support async operations")
145
-
146
- async def _arun(self, **kwargs: Any) -> Any:
147
- """Asynchronously execute the tool with given arguments.
148
-
149
- Args:
150
- kwargs: The arguments to pass to the tool.
151
-
152
- Returns:
153
- The result of the tool execution.
154
-
155
- Raises:
156
- ToolException: If tool execution fails.
157
- """
158
- logger.info(f'MCP tool: "{self.name}" received input: {kwargs}')
159
-
160
- try:
161
- tool_result: CallToolResult = await self.connector.call_tool(
162
- self.name, kwargs
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
- # Use the helper function to parse the result
166
- return _parse_mcp_tool_result(tool_result)
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
- except Exception as e:
176
- if self.handle_tool_error:
177
- return f"Error executing MCP tool: {str(e)}"
178
- raise
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
- langchain_tools.append(McpToLangChainAdapter())
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
- "You are a helpful AI assistant that can use tools to help users.",
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=True
230
+ agent=agent, tools=self.tools, max_iterations=self.max_steps, verbose=False
207
231
  )
208
232
 
209
233
  async def run(
@@ -1,10 +1,11 @@
1
1
  """
2
- Model-Agnostic MCP: Main integration module.
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
- connector: BaseConnector | None = None,
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
- connector: The MCP connector to use if client is not provided.
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.connector = connector
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 not connector:
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._session: MCPSession | None = None
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
- self._session = self.client.get_session(self.server_name)
65
- except ValueError:
66
- self._session = await self.client.create_session(self.server_name)
67
- connector_to_use = self._session.connector
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
- connector_to_use = self.connector
71
- await connector_to_use.connect()
72
- await connector_to_use.initialize()
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
- connector=connector_to_use, llm=self.llm, max_steps=self.max_steps
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 close(self) -> None:
84
- """Close the MCP connection."""
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
- # If using client with session, close the session through client
91
- if self.client and self._session:
92
- await self.client.close_session(self.server_name)
93
- # If using direct connector, disconnect
94
- elif self.connector:
95
- await self.connector.disconnect()
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
- self._initialized = False
98
- except Exception as e:
99
- logger.warning(f"Warning: Error during agent closure: {e}")
100
- # Still try to clean up even if there was an error
101
- self._agent = None
102
- self._initialized = False
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, query: str, max_steps: int | None = None, manage_connector: bool = True
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 manage_connector:
126
- # Initialize if needed
127
- if not self._initialized or not self._agent:
128
- await self.initialize()
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
- # Run the query
131
- if not self._agent:
132
- raise RuntimeError("MCP client failed to initialize")
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
- result = await self._agent.run(query, max_steps)
135
- else:
136
- # Caller is managing connector lifecycle
137
- if not self._initialized and self.auto_initialize:
138
- await self.initialize()
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
- if not self._agent:
141
- raise RuntimeError("MCP client is not initialized")
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
- result = await self._agent.run(query, max_steps)
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
- # Make sure to clean up the connection if we're managing it
148
- if manage_connector and not self.client:
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
+ """