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.
- mcp_use/agents/mcpagent.py +117 -22
- mcp_use/client.py +35 -9
- mcp_use/config.py +30 -4
- mcp_use/connectors/__init__.py +12 -5
- mcp_use/connectors/base.py +135 -37
- mcp_use/connectors/http.py +108 -30
- mcp_use/connectors/sandbox.py +296 -0
- mcp_use/connectors/stdio.py +7 -2
- mcp_use/connectors/utils.py +13 -0
- mcp_use/connectors/websocket.py +7 -2
- mcp_use/session.py +1 -4
- mcp_use/task_managers/__init__.py +2 -1
- mcp_use/task_managers/base.py +10 -4
- mcp_use/task_managers/streamable_http.py +81 -0
- mcp_use/task_managers/websocket.py +5 -0
- mcp_use/telemetry/__init__.py +0 -0
- mcp_use/telemetry/events.py +93 -0
- mcp_use/telemetry/posthog.py +214 -0
- mcp_use/telemetry/utils.py +48 -0
- mcp_use/types/sandbox.py +23 -0
- mcp_use/utils.py +27 -0
- {mcp_use-1.2.13.dist-info → mcp_use-1.3.1.dist-info}/METADATA +209 -32
- mcp_use-1.3.1.dist-info/RECORD +46 -0
- mcp_use-1.2.13.dist-info/RECORD +0 -37
- {mcp_use-1.2.13.dist-info → mcp_use-1.3.1.dist-info}/WHEEL +0 -0
- {mcp_use-1.2.13.dist-info → mcp_use-1.3.1.dist-info}/licenses/LICENSE +0 -0
mcp_use/agents/mcpagent.py
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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, "
|
|
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
|
-
|
|
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
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
175
|
+
"""Create sessions for all configured servers.
|
|
150
176
|
|
|
151
177
|
Args:
|
|
152
|
-
auto_initialize: Whether to automatically initialize the
|
|
178
|
+
auto_initialize: Whether to automatically initialize the sessions.
|
|
153
179
|
|
|
154
180
|
Returns:
|
|
155
|
-
|
|
181
|
+
Dictionary mapping server names to their MCPSession instances.
|
|
156
182
|
|
|
157
183
|
Warns:
|
|
158
|
-
|
|
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 .
|
|
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(
|
|
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
|
|
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(
|
mcp_use/connectors/__init__.py
CHANGED
|
@@ -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 .
|
|
11
|
-
from .
|
|
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__ = [
|
|
14
|
+
__all__ = [
|
|
15
|
+
"BaseConnector",
|
|
16
|
+
"StdioConnector",
|
|
17
|
+
"HttpConnector",
|
|
18
|
+
"WebSocketConnector",
|
|
19
|
+
"SandboxConnector",
|
|
20
|
+
]
|