mcp-use 1.3.0__py3-none-any.whl → 1.3.2__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.

Files changed (38) hide show
  1. mcp_use/__init__.py +2 -0
  2. mcp_use/adapters/base.py +2 -6
  3. mcp_use/adapters/langchain_adapter.py +4 -11
  4. mcp_use/agents/base.py +1 -3
  5. mcp_use/agents/mcpagent.py +121 -45
  6. mcp_use/agents/prompts/system_prompt_builder.py +1 -3
  7. mcp_use/client.py +26 -11
  8. mcp_use/config.py +9 -9
  9. mcp_use/connectors/base.py +136 -32
  10. mcp_use/connectors/http.py +100 -30
  11. mcp_use/connectors/sandbox.py +11 -16
  12. mcp_use/connectors/stdio.py +8 -5
  13. mcp_use/connectors/websocket.py +8 -5
  14. mcp_use/logging.py +1 -1
  15. mcp_use/managers/server_manager.py +5 -16
  16. mcp_use/managers/tools/disconnect_server.py +1 -3
  17. mcp_use/managers/tools/get_active_server.py +1 -4
  18. mcp_use/managers/tools/search_tools.py +29 -36
  19. mcp_use/managers/tools/use_tool.py +5 -18
  20. mcp_use/observability/__init__.py +8 -0
  21. mcp_use/observability/laminar.py +21 -0
  22. mcp_use/observability/langfuse.py +35 -0
  23. mcp_use/session.py +1 -4
  24. mcp_use/task_managers/__init__.py +2 -1
  25. mcp_use/task_managers/base.py +10 -4
  26. mcp_use/task_managers/streamable_http.py +81 -0
  27. mcp_use/task_managers/websocket.py +5 -0
  28. mcp_use/telemetry/__init__.py +0 -0
  29. mcp_use/telemetry/events.py +93 -0
  30. mcp_use/telemetry/telemetry.py +306 -0
  31. mcp_use/telemetry/utils.py +48 -0
  32. mcp_use/utils.py +27 -0
  33. {mcp_use-1.3.0.dist-info → mcp_use-1.3.2.dist-info}/METADATA +82 -26
  34. mcp_use-1.3.2.dist-info/RECORD +49 -0
  35. mcp_use/types/clientoptions.py +0 -23
  36. mcp_use-1.3.0.dist-info/RECORD +0 -41
  37. {mcp_use-1.3.0.dist-info → mcp_use-1.3.2.dist-info}/WHEEL +0 -0
  38. {mcp_use-1.3.0.dist-info → mcp_use-1.3.2.dist-info}/licenses/LICENSE +0 -0
mcp_use/__init__.py CHANGED
@@ -7,6 +7,7 @@ to MCP tools through existing LangChain adapters.
7
7
 
8
8
  from importlib.metadata import version
9
9
 
10
+ from . import observability
10
11
  from .agents.mcpagent import MCPAgent
11
12
  from .client import MCPClient
12
13
  from .config import load_config_file
@@ -30,6 +31,7 @@ __all__ = [
30
31
  "MCP_USE_DEBUG",
31
32
  "Logger",
32
33
  "set_debug",
34
+ "observability",
33
35
  ]
34
36
 
35
37
 
mcp_use/adapters/base.py CHANGED
@@ -34,9 +34,7 @@ class BaseAdapter(ABC):
34
34
  self._connector_tool_map: dict[BaseConnector, list[T]] = {}
35
35
 
36
36
  @classmethod
37
- async def create_tools(
38
- cls, client: "MCPClient", disallowed_tools: list[str] | None = None
39
- ) -> list[T]:
37
+ async def create_tools(cls, client: "MCPClient", disallowed_tools: list[str] | None = None) -> list[T]:
40
38
  """Create tools from an MCPClient instance.
41
39
 
42
40
  This is the recommended way to create tools from an MCPClient, as it handles
@@ -86,9 +84,7 @@ class BaseAdapter(ABC):
86
84
  """
