mcp-use 1.2.13__py3-none-any.whl → 1.3.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.

@@ -6,9 +6,11 @@ to provide a simple interface for using MCP tools with different LLMs.
6
6
  """
7
7
 
8
8
  import logging
9
+ import time
9
10
  from collections.abc import AsyncIterator
10
11
 
11
12
  from langchain.agents import AgentExecutor, create_tool_calling_agent
13
+ from langchain.agents.output_parsers.tools import ToolAgentAction
12
14
  from langchain.globals import set_debug
13
15
  from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
14
16
  from langchain.schema import AIMessage, BaseMessage, HumanMessage, SystemMessage
@@ -21,7 +23,8 @@ from langchain_core.utils.input import get_color_mapping
21
23
 
22
24
  from mcp_use.client import MCPClient
23
25
  from mcp_use.connectors.base import BaseConnector
24
- from mcp_use.session import MCPSession
26
+ from mcp_use.telemetry.posthog import Telemetry
27
+ from mcp_use.telemetry.utils import extract_model_info
25
28
 
26
29
  from ..adapters.langchain_adapter import LangChainAdapter
27
30
  from ..logging import logger
@@ -93,6 +96,9 @@ class MCPAgent:
93
96
  # Create the adapter for tool conversion
94
97
  self.adapter = LangChainAdapter(disallowed_tools=self.disallowed_tools)
95
98
 
99
+ # Initialize telemetry
100
+ self.telemetry = Telemetry()
101
+
96
102
  # Initialize server manager if requested
97
103
  self.server_manager = None
98
104
  if self.use_server_manager:
@@ -102,9 +108,10 @@ class MCPAgent:
102
108
 
103
109
  # State tracking
104
110
  self._agent_executor: AgentExecutor | None = None
105
- self._sessions: dict[str, MCPSession] = {}
106
111
  self._system_message: SystemMessage | None = None
107
- self._tools: list[BaseTool] = []
112
+
113
+ # Track model info for telemetry
114
+ self._model_provider, self._model_name = extract_model_info(self.llm)
108
115
 
109
116
  async def initialize(self) -> None:
110
117
  """Initialize the MCP client and agent."""
@@ -132,6 +139,7 @@ class MCPAgent:
132
139
  if not self._sessions:
133
140
  logger.info("🔄 No active sessions found, creating new ones...")
134
141
  self._sessions = await self.client.create_all_sessions()
142
+ self.connectors = [session.connector for session in self._sessions.values()]
135
143
  logger.info(f"✅ Created {len(self._sessions)} new sessions")
136
144
 
137
145
  # Create LangChain tools directly from the client using the adapter
@@ -143,7 +151,7 @@ class MCPAgent:
143
151
  connectors_to_use = self.connectors
144
152
  logger.info(f"🔗 Connecting to {len(connectors_to_use)} direct connectors...")
145
153
  for connector in connectors_to_use:
146
- if not hasattr(connector, "client") or connector.client is None:
154
+ if not hasattr(connector, "client_session") or connector.client_session is None:
147
155
  await connector.connect()
148
156
 
149
157
  # Create LangChain tools using the adapter with connectors
@@ -346,20 +354,18 @@ class MCPAgent:
346
354
  history_to_use = (
347
355
  external_history if external_history is not None else self._conversation_history
348
356
  )
349
- langchain_history: list[BaseMessage] = [
350
- m for m in history_to_use if isinstance(m, HumanMessage | AIMessage)
351
- ]
352
- inputs = {"input": query, "chat_history": langchain_history}
357
+ inputs = {"input": query, "chat_history": history_to_use}
353
358
 
354
359
  # 3. Stream & diff -------------------------------------------------------
355
- accumulated = ""
356
360
  async for event in self._agent_executor.astream_events(inputs):
361
+ if event.get("event") == "on_chain_end":
362
+ output = event["data"]["output"]
363
+ if isinstance(output, list):
364
+ for message in output:
365
+ if not isinstance(message, ToolAgentAction):
366
+ self.add_to_history(message)
357
367
  yield event
358
368
 
359
- # 4. Persist assistant message ------------------------------------------
360
- if self.memory_enabled and accumulated:
361
- self.add_to_history(AIMessage(content=accumulated))
362
-
363
369
  # 5. House-keeping -------------------------------------------------------
364
370
  if initialised_here and manage_connector:
365
371
  await self.close()
@@ -378,13 +384,58 @@ class MCPAgent:
378
384
  async for chunk in agent.astream("hello"):
379
385
  print(chunk, end="|", flush=True)
380
386
  """
381
- async for chunk in self._generate_response_chunks_async(
382
- query=query,
383
- max_steps=max_steps,
384
- manage_connector=manage_connector,
385
- external_history=external_history,
386
- ):
387
- yield chunk
387
+ start_time = time.time()
388
+ success = False
389
+ chunk_count = 0
390
+ total_response_length = 0
391
+
392
+ try:
393
+ async for chunk in self._generate_response_chunks_async(
394
+ query=query,
395
+ max_steps=max_steps,
396
+ manage_connector=manage_connector,
397
+ external_history=external_history,
398
+ ):
399
+ chunk_count += 1
400
+ if isinstance(chunk, str):
401
+ total_response_length += len(chunk)
402
+ yield chunk
403
+ success = True
404
+ finally:
405
+ # Track comprehensive execution data for streaming
406
+ execution_time_ms = int((time.time() - start_time) * 1000)
407
+
408
+ server_count = 0
409
+ if self.client:
410
+ server_count = len(self.client.get_all_active_sessions())
411
+ elif self.connectors:
412
+ server_count = len(self.connectors)
413
+
414
+ conversation_history_length = (
415
+ len(self._conversation_history) if self.memory_enabled else 0
416
+ )
417
+
418
+ self.telemetry.track_agent_execution(
419
+ execution_method="astream",
420
+ query=query,
421
+ success=success,
422
+ model_provider=self._model_provider,
423
+ model_name=self._model_name,
424
+ server_count=server_count,
425
+ server_identifiers=[connector.public_identifier for connector in self.connectors],
426
+ total_tools_available=len(self._tools) if self._tools else 0,
427
+ tools_available_names=[tool.name for tool in self._tools],
428
+ max_steps_configured=self.max_steps,
429
+ memory_enabled=self.memory_enabled,
430
+ use_server_manager=self.use_server_manager,
431
+ max_steps_used=max_steps,
432
+ manage_connector=manage_connector,
433
+ external_history_used=external_history is not None,
434
+ response=f"[STREAMED RESPONSE - {total_response_length} chars]",
435
+ execution_time_ms=execution_time_ms,
436
+ error_type=None if success else "streaming_error",
437
+ conversation_history_length=conversation_history_length,
438
+ )
388
439
 