87
85
  # Check if we already have tools for this connector
88
86
  if connector in self._connector_tool_map:
89
- logger.debug(
90
- f"Returning {len(self._connector_tool_map[connector])} existing tools for connector"
91
- )
87
+ logger.debug(f"Returning {len(self._connector_tool_map[connector])} existing tools for connector")
92
88
  return self._connector_tool_map[connector]
93
89
 
94
90
  # Create tools for this connector
@@ -89,9 +89,7 @@ class LangChainAdapter(BaseAdapter):
89
89
  elif hasattr(resource, "blob"):
90
90
  # Assuming blob needs decoding or specific handling; adjust as needed
91
91
  decoded_result += (
92
- resource.blob.decode()
93
- if isinstance(resource.blob, bytes)
94
- else str(resource.blob)
92
+ resource.blob.decode() if isinstance(resource.blob, bytes) else str(resource.blob)
95
93
  )
96
94
  else:
97
95
  raise ToolException(f"Unexpected resource type: {resource.type}")
@@ -154,9 +152,7 @@ class LangChainAdapter(BaseAdapter):
154
152
  logger.debug(f'MCP tool: "{self.name}" received input: {kwargs}')
155
153
 
156
154
  try:
157
- tool_result: CallToolResult = await self.tool_connector.call_tool(
158
- self.name, kwargs
159
- )
155
+ tool_result: CallToolResult = await self.tool_connector.call_tool(self.name, kwargs)
160
156
  try:
161
157
  # Use the helper function to parse the result
162
158
  return adapter_self._parse_mcp_tool_result(tool_result)
@@ -185,8 +181,7 @@ class LangChainAdapter(BaseAdapter):
185
181
  class ResourceTool(BaseTool):
186
182
  name: str = _sanitize(mcp_resource.name or f"resource_{mcp_resource.uri}")
187
183
  description: str = (
188
- mcp_resource.description
189
- or f"Return the content of the resource located at URI {mcp_resource.uri}."
184
+ mcp_resource.description or f"Return the content of the resource located at URI {mcp_resource.uri}."
190
185
  )
191
186
  args_schema: type[BaseModel] = ReadResourceRequestParams
192
187
  tool_connector: BaseConnector = connector
@@ -243,9 +238,7 @@ class LangChainAdapter(BaseAdapter):
243
238
  Field(None, description=arg.description),
244
239
  )
245
240
 
246
- InputSchema = create_model(
247
- dynamic_model_name, **field_definitions_for_create, __base__=BaseModel
248
- )
241
+ InputSchema = create_model(dynamic_model_name, **field_definitions_for_create, __base__=BaseModel)
249
242
  else:
250
243
  # Create an empty Pydantic model if there are no arguments
251
244
  InputSchema = create_model(dynamic_model_name, __base__=BaseModel)
mcp_use/agents/base.py CHANGED
@@ -48,9 +48,7 @@ class BaseAgent(ABC):
48
48
  pass
49
49
 
50
50
  @abstractmethod
51
- async def step(
52
- self, query: str, previous_steps: list[dict[str, Any]] | None = None
53
- ) -> dict[str, Any]:
51
+ async def step(self, query: str, previous_steps: list[dict[str, Any]] | None = None) -> dict[str, Any]:
54
52
  """Perform a single step of the agent.
55
53
 
56
54
  Args:
@@ -6,6 +6,7 @@ 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
@@ -22,6 +23,8 @@ from langchain_core.utils.input import get_color_mapping
22
23
 
23
24
  from mcp_use.client import MCPClient
24
25
  from mcp_use.connectors.base import BaseConnector
26
+ from mcp_use.telemetry.telemetry 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:
@@ -104,6 +110,9 @@ class MCPAgent:
104
110
  self._agent_executor: AgentExecutor | None = None
105
111
  self._system_message: SystemMessage | None = None
106
112
 
113
+ # Track model info for telemetry
114
+ self._model_provider, self._model_name = extract_model_info(self.llm)
115
+
107
116
  async def initialize(self) -> None:
108
117
  """Initialize the MCP client and agent."""
109
118
  logger.info("🚀 Initializing MCP agent and connecting to services...")
@@ -113,9 +122,7 @@ class MCPAgent:
113
122
  # Get server management tools
114
123
  management_tools = self.server_manager.tools
115
124
  self._tools = management_tools
116
- logger.info(
117
- f"🔧 Server manager mode active with {len(management_tools)} management tools"
118
- )
125
+ logger.info(f"🔧 Server manager mode active with {len(management_tools)} management tools")
119
126
 
120
127
  # Create the system message based on available tools
121
128
  await self._create_system_message_from_tools(self._tools)
@@ -130,6 +137,7 @@ class MCPAgent:
130
137
  if not self._sessions:
131
138
  logger.info("🔄 No active sessions found, creating new ones...")
132
139
  self._sessions = await self.client.create_all_sessions()
140
+ self.connectors = [session.connector for session in self._sessions.values()]
133
141
  logger.info(f"✅ Created {len(self._sessions)} new sessions")
134
142
 
135
143
  # Create LangChain tools directly from the client using the adapter
@@ -141,7 +149,7 @@ class MCPAgent:
141
149
  connectors_to_use = self.connectors
142
150
  logger.info(f"🔗 Connecting to {len(connectors_to_use)} direct connectors...")
143
151
  for connector in connectors_to_use:
144
- if not hasattr(connector, "client") or connector.client is None:
152
+ if not hasattr(connector, "client_session") or connector.client_session is None:
145
153
  await connector.connect()
146
154
 
147
155
  # Create LangChain tools using the adapter with connectors
@@ -180,9 +188,7 @@ class MCPAgent:
180
188
 
181
189
  # Update conversation history if memory is enabled
182
190
  if self.memory_enabled:
183
- history_without_system = [
184
- msg for msg in self._conversation_history if not isinstance(msg, SystemMessage)
185
- ]
191
+ history_without_system = [msg for msg in self._conversation_history if not isinstance(msg, SystemMessage)]
186
192
  self._conversation_history = [self._system_message] + history_without_system
187
193
 
188
194
  def _create_agent(self) -> AgentExecutor:
@@ -213,9 +219,7 @@ class MCPAgent:
213
219
  agent = create_tool_calling_agent(llm=self.llm, tools=self._tools, prompt=prompt)
214
220
 
215
221
  # Use the standard AgentExecutor
216
- executor = AgentExecutor(
217
- agent=agent, tools=self._tools, max_iterations=self.max_steps, verbose=self.verbose
218
- )
222
+ executor = AgentExecutor(agent=agent, tools=self._tools, max_iterations=self.max_steps, verbose=self.verbose)
219
223
  logger.debug(f"Created agent executor with max_iterations={self.max_steps}")
220
224
  return executor
221
225
 
@@ -263,9 +267,7 @@ class MCPAgent:
263
267
  # Update conversation history if memory is enabled
264
268
  if self.memory_enabled:
265
269
  # Remove old system message if it exists
266
- history_without_system = [
267
- msg for msg in self._conversation_history if not isinstance(msg, SystemMessage)
268
- ]
270
+ history_without_system = [msg for msg in self._conversation_history if not isinstance(msg, SystemMessage)]
269
271
  self._conversation_history = history_without_system
270
272
 
271
273
  # Add new system message
@@ -290,9 +292,7 @@ class MCPAgent:
290
292
  # If the agent is already initialized, we need to reinitialize it
291
293
  # to apply the changes to the available tools
292
294
  if self._initialized:
293
- logger.debug(
294
- "Agent already initialized. Changes will take effect on next initialization."
295
- )
295
+ logger.debug("Agent already initialized. Changes will take effect on next initialization.")
296
296
  # We don't automatically reinitialize here as it could be disruptive
297
297
  # to ongoing operations. The user can call initialize() explicitly if needed.
298
298
 
@@ -325,9 +325,7 @@ class MCPAgent:
325
325
 
326
326
  # 1. Initialise on-demand ------------------------------------------------
327
327
  initialised_here = False
328
- if (manage_connector and not self._initialized) or (
329
- not self._initialized and self.auto_initialize
330
- ):
328
+ if (manage_connector and not self._initialized) or (not self._initialized and self.auto_initialize):
331
329
  await self.initialize()
332
330
  initialised_here = True
333
331
 
@@ -341,9 +339,7 @@ class MCPAgent:
341
339
  if self.memory_enabled:
342
340
  self.add_to_history(HumanMessage(content=query))
343
341
 
344
- history_to_use = (
345
- external_history if external_history is not None else self._conversation_history
346
- )
342
+ history_to_use = external_history if external_history is not None else self._conversation_history
347
343
  inputs = {"input": query, "chat_history": history_to_use}
348
344
 
349
345
  # 3. Stream & diff -------------------------------------------------------
@@ -355,9 +351,12 @@ class MCPAgent:
355
351
  if not isinstance(message, ToolAgentAction):
356
352
  self.add_to_history(message)
357
353
  yield event
358
-
359
354
  # 5. House-keeping -------------------------------------------------------
360
- if initialised_here and manage_connector:
355
+ # Restrict agent cleanup in _generate_response_chunks_async to only occur
356
+ # when the agent was initialized in this generator and is not client-managed
357
+ # and the user does want us to manage the connection.
358
+ if not self.client and initialised_here and manage_connector:
359
+ logger.info("🧹 Closing agent after generator completion")
361
360
  await self.close()
362
361
 
363
362
  async def astream(
@@ -374,13 +373,56 @@ class MCPAgent:
374
373
  async for chunk in agent.astream("hello"):
375
374
  print(chunk, end="|", flush=True)
376
375
  """