389
440
  async def run(
390
441
  self,
@@ -413,6 +464,10 @@ class MCPAgent:
413
464
  """
414
465
  result = ""
415
466
  initialized_here = False
467
+ start_time = time.time()
468
+ tools_used_names = []
469
+ steps_taken = 0
470
+ success = False
416
471
 
417
472
  try:
418
473
  # Initialize if needed
@@ -468,6 +523,7 @@ class MCPAgent:
468
523
  logger.info(f"🏁 Starting agent execution with max_steps={steps}")
469
524
 
470
525
  for step_num in range(steps):
526
+ steps_taken = step_num + 1
471
527
  # --- Check for tool updates if using server manager ---
472
528
  if self.use_server_manager and self.server_manager:
473
529
  current_tools = self.server_manager.tools
@@ -514,9 +570,10 @@ class MCPAgent:
514
570
  # If it's actions/steps, add to intermediate steps
515
571
  intermediate_steps.extend(next_step_output)
516
572
 
517
- # Log tool calls
573
+ # Log tool calls and track tool usage
518
574
  for action, output in next_step_output:
519
575
  tool_name = action.tool
576
+ tools_used_names.append(tool_name)
520
577
  tool_input_str = str(action.tool_input)
521
578
  # Truncate long inputs for readability
522
579
  if len(tool_input_str) > 100:
@@ -559,7 +616,8 @@ class MCPAgent:
559
616
  if self.memory_enabled:
560
617
  self.add_to_history(AIMessage(content=result))
561
618
 
562
- logger.info("🎉 Agent execution complete")
619
+ logger.info(f"🎉 Agent execution complete in {time.time() - start_time} seconds")
620
+ success = True
563
621
  return result
564
622
 
565
623
  except Exception as e:
@@ -570,6 +628,43 @@ class MCPAgent:
570
628
  raise
571
629
 
572
630
  finally:
631
+ # Track comprehensive execution data
632
+ execution_time_ms = int((time.time() - start_time) * 1000)
633
+
634
+ server_count = 0
635
+ if self.client:
636
+ server_count = len(self.client.get_all_active_sessions())
637
+ elif self.connectors:
638
+ server_count = len(self.connectors)
639
+
640
+ conversation_history_length = (
641
+ len(self._conversation_history) if self.memory_enabled else 0
642
+ )
643
+ self.telemetry.track_agent_execution(
644
+ execution_method="run",
645
+ query=query,
646
+ success=success,
647
+ model_provider=self._model_provider,
648
+ model_name=self._model_name,
649
+ server_count=server_count,
650
+ server_identifiers=[connector.public_identifier for connector in self.connectors],
651
+ total_tools_available=len(self._tools) if self._tools else 0,
652
+ tools_available_names=[tool.name for tool in self._tools],
653
+ max_steps_configured=self.max_steps,
654
+ memory_enabled=self.memory_enabled,
655
+ use_server_manager=self.use_server_manager,
656
+ max_steps_used=max_steps,
657
+ manage_connector=manage_connector,
658
+ external_history_used=external_history is not None,
659
+ steps_taken=steps_taken,
660
+ tools_used_count=len(tools_used_names),
661
+ tools_used_names=tools_used_names,
662
+ response=result,
663
+ execution_time_ms=execution_time_ms,
664
+ error_type=None if success else "execution_error",
665
+ conversation_history_length=conversation_history_length,
666
+ )
667
+
573
668
  # Clean up if necessary (e.g., if not using client-managed sessions)
574
669
  if manage_connector and not self.client and not initialized_here:
575
670
  logger.info("🧹 Closing agent after query completion")
mcp_use/client.py CHANGED
@@ -9,6 +9,8 @@ import json
9
9
  import warnings
10
10
  from typing import Any
11
11
 
12
+ from mcp_use.types.sandbox import SandboxOptions
13
+
12
14
  from .config import create_connector_from_config, load_config_file
13
15
  from .logging import logger
14
16
  from .session import MCPSession
@@ -24,14 +26,20 @@ class MCPClient:
24
26
  def __init__(
25
27
  self,
26
28
  config: str | dict[str, Any] | None = None,
29
+ sandbox: bool = False,
30
+ sandbox_options: SandboxOptions | None = None,
27
31
  ) -> None:
28
32
  """Initialize a new MCP client.
29
33
 
30
34
  Args:
31
35
  config: Either a dict containing configuration or a path to a JSON config file.
32
36
  If None, an empty configuration is used.
37
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
38
+ sandbox_options: Optional sandbox configuration options.
33
39
  """
34
40
  self.config: dict[str, Any] = {}
41
+ self.sandbox = sandbox
42
+ self.sandbox_options = sandbox_options
35
43
  self.sessions: dict[str, MCPSession] = {}
36
44
  self.active_sessions: list[str] = []
37
45
 
@@ -43,22 +51,35 @@ class MCPClient:
43
51
  self.config = config
44
52
 
45
53
  @classmethod
46
- def from_dict(cls, config: dict[str, Any]) -> "MCPClient":
54
+ def from_dict(
55
+ cls,
56
+ config: dict[str, Any],
57
+ sandbox: bool = False,
58
+ sandbox_options: SandboxOptions | None = None,
59
+ ) -> "MCPClient":
47
60
  """Create a MCPClient from a dictionary.
48
61
 
49
62
  Args:
50
63
  config: The configuration dictionary.
64
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
65
+ sandbox_options: Optional sandbox configuration options.
51
66
  """
52
- return cls(config=config)
67
+ return cls(config=config, sandbox=sandbox, sandbox_options=sandbox_options)
53
68
 
54
69
  @classmethod
55
- def from_config_file(cls, filepath: str) -> "MCPClient":
70
+ def from_config_file(
71
+ cls, filepath: str, sandbox: bool = False, sandbox_options: SandboxOptions | None = None
72
+ ) -> "MCPClient":
56
73
  """Create a MCPClient from a configuration file.
57
74
 
58
75
  Args:
59
76
  filepath: The path to the configuration file.
77
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
78
+ sandbox_options: Optional sandbox configuration options.
60
79
  """
61
- return cls(config=load_config_file(filepath))
80
+ return cls(
81
+ config=load_config_file(filepath), sandbox=sandbox, sandbox_options=sandbox_options
82
+ )
62
83
 
63
84
  def add_server(
64
85
  self,
@@ -111,6 +132,7 @@ class MCPClient:
111
132
 
112
133
  Args:
113
134
  server_name: The name of the server to create a session for.
135
+ auto_initialize: Whether to automatically initialize the session.
114
136
 
115
137
  Returns:
116
138
  The created MCPSession.
@@ -128,7 +150,11 @@ class MCPClient:
128
150
  raise ValueError(f"Server '{server_name}' not found in config")
129
151
 
130
152
  server_config = servers[server_name]
131
- connector = create_connector_from_config(server_config)
153
+
154
+ # Create connector with options
155
+ connector = create_connector_from_config(
156
+ server_config, sandbox=self.sandbox, sandbox_options=self.sandbox_options
157
+ )
132
158
 
133
159
  # Create the session
134
160
  session = MCPSession(connector)
@@ -146,16 +172,16 @@ class MCPClient:
146
172
  self,
147
173
  auto_initialize: bool = True,
148
174
  ) -> dict[str, MCPSession]:
149
- """Create a session for the specified server.
175
+ """Create sessions for all configured servers.
150
176
 
151
177
  Args:
152
- auto_initialize: Whether to automatically initialize the session.
178
+ auto_initialize: Whether to automatically initialize the sessions.
153
179
 
154
180
  Returns:
155
- The created MCPSession. If server_name is None, returns the first created session.
181
+ Dictionary mapping server names to their MCPSession instances.
156
182
 
157
183
  Warns:
158
- Warning: If no servers are configured.
184
+ UserWarning: If no servers are configured.
159
185
  """
160
186
  # Get server config
161
187
  servers = self.config.get("mcpServers", {})
mcp_use/config.py CHANGED
@@ -7,7 +7,16 @@ This module provides functionality to load MCP configuration from JSON files.
7
7
  import json
8
8
  from typing import Any
9
9
 
10
- from .connectors import BaseConnector, HttpConnector, StdioConnector, WebSocketConnector
10
+ from mcp_use.types.sandbox import SandboxOptions
11
+
12
+ from .connectors import (
13
+ BaseConnector,
14
+ HttpConnector,
15
+ SandboxConnector,
16
+ StdioConnector,
17
+ WebSocketConnector,
18
+ )
19
+ from .connectors.utils import is_stdio_server
11
20
 
12
21
 
13
22
  def load_config_file(filepath: str) -> dict[str, Any]:
@@ -23,23 +32,40 @@ def load_config_file(filepath: str) -> dict[str, Any]:
23
32
  return json.load(f)
24
33
 
25
34
 
26
- def create_connector_from_config(server_config: dict[str, Any]) -> BaseConnector:
35
+ def create_connector_from_config(
36
+ server_config: dict[str, Any],
37
+ sandbox: bool = False,
38
+ sandbox_options: SandboxOptions | None = None,
39
+ ) -> BaseConnector:
27
40
  """Create a connector based on server configuration.
28
-
41
+ This function can be called with just the server_config parameter:
42
+ create_connector_from_config(server_config)
29
43
  Args:
30
44
  server_config: The server configuration section
45
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
46
+ sandbox_options: Optional sandbox configuration options.
31
47
 
32
48
  Returns:
33
49
  A configured connector instance
34
50
  """
51
+
35
52
  # Stdio connector (command-based)
36
- if "command" in server_config and "args" in server_config:
53
+ if is_stdio_server(server_config) and not sandbox:
37
54
  return StdioConnector(
38
55
  command=server_config["command"],
39
56
  args=server_config["args"],
40
57
  env=server_config.get("env", None),
41
58
  )
42
59
 
60
+ # Sandboxed connector
61
+ elif is_stdio_server(server_config) and sandbox:
62
+ return SandboxConnector(
63
+ command=server_config["command"],
64
+ args=server_config["args"],
65
+ env=server_config.get("env", None),
66
+ e2b_options=sandbox_options,
67
+ )
68
+
43
69
  # HTTP connector
44
70
  elif "url" in server_config:
45
71
  return HttpConnector(
@@ -5,9 +5,16 @@ This module provides interfaces for connecting to MCP implementations
5
5
  through different transport mechanisms.
6
6
  """
7
7
 
8
- from .base import BaseConnector
9
- from .http import HttpConnector
10
- from .stdio import StdioConnector
11
- from .websocket import WebSocketConnector
8
+ from .base import BaseConnector # noqa: F401
9
+ from .http import HttpConnector # noqa: F401
10
+ from .sandbox import SandboxConnector # noqa: F401
11
+ from .stdio import StdioConnector # noqa: F401
12
+ from .websocket import WebSocketConnector # noqa: F401
12
13
 
13
- __all__ = ["BaseConnector", "StdioConnector", "WebSocketConnector", "HttpConnector"]
14
+ __all__ = [
15
+ "BaseConnector",
16
+ "StdioConnector",
17
+ "HttpConnector",
18
+ "WebSocketConnector",
19
+ "SandboxConnector",
20
+ ]