377
- async for chunk in self._generate_response_chunks_async(
378
- query=query,
379
- max_steps=max_steps,
380
- manage_connector=manage_connector,
381
- external_history=external_history,
382
- ):
383
- yield chunk
376
+ start_time = time.time()
377
+ success = False
378
+ chunk_count = 0
379
+ total_response_length = 0
380
+
381
+ try:
382
+ async for chunk in self._generate_response_chunks_async(
383
+ query=query,
384
+ max_steps=max_steps,
385
+ manage_connector=manage_connector,
386
+ external_history=external_history,
387
+ ):
388
+ chunk_count += 1
389
+ if isinstance(chunk, str):
390
+ total_response_length += len(chunk)
391
+ yield chunk
392
+ success = True
393
+ finally:
394
+ # Track comprehensive execution data for streaming
395
+ execution_time_ms = int((time.time() - start_time) * 1000)
396
+
397
+ server_count = 0
398
+ if self.client:
399
+ server_count = len(self.client.get_all_active_sessions())
400
+ elif self.connectors:
401
+ server_count = len(self.connectors)
402
+
403
+ conversation_history_length = len(self._conversation_history) if self.memory_enabled else 0
404
+
405
+ self.telemetry.track_agent_execution(
406
+ execution_method="astream",
407
+ query=query,
408
+ success=success,
409
+ model_provider=self._model_provider,
410
+ model_name=self._model_name,
411
+ server_count=server_count,
412
+ server_identifiers=[connector.public_identifier for connector in self.connectors],
413
+ total_tools_available=len(self._tools) if self._tools else 0,
414
+ tools_available_names=[tool.name for tool in self._tools],
415
+ max_steps_configured=self.max_steps,
416
+ memory_enabled=self.memory_enabled,
417
+ use_server_manager=self.use_server_manager,
418
+ max_steps_used=max_steps,
419
+ manage_connector=manage_connector,
420
+ external_history_used=external_history is not None,
421
+ response=f"[STREAMED RESPONSE - {total_response_length} chars]",
422
+ execution_time_ms=execution_time_ms,
423
+ error_type=None if success else "streaming_error",
424
+ conversation_history_length=conversation_history_length,
425
+ )
384
426
 
385
427
  async def run(
386
428
  self,
@@ -409,6 +451,10 @@ class MCPAgent:
409
451
  """
410
452
  result = ""
411
453
  initialized_here = False
454
+ start_time = time.time()
455
+ tools_used_names = []
456
+ steps_taken = 0
457
+ success = False
412
458
 
413
459
  try:
414
460
  # Initialize if needed
@@ -427,11 +473,7 @@ class MCPAgent:
427
473
  if self._agent_executor:
428
474
  self._agent_executor.max_iterations = steps
429
475
 
430
- display_query = (
431
- query[:50].replace("\n", " ") + "..."
432
- if len(query) > 50
433
- else query.replace("\n", " ")
434
- )
476
+ display_query = query[:50].replace("\n", " ") + "..." if len(query) > 50 else query.replace("\n", " ")
435
477
  logger.info(f"💬 Received query: '{display_query}'")
436
478
 
437
479
  # Add the user query to conversation history if memory is enabled
@@ -439,9 +481,7 @@ class MCPAgent:
439
481
  self.add_to_history(HumanMessage(content=query))
440
482
 
441
483
  # Use the provided history or the internal history
442
- history_to_use = (
443
- external_history if external_history is not None else self._conversation_history
444
- )
484
+ history_to_use = external_history if external_history is not None else self._conversation_history
445
485
 
446
486
  # Convert messages to format expected by LangChain agent input
447
487
  # Exclude the main system message as it's part of the agent's prompt
@@ -457,13 +497,12 @@ class MCPAgent:
457
497
 
458
498
  # Construct a mapping of tool name to tool for easy lookup
459
499
  name_to_tool_map = {tool.name: tool for tool in self._tools}
460
- color_mapping = get_color_mapping(
461
- [tool.name for tool in self._tools], excluded_colors=["green", "red"]
462
- )
500
+ color_mapping = get_color_mapping([tool.name for tool in self._tools], excluded_colors=["green", "red"])
463
501
 
464
502
  logger.info(f"🏁 Starting agent execution with max_steps={steps}")
465
503
 
466
504
  for step_num in range(steps):
505
+ steps_taken = step_num + 1
467
506
  # --- Check for tool updates if using server manager ---
468
507
  if self.use_server_manager and self.server_manager:
469
508
  current_tools = self.server_manager.tools
@@ -472,7 +511,7 @@ class MCPAgent:
472
511
 
473
512
  if current_tool_names != existing_tool_names:
474
513
  logger.info(
475
- f"🔄 Tools changed before step {step_num + 1}, updating agent. "
514
+ f"🔄 Tools changed before step {step_num + 1}, updating agent."
476
515
  f"New tools: {', '.join(current_tool_names)}"
477
516
  )
478
517
  self._tools = current_tools
@@ -510,9 +549,10 @@ class MCPAgent:
510
549
  # If it's actions/steps, add to intermediate steps
511
550
  intermediate_steps.extend(next_step_output)
512
551
 
513
- # Log tool calls
552
+ # Log tool calls and track tool usage
514
553
  for action, output in next_step_output:
515
554
  tool_name = action.tool
555
+ tools_used_names.append(tool_name)
516
556
  tool_input_str = str(action.tool_input)
517
557
  # Truncate long inputs for readability
518
558
  if len(tool_input_str) > 100:
@@ -555,7 +595,8 @@ class MCPAgent:
555
595
  if self.memory_enabled:
556
596
  self.add_to_history(AIMessage(content=result))
557
597
 
558
- logger.info("🎉 Agent execution complete")
598
+ logger.info(f"🎉 Agent execution complete in {time.time() - start_time} seconds")
599
+ success = True
559
600
  return result
560
601
 
561
602
  except Exception as e:
@@ -566,6 +607,41 @@ class MCPAgent:
566
607
  raise
567
608
 
568
609
  finally:
610
+ # Track comprehensive execution data
611
+ execution_time_ms = int((time.time() - start_time) * 1000)
612
+
613
+ server_count = 0
614
+ if self.client:
615
+ server_count = len(self.client.get_all_active_sessions())
616
+ elif self.connectors:
617
+ server_count = len(self.connectors)
618
+
619
+ conversation_history_length = len(self._conversation_history) if self.memory_enabled else 0
620
+ self.telemetry.track_agent_execution(
621
+ execution_method="run",
622
+ query=query,
623
+ success=success,
624
+ model_provider=self._model_provider,
625
+ model_name=self._model_name,
626
+ server_count=server_count,
627
+ server_identifiers=[connector.public_identifier for connector in self.connectors],
628
+ total_tools_available=len(self._tools) if self._tools else 0,
629
+ tools_available_names=[tool.name for tool in self._tools],
630
+ max_steps_configured=self.max_steps,
631
+ memory_enabled=self.memory_enabled,
632
+ use_server_manager=self.use_server_manager,
633
+ max_steps_used=max_steps,
634
+ manage_connector=manage_connector,
635
+ external_history_used=external_history is not None,
636
+ steps_taken=steps_taken,
637
+ tools_used_count=len(tools_used_names),
638
+ tools_used_names=tools_used_names,
639
+ response=result,
640
+ execution_time_ms=execution_time_ms,
641
+ error_type=None if success else "execution_error",
642
+ conversation_history_length=conversation_history_length,
643
+ )
644
+
569
645
  # Clean up if necessary (e.g., if not using client-managed sessions)
570
646
  if manage_connector and not self.client and not initialized_here:
571
647
  logger.info("🧹 Closing agent after query completion")
@@ -2,9 +2,7 @@ from langchain.schema import SystemMessage
2
2
  from langchain_core.tools import BaseTool
3
3
 
4
4
 
5
- def generate_tool_descriptions(
6
- tools: list[BaseTool], disallowed_tools: list[str] | None = None
7
- ) -> list[str]:
5
+ def generate_tool_descriptions(tools: list[BaseTool], disallowed_tools: list[str] | None = None) -> list[str]:
8
6
  """
9
7
  Generates a list of formatted tool descriptions, excluding disallowed tools.
10
8
 
mcp_use/client.py CHANGED
@@ -9,10 +9,11 @@ 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
15
- from .types.clientoptions import ClientOptions
16
17
 
17
18
 
18
19
  class MCPClient:
@@ -25,17 +26,20 @@ class MCPClient:
25
26
  def __init__(
26
27
  self,
27
28
  config: str | dict[str, Any] | None = None,
28
- options: ClientOptions | None = None,
29
+ sandbox: bool = False,
30
+ sandbox_options: SandboxOptions | None = None,
29
31
  ) -> None:
30
32
  """Initialize a new MCP client.
31
33
 
32
34
  Args:
33
35
  config: Either a dict containing configuration or a path to a JSON config file.
34
36
  If None, an empty configuration is used.
35
- options: Configuration options for the client.
37
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
38
+ sandbox_options: Optional sandbox configuration options.
36
39
  """
37
40
  self.config: dict[str, Any] = {}
38
- self.options = options or {}
41
+ self.sandbox = sandbox
42
+ self.sandbox_options = sandbox_options
39
43
  self.sessions: dict[str, MCPSession] = {}
40
44
  self.active_sessions: list[str] = []
41
45
 
@@ -47,24 +51,33 @@ class MCPClient:
47
51
  self.config = config
48
52
 
49
53
  @classmethod
50
- def from_dict(cls, config: dict[str, Any], options: ClientOptions | None = None) -> "MCPClient":
54
+ def from_dict(
55
+ cls,
56
+ config: dict[str, Any],
57
+ sandbox: bool = False,
58
+ sandbox_options: SandboxOptions | None = None,
59
+ ) -> "MCPClient":
51
60
  """Create a MCPClient from a dictionary.
52
61
 
53
62
  Args:
54
63
  config: The configuration dictionary.
55
- options: Optional client configuration options.
64
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
65
+ sandbox_options: Optional sandbox configuration options.
56
66
  """
57
- return cls(config=config, options=options)
67
+ return cls(config=config, sandbox=sandbox, sandbox_options=sandbox_options)
58
68
 
59
69
  @classmethod
60
- def from_config_file(cls, filepath: str, options: ClientOptions | None = None) -> "MCPClient":
70
+ def from_config_file(
71
+ cls, filepath: str, sandbox: bool = False, sandbox_options: SandboxOptions | None = None
72
+ ) -> "MCPClient":
61
73
  """Create a MCPClient from a configuration file.
62
74
 
63
75
  Args:
64
76
  filepath: The path to the configuration file.
65
- options: Optional client configuration options.
77
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
78
+ sandbox_options: Optional sandbox configuration options.
66
79
  """
67
- return cls(config=load_config_file(filepath), options=options)
80
+ return cls(config=load_config_file(filepath), sandbox=sandbox, sandbox_options=sandbox_options)
68
81
 
69
82
  def add_server(
70
83
  self,
@@ -137,7 +150,9 @@ class MCPClient:
137
150
  server_config = servers[server_name]
138
151
 
139
152
  # Create connector with options
140
- connector = create_connector_from_config(server_config, options=self.options)
153
+ connector = create_connector_from_config(
154
+ server_config, sandbox=self.sandbox, sandbox_options=self.sandbox_options
155
+ )
141
156
 
142
157
  # Create the session
143
158
  session = MCPSession(connector)
mcp_use/config.py CHANGED
@@ -7,6 +7,8 @@ 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 mcp_use.types.sandbox import SandboxOptions
11
+
10
12
  from .connectors import (
11
13
  BaseConnector,
12
14
  HttpConnector,
@@ -15,7 +17,6 @@ from .connectors import (
15
17
  WebSocketConnector,
16
18
  )
17
19
  from .connectors.utils import is_stdio_server
18
- from .types.clientoptions import ClientOptions
19
20
 
20
21
 
21
22
  def load_config_file(filepath: str) -> dict[str, Any]:
@@ -33,24 +34,23 @@ def load_config_file(filepath: str) -> dict[str, Any]:
33
34
 
34
35
  def create_connector_from_config(
35
36
  server_config: dict[str, Any],
36
- options: ClientOptions | None = None,
37
+ sandbox: bool = False,
38
+ sandbox_options: SandboxOptions | None = None,
37
39
  ) -> BaseConnector:
38
40
  """Create a connector based on server configuration.
39
41
  This function can be called with just the server_config parameter:
40
42
  create_connector_from_config(server_config)
41
43
  Args:
42
44
  server_config: The server configuration section
43
- options: Optional client configuration options including sandboxing preferences.
44
- If None, default client options will be used.
45
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
46
+ sandbox_options: Optional sandbox configuration options.
45
47
 
46
48
  Returns:
47
49
  A configured connector instance
48
50
  """
49
- # Use default options if none provided
50
- options = options or {"is_sandboxed": False}
51
51
 
52
52
  # Stdio connector (command-based)
53
- if is_stdio_server(server_config) and not options.get("is_sandboxed", False):
53
+ if is_stdio_server(server_config) and not sandbox:
54
54
  return StdioConnector(
55
55
  command=server_config["command"],
56
56
  args=server_config["args"],
@@ -58,12 +58,12 @@ def create_connector_from_config(
58
58
  )
59
59
 
60
60
  # Sandboxed connector
61
- elif is_stdio_server(server_config) and options.get("is_sandboxed", False):
61
+ elif is_stdio_server(server_config) and sandbox:
62
62
  return SandboxConnector(
63
63
  command=server_config["command"],
64
64
  args=server_config["args"],
65
65
  env=server_config.get("env", None),
66
- e2b_options=options.get("sandbox_options", {}),
66
+ e2b_options=sandbox_options,
67
67
  )
68
68
 
69
69
  # HTTP connector