mcp-use 1.3.8__py3-none-any.whl → 1.3.9__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 +0 -1
- mcp_use/adapters/base.py +3 -3
- mcp_use/adapters/langchain_adapter.py +5 -4
- mcp_use/agents/remote.py +67 -10
- mcp_use/client.py +15 -1
- mcp_use/config.py +10 -8
- mcp_use/connectors/base.py +100 -15
- mcp_use/connectors/http.py +13 -2
- mcp_use/connectors/sandbox.py +12 -6
- mcp_use/connectors/stdio.py +11 -2
- mcp_use/errors/__init__.py +1 -0
- mcp_use/errors/error_formatting.py +29 -0
- mcp_use/managers/server_manager.py +1 -7
- mcp_use/managers/tools/connect_server.py +2 -1
- mcp_use/managers/tools/disconnect_server.py +2 -1
- mcp_use/managers/tools/list_servers_tool.py +2 -0
- mcp_use/session.py +70 -0
- mcp_use/telemetry/telemetry.py +1 -4
- {mcp_use-1.3.8.dist-info → mcp_use-1.3.9.dist-info}/METADATA +67 -43
- {mcp_use-1.3.8.dist-info → mcp_use-1.3.9.dist-info}/RECORD +22 -20
- {mcp_use-1.3.8.dist-info → mcp_use-1.3.9.dist-info}/WHEEL +0 -0
- {mcp_use-1.3.8.dist-info → mcp_use-1.3.9.dist-info}/licenses/LICENSE +0 -0
mcp_use/__init__.py
CHANGED
mcp_use/adapters/base.py
CHANGED
|
@@ -91,14 +91,14 @@ class BaseAdapter(ABC):
|
|
|
91
91
|
|
|
92
92
|
connector_tools = []
|
|
93
93
|
# Now create tools for each MCP tool
|
|
94
|
-
for tool in connector.
|
|
94
|
+
for tool in await connector.list_tools():
|
|
95
95
|
# Convert the tool and add it to the list if conversion was successful
|
|
96
96
|
converted_tool = self._convert_tool(tool, connector)
|
|
97
97
|
if converted_tool:
|
|
98
98
|
connector_tools.append(converted_tool)
|
|
99
99
|
|
|
100
100
|
# Convert resources to tools so that agents can access resource content directly
|
|
101
|
-
resources_list = connector.
|
|
101
|
+
resources_list = await connector.list_resources() or []
|
|
102
102
|
if resources_list:
|
|
103
103
|
for resource in resources_list:
|
|
104
104
|
converted_resource = self._convert_resource(resource, connector)
|
|
@@ -106,7 +106,7 @@ class BaseAdapter(ABC):
|
|
|
106
106
|
connector_tools.append(converted_resource)
|
|
107
107
|
|
|
108
108
|
# Convert prompts to tools so that agents can retrieve prompt content
|
|
109
|
-
prompts_list = connector.
|
|
109
|
+
prompts_list = await connector.list_prompts() or []
|
|
110
110
|
if prompts_list:
|
|
111
111
|
for prompt in prompts_list:
|
|
112
112
|
converted_prompt = self._convert_prompt(prompt, connector)
|
|
@@ -21,6 +21,7 @@ from mcp.types import (
|
|
|
21
21
|
from pydantic import BaseModel, Field, create_model
|
|
22
22
|
|
|
23
23
|
from ..connectors.base import BaseConnector
|
|
24
|
+
from ..errors.error_formatting import format_error
|
|
24
25
|
from ..logging import logger
|
|
25
26
|
from .base import BaseAdapter
|
|
26
27
|
|
|
@@ -159,11 +160,11 @@ class LangChainAdapter(BaseAdapter):
|
|
|
159
160
|
except Exception as e:
|
|
160
161
|
# Log the exception for debugging
|
|
161
162
|
logger.error(f"Error parsing tool result: {e}")
|
|
162
|
-
return
|
|
163
|
+
return format_error(e, tool=self.name, tool_content=tool_result.content)
|
|
163
164
|
|
|
164
165
|
except Exception as e:
|
|
165
166
|
if self.handle_tool_error:
|
|
166
|
-
return
|
|
167
|
+
return format_error(e, tool=self.name) # Format the error to make LLM understand it
|
|
167
168
|
raise
|
|
168
169
|
|
|
169
170
|
return McpToLangChainAdapter()
|
|
@@ -204,7 +205,7 @@ class LangChainAdapter(BaseAdapter):
|
|
|
204
205
|
return content_decoded
|
|
205
206
|
except Exception as e:
|
|
206
207
|
if self.handle_tool_error:
|
|
207
|
-
return
|
|
208
|
+
return format_error(e, tool=self.name) # Format the error to make LLM understand it
|
|
208
209
|
raise
|
|
209
210
|
|
|
210
211
|
return ResourceTool()
|
|
@@ -261,7 +262,7 @@ class LangChainAdapter(BaseAdapter):
|
|
|
261
262
|
return result.messages
|
|
262
263
|
except Exception as e:
|
|
263
264
|
if self.handle_tool_error:
|
|
264
|
-
return
|
|
265
|
+
return format_error(e, tool=self.name) # Format the error to make LLM understand it
|
|
265
266
|
raise
|
|
266
267
|
|
|
267
268
|
return PromptTool()
|
mcp_use/agents/remote.py
CHANGED
|
@@ -14,6 +14,11 @@ from ..logging import logger
|
|
|
14
14
|
|
|
15
15
|
T = TypeVar("T", bound=BaseModel)
|
|
16
16
|
|
|
17
|
+
# API endpoint constants
|
|
18
|
+
API_CHATS_ENDPOINT = "/api/v1/chats"
|
|
19
|
+
API_CHAT_EXECUTE_ENDPOINT = "/api/v1/chats/{chat_id}/execute"
|
|
20
|
+
API_CHAT_DELETE_ENDPOINT = "/api/v1/chats/{chat_id}"
|
|
21
|
+
|
|
17
22
|
|
|
18
23
|
class RemoteAgent:
|
|
19
24
|
"""Agent that executes remotely via API."""
|
|
@@ -28,6 +33,7 @@ class RemoteAgent:
|
|
|
28
33
|
"""
|
|
29
34
|
self.agent_id = agent_id
|
|
30
35
|
self.base_url = base_url
|
|
36
|
+
self._chat_id = None # Persistent chat session
|
|
31
37
|
|
|
32
38
|
# Handle API key validation
|
|
33
39
|
if api_key is None:
|
|
@@ -103,6 +109,49 @@ class RemoteAgent:
|
|
|
103
109
|
return output_schema.model_validate({"content": str(result_data)})
|
|
104
110
|
raise
|
|
105
111
|
|
|
112
|
+
async def _create_chat_session(self, query: str) -> str:
|
|
113
|
+
"""Create a persistent chat session for the agent.
|
|
114
|
+
Args:
|
|
115
|
+
query: The initial query (not used in title anymore)
|
|
116
|
+
Returns:
|
|
117
|
+
The chat ID of the created session
|
|
118
|
+
Raises:
|
|
119
|
+
RuntimeError: If chat creation fails
|
|
120
|
+
"""
|
|
121
|
+
chat_payload = {
|
|
122
|
+
"title": f"Remote Agent Session - {self.agent_id}",
|
|
123
|
+
"agent_id": self.agent_id,
|
|
124
|
+
"type": "agent_execution",
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
headers = {"Content-Type": "application/json", "x-api-key": self.api_key}
|
|
128
|
+
chat_url = f"{self.base_url}{API_CHATS_ENDPOINT}"
|
|
129
|
+
|
|
130
|
+
logger.info(f"📝 Creating chat session for agent {self.agent_id}")
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
chat_response = await self._client.post(chat_url, json=chat_payload, headers=headers)
|
|
134
|
+
chat_response.raise_for_status()
|
|
135
|
+
|
|
136
|
+
chat_data = chat_response.json()
|
|
137
|
+
chat_id = chat_data["id"]
|
|
138
|
+
logger.info(f"✅ Chat session created: {chat_id}")
|
|
139
|
+
return chat_id
|
|
140
|
+
|
|
141
|
+
except httpx.HTTPStatusError as e:
|
|
142
|
+
status_code = e.response.status_code
|
|
143
|
+
response_text = e.response.text
|
|
144
|
+
|
|
145
|
+
if status_code == 404:
|
|
146
|
+
raise RuntimeError(
|
|
147
|
+
f"Agent not found: Agent '{self.agent_id}' does not exist or you don't have access to it. "
|
|
148
|
+
"Please verify the agent ID and ensure it exists in your account."
|
|
149
|
+
) from e
|
|
150
|
+
else:
|
|
151
|
+
raise RuntimeError(f"Failed to create chat session: {status_code} - {response_text}") from e
|
|
152
|
+
except Exception as e:
|
|
153
|
+
raise RuntimeError(f"Failed to create chat session: {str(e)}") from e
|
|
154
|
+
|
|
106
155
|
async def run(
|
|
107
156
|
self,
|
|
108
157
|
query: str,
|
|
@@ -126,20 +175,28 @@ class RemoteAgent:
|
|
|
126
175
|
if external_history is not None:
|
|
127
176
|
logger.warning("External history is not yet supported for remote execution")
|
|
128
177
|
|
|
129
|
-
|
|
178
|
+
try:
|
|
179
|
+
logger.info(f"🌐 Executing query on remote agent {self.agent_id}")
|
|
130
180
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
logger.info(f"🔧 Using structured output with schema: {output_schema.__name__}")
|
|
181
|
+
# Step 1: Create a chat session for this agent (only if we don't have one)
|
|
182
|
+
if self._chat_id is None:
|
|
183
|
+
self._chat_id = await self._create_chat_session(query)
|
|
135
184
|
|
|
136
|
-
|
|
185
|
+
chat_id = self._chat_id
|
|
137
186
|
|
|
138
|
-
|
|
187
|
+
# Step 2: Execute the agent within the chat context
|
|
188
|
+
execution_payload = {"query": query, "max_steps": max_steps or 10}
|
|
139
189
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
190
|
+
# Add structured output schema if provided
|
|
191
|
+
if output_schema is not None:
|
|
192
|
+
execution_payload["output_schema"] = self._pydantic_to_json_schema(output_schema)
|
|
193
|
+
logger.info(f"🔧 Using structured output with schema: {output_schema.__name__}")
|
|
194
|
+
|
|
195
|
+
headers = {"Content-Type": "application/json", "x-api-key": self.api_key}
|
|
196
|
+
execution_url = f"{self.base_url}{API_CHAT_EXECUTE_ENDPOINT.format(chat_id=chat_id)}"
|
|
197
|
+
logger.info(f"🚀 Executing agent in chat {chat_id}")
|
|
198
|
+
|
|
199
|
+
response = await self._client.post(execution_url, json=execution_payload, headers=headers)
|
|
143
200
|
response.raise_for_status()
|
|
144
201
|
|
|
145
202
|
result = response.json()
|
mcp_use/client.py
CHANGED
|
@@ -9,7 +9,7 @@ import json
|
|
|
9
9
|
import warnings
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
|
-
from mcp.client.session import ElicitationFnT, SamplingFnT
|
|
12
|
+
from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
|
|
13
13
|
|
|
14
14
|
from mcp_use.types.sandbox import SandboxOptions
|
|
15
15
|
|
|
@@ -33,6 +33,8 @@ class MCPClient:
|
|
|
33
33
|
sandbox_options: SandboxOptions | None = None,
|
|
34
34
|
sampling_callback: SamplingFnT | None = None,
|
|
35
35
|
elicitation_callback: ElicitationFnT | None = None,
|
|
36
|
+
message_handler: MessageHandlerFnT | None = None,
|
|
37
|
+
logging_callback: LoggingFnT | None = None,
|
|
36
38
|
) -> None:
|
|
37
39
|
"""Initialize a new MCP client.
|
|
38
40
|
|
|
@@ -51,6 +53,8 @@ class MCPClient:
|
|
|
51
53
|
self.active_sessions: list[str] = []
|
|
52
54
|
self.sampling_callback = sampling_callback
|
|
53
55
|
self.elicitation_callback = elicitation_callback
|
|
56
|
+
self.message_handler = message_handler
|
|
57
|
+
self.logging_callback = logging_callback
|
|
54
58
|
# Load configuration if provided
|
|
55
59
|
if config is not None:
|
|
56
60
|
if isinstance(config, str):
|
|
@@ -66,6 +70,8 @@ class MCPClient:
|
|
|
66
70
|
sandbox_options: SandboxOptions | None = None,
|
|
67
71
|
sampling_callback: SamplingFnT | None = None,
|
|
68
72
|
elicitation_callback: ElicitationFnT | None = None,
|
|
73
|
+
message_handler: MessageHandlerFnT | None = None,
|
|
74
|
+
logging_callback: LoggingFnT | None = None,
|
|
69
75
|
) -> "MCPClient":
|
|
70
76
|
"""Create a MCPClient from a dictionary.
|
|
71
77
|
|
|
@@ -82,6 +88,8 @@ class MCPClient:
|
|
|
82
88
|
sandbox_options=sandbox_options,
|
|
83
89
|
sampling_callback=sampling_callback,
|
|
84
90
|
elicitation_callback=elicitation_callback,
|
|
91
|
+
message_handler=message_handler,
|
|
92
|
+
logging_callback=logging_callback,
|
|
85
93
|
)
|
|
86
94
|
|
|
87
95
|
@classmethod
|
|
@@ -92,6 +100,8 @@ class MCPClient:
|
|
|
92
100
|
sandbox_options: SandboxOptions | None = None,
|
|
93
101
|
sampling_callback: SamplingFnT | None = None,
|
|
94
102
|
elicitation_callback: ElicitationFnT | None = None,
|
|
103
|
+
message_handler: MessageHandlerFnT | None = None,
|
|
104
|
+
logging_callback: LoggingFnT | None = None,
|
|
95
105
|
) -> "MCPClient":
|
|
96
106
|
"""Create a MCPClient from a configuration file.
|
|
97
107
|
|
|
@@ -108,6 +118,8 @@ class MCPClient:
|
|
|
108
118
|
sandbox_options=sandbox_options,
|
|
109
119
|
sampling_callback=sampling_callback,
|
|
110
120
|
elicitation_callback=elicitation_callback,
|
|
121
|
+
message_handler=message_handler,
|
|
122
|
+
logging_callback=logging_callback,
|
|
111
123
|
)
|
|
112
124
|
|
|
113
125
|
def add_server(
|
|
@@ -187,6 +199,8 @@ class MCPClient:
|
|
|
187
199
|
sandbox_options=self.sandbox_options,
|
|
188
200
|
sampling_callback=self.sampling_callback,
|
|
189
201
|
elicitation_callback=self.elicitation_callback,
|
|
202
|
+
message_handler=self.message_handler,
|
|
203
|
+
logging_callback=self.logging_callback,
|
|
190
204
|
)
|
|
191
205
|
|
|
192
206
|
# Create the session
|
mcp_use/config.py
CHANGED
|
@@ -7,17 +7,11 @@ 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.client.session import ElicitationFnT, SamplingFnT
|
|
10
|
+
from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
|
|
11
11
|
|
|
12
12
|
from mcp_use.types.sandbox import SandboxOptions
|
|
13
13
|
|
|
14
|
-
from .connectors import
|
|
15
|
-
BaseConnector,
|
|
16
|
-
HttpConnector,
|
|
17
|
-
SandboxConnector,
|
|
18
|
-
StdioConnector,
|
|
19
|
-
WebSocketConnector,
|
|
20
|
-
)
|
|
14
|
+
from .connectors import BaseConnector, HttpConnector, SandboxConnector, StdioConnector, WebSocketConnector
|
|
21
15
|
from .connectors.utils import is_stdio_server
|
|
22
16
|
|
|
23
17
|
|
|
@@ -40,6 +34,8 @@ def create_connector_from_config(
|
|
|
40
34
|
sandbox_options: SandboxOptions | None = None,
|
|
41
35
|
sampling_callback: SamplingFnT | None = None,
|
|
42
36
|
elicitation_callback: ElicitationFnT | None = None,
|
|
37
|
+
message_handler: MessageHandlerFnT | None = None,
|
|
38
|
+
logging_callback: LoggingFnT | None = None,
|
|
43
39
|
) -> BaseConnector:
|
|
44
40
|
"""Create a connector based on server configuration.
|
|
45
41
|
This function can be called with just the server_config parameter:
|
|
@@ -61,6 +57,8 @@ def create_connector_from_config(
|
|
|
61
57
|
env=server_config.get("env", None),
|
|
62
58
|
sampling_callback=sampling_callback,
|
|
63
59
|
elicitation_callback=elicitation_callback,
|
|
60
|
+
message_handler=message_handler,
|
|
61
|
+
logging_callback=logging_callback,
|
|
64
62
|
)
|
|
65
63
|
|
|
66
64
|
# Sandboxed connector
|
|
@@ -72,6 +70,8 @@ def create_connector_from_config(
|
|
|
72
70
|
e2b_options=sandbox_options,
|
|
73
71
|
sampling_callback=sampling_callback,
|
|
74
72
|
elicitation_callback=elicitation_callback,
|
|
73
|
+
message_handler=message_handler,
|
|
74
|
+
logging_callback=logging_callback,
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
# HTTP connector
|
|
@@ -84,6 +84,8 @@ def create_connector_from_config(
|
|
|
84
84
|
sse_read_timeout=server_config.get("sse_read_timeout", 60 * 5),
|
|
85
85
|
sampling_callback=sampling_callback,
|
|
86
86
|
elicitation_callback=elicitation_callback,
|
|
87
|
+
message_handler=message_handler,
|
|
88
|
+
logging_callback=logging_callback,
|
|
87
89
|
)
|
|
88
90
|
|
|
89
91
|
# WebSocket connector
|
mcp_use/connectors/base.py
CHANGED
|
@@ -5,14 +5,27 @@ This module provides the base connector interface that all MCP connectors
|
|
|
5
5
|
must implement.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import warnings
|
|
8
9
|
from abc import ABC, abstractmethod
|
|
9
10
|
from datetime import timedelta
|
|
10
11
|
from typing import Any
|
|
11
12
|
|
|
12
13
|
from mcp import ClientSession, Implementation
|
|
13
|
-
from mcp.client.session import ElicitationFnT, SamplingFnT
|
|
14
|
+
from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
|
|
14
15
|
from mcp.shared.exceptions import McpError
|
|
15
|
-
from mcp.types import
|
|
16
|
+
from mcp.types import (
|
|
17
|
+
CallToolResult,
|
|
18
|
+
GetPromptResult,
|
|
19
|
+
Prompt,
|
|
20
|
+
PromptListChangedNotification,
|
|
21
|
+
ReadResourceResult,
|
|
22
|
+
Resource,
|
|
23
|
+
ResourceListChangedNotification,
|
|
24
|
+
ServerCapabilities,
|
|
25
|
+
ServerNotification,
|
|
26
|
+
Tool,
|
|
27
|
+
ToolListChangedNotification,
|
|
28
|
+
)
|
|
16
29
|
from pydantic import AnyUrl
|
|
17
30
|
|
|
18
31
|
import mcp_use
|
|
@@ -31,6 +44,8 @@ class BaseConnector(ABC):
|
|
|
31
44
|
self,
|
|
32
45
|
sampling_callback: SamplingFnT | None = None,
|
|
33
46
|
elicitation_callback: ElicitationFnT | None = None,
|
|
47
|
+
message_handler: MessageHandlerFnT | None = None,
|
|
48
|
+
logging_callback: LoggingFnT | None = None,
|
|
34
49
|
):
|
|
35
50
|
"""Initialize base connector with common attributes."""
|
|
36
51
|
self.client_session: ClientSession | None = None
|
|
@@ -43,6 +58,9 @@ class BaseConnector(ABC):
|
|
|
43
58
|
self.auto_reconnect = True # Whether to automatically reconnect on connection loss (not configurable for now)
|
|
44
59
|
self.sampling_callback = sampling_callback
|
|
45
60
|
self.elicitation_callback = elicitation_callback
|
|
61
|
+
self.message_handler = message_handler
|
|
62
|
+
self.logging_callback = logging_callback
|
|
63
|
+
self.capabilities: ServerCapabilities | None = None
|
|
46
64
|
|
|
47
65
|
@property
|
|
48
66
|
def client_info(self) -> Implementation:
|
|
@@ -53,6 +71,20 @@ class BaseConnector(ABC):
|
|
|
53
71
|
url="https://github.com/mcp-use/mcp-use",
|
|
54
72
|
)
|
|
55
73
|
|
|
74
|
+
async def _internal_message_handler(self, message: Any) -> None:
|
|
75
|
+
"""Wrap the user-provided message handler."""
|
|
76
|
+
if isinstance(message, ServerNotification):
|
|
77
|
+
if isinstance(message.root, ToolListChangedNotification):
|
|
78
|
+
logger.debug("Received tool list changed notification")
|
|
79
|
+
elif isinstance(message.root, ResourceListChangedNotification):
|
|
80
|
+
logger.debug("Received resource list changed notification")
|
|
81
|
+
elif isinstance(message.root, PromptListChangedNotification):
|
|
82
|
+
logger.debug("Received prompt list changed notification")
|
|
83
|
+
|
|
84
|
+
# Call the user's handler
|
|
85
|
+
if self.message_handler:
|
|
86
|
+
await self.message_handler(message)
|
|
87
|
+
|
|
56
88
|
@abstractmethod
|
|
57
89
|
async def connect(self) -> None:
|
|
58
90
|
"""Establish a connection to the MCP implementation."""
|
|
@@ -125,37 +157,37 @@ class BaseConnector(ABC):
|
|
|
125
157
|
result = await self.client_session.initialize()
|
|
126
158
|
self._initialized = True # Mark as initialized
|
|
127
159
|
|
|
128
|
-
|
|
160
|
+
self.capabilities = result.capabilities
|
|
129
161
|
|
|
130
|
-
if
|
|
162
|
+
if self.capabilities.tools:
|
|
131
163
|
# Get available tools directly from client session
|
|
132
164
|
try:
|
|
133
165
|
tools_result = await self.client_session.list_tools()
|
|
134
166
|
self._tools = tools_result.tools if tools_result else []
|
|
135
167
|
except Exception as e:
|
|
136
|
-
logger.error(f"Error listing tools: {e}")
|
|
168
|
+
logger.error(f"Error listing tools for connector {self.public_identifier}: {e}")
|
|
137
169
|
self._tools = []
|
|
138
170
|
else:
|
|
139
171
|
self._tools = []
|
|
140
172
|
|
|
141
|
-
if
|
|
173
|
+
if self.capabilities.resources:
|
|
142
174
|
# Get available resources directly from client session
|
|
143
175
|
try:
|
|
144
176
|
resources_result = await self.client_session.list_resources()
|
|
145
177
|
self._resources = resources_result.resources if resources_result else []
|
|
146
178
|
except Exception as e:
|
|
147
|
-
logger.error(f"Error listing resources: {e}")
|
|
179
|
+
logger.error(f"Error listing resources for connector {self.public_identifier}: {e}")
|
|
148
180
|
self._resources = []
|
|
149
181
|
else:
|
|
150
182
|
self._resources = []
|
|
151
183
|
|
|
152
|
-
if
|
|
184
|
+
if self.capabilities.prompts:
|
|
153
185
|
# Get available prompts directly from client session
|
|
154
186
|
try:
|
|
155
187
|
prompts_result = await self.client_session.list_prompts()
|
|
156
188
|
self._prompts = prompts_result.prompts if prompts_result else []
|
|
157
189
|
except Exception as e:
|
|
158
|
-
logger.error(f"Error listing prompts: {e}")
|
|
190
|
+
logger.error(f"Error listing prompts for connector {self.public_identifier}: {e}")
|
|
159
191
|
self._prompts = []
|
|
160
192
|
else:
|
|
161
193
|
self._prompts = []
|
|
@@ -170,21 +202,57 @@ class BaseConnector(ABC):
|
|
|
170
202
|
|
|
171
203
|
@property
|
|
172
204
|
def tools(self) -> list[Tool]:
|
|
173
|
-
"""Get the list of available tools.
|
|
205
|
+
"""Get the list of available tools.
|
|
206
|
+
|
|
207
|
+
.. deprecated::
|
|
208
|
+
This property is deprecated because it may return stale data when the server
|
|
209
|
+
sends list change notifications. Use `await list_tools()` instead to ensure
|
|
210
|
+
you always get the latest data.
|
|
211
|
+
"""
|
|
212
|
+
warnings.warn(
|
|
213
|
+
"The 'tools' property is deprecated and may return stale data. "
|
|
214
|
+
"Use 'await list_tools()' instead to ensure fresh data.",
|
|
215
|
+
DeprecationWarning,
|
|
216
|
+
stacklevel=2,
|
|
217
|
+
)
|
|
174
218
|
if self._tools is None:
|
|
175
219
|
raise RuntimeError("MCP client is not initialized")
|
|
176
220
|
return self._tools
|
|
177
221
|
|
|
178
222
|
@property
|
|
179
223
|
def resources(self) -> list[Resource]:
|
|
180
|
-
"""Get the list of available resources.
|
|
224
|
+
"""Get the list of available resources.
|
|
225
|
+
|
|
226
|
+
.. deprecated::
|
|
227
|
+
This property is deprecated because it may return stale data when the server
|
|
228
|
+
sends list change notifications. Use `await list_resources()` instead to ensure
|
|
229
|
+
you always get the latest data.
|
|
230
|
+
"""
|
|
231
|
+
warnings.warn(
|
|
232
|
+
"The 'resources' property is deprecated and may return stale data. "
|
|
233
|
+
"Use 'await list_resources()' instead to ensure fresh data.",
|
|
234
|
+
DeprecationWarning,
|
|
235
|
+
stacklevel=2,
|
|
236
|
+
)
|
|
181
237
|
if self._resources is None:
|
|
182
238
|
raise RuntimeError("MCP client is not initialized")
|
|
183
239
|
return self._resources
|
|
184
240
|
|
|
185
241
|
@property
|
|
186
242
|
def prompts(self) -> list[Prompt]:
|
|
187
|
-
"""Get the list of available prompts.
|
|
243
|
+
"""Get the list of available prompts.
|
|
244
|
+
|
|
245
|
+
.. deprecated::
|
|
246
|
+
This property is deprecated because it may return stale data when the server
|
|
247
|
+
sends list change notifications. Use `await list_prompts()' instead to ensure
|
|
248
|
+
you always get the latest data.
|
|
249
|
+
"""
|
|
250
|
+
warnings.warn(
|
|
251
|
+
"The 'prompts' property is deprecated and may return stale data. "
|
|
252
|
+
"Use 'await list_prompts()' instead to ensure fresh data.",
|
|
253
|
+
DeprecationWarning,
|
|
254
|
+
stacklevel=2,
|
|
255
|
+
)
|
|
188
256
|
if self._prompts is None:
|
|
189
257
|
raise RuntimeError("MCP client is not initialized")
|
|
190
258
|
return self._prompts
|
|
@@ -303,28 +371,39 @@ class BaseConnector(ABC):
|
|
|
303
371
|
async def list_tools(self) -> list[Tool]:
|
|
304
372
|
"""List all available tools from the MCP implementation."""
|
|
305
373
|
|
|
374
|
+
if self.capabilities and not self.capabilities.tools:
|
|
375
|
+
logger.debug(f"Server {self.public_identifier} does not support tools")
|
|
376
|
+
return []
|
|
377
|
+
|
|
306
378
|
# Ensure we're connected
|
|
307
379
|
await self._ensure_connected()
|
|
308
380
|
|
|
309
381
|
logger.debug("Listing tools")
|
|
310
382
|
try:
|
|
311
383
|
result = await self.client_session.list_tools()
|
|
384
|
+
self._tools = result.tools
|
|
312
385
|
return result.tools
|
|
313
386
|
except McpError as e:
|
|
314
|
-
logger.error(f"Error listing tools: {e}")
|
|
387
|
+
logger.error(f"Error listing tools for connector {self.public_identifier}: {e}")
|
|
315
388
|
return []
|
|
316
389
|
|
|
317
390
|
async def list_resources(self) -> list[Resource]:
|
|
318
391
|
"""List all available resources from the MCP implementation."""
|
|
392
|
+
|
|
393
|
+
if self.capabilities and not self.capabilities.resources:
|
|
394
|
+
logger.debug(f"Server {self.public_identifier} does not support resources")
|
|
395
|
+
return []
|
|
396
|
+
|
|
319
397
|
# Ensure we're connected
|
|
320
398
|
await self._ensure_connected()
|
|
321
399
|
|
|
322
400
|
logger.debug("Listing resources")
|
|
323
401
|
try:
|
|
324
402
|
result = await self.client_session.list_resources()
|
|
403
|
+
self._resources = result.resources
|
|
325
404
|
return result.resources
|
|
326
405
|
except McpError as e:
|
|
327
|
-
logger.
|
|
406
|
+
logger.warning(f"Error listing resources for connector {self.public_identifier}: {e}")
|
|
328
407
|
return []
|
|
329
408
|
|
|
330
409
|
async def read_resource(self, uri: AnyUrl) -> ReadResourceResult:
|
|
@@ -337,14 +416,20 @@ class BaseConnector(ABC):
|
|
|
337
416
|
|
|
338
417
|
async def list_prompts(self) -> list[Prompt]:
|
|
339
418
|
"""List all available prompts from the MCP implementation."""
|
|
419
|
+
|
|
420
|
+
if self.capabilities and not self.capabilities.prompts:
|
|
421
|
+
logger.debug(f"Server {self.public_identifier} does not support prompts")
|
|
422
|
+
return []
|
|
423
|
+
|
|
340
424
|
await self._ensure_connected()
|
|
341
425
|
|
|
342
426
|
logger.debug("Listing prompts")
|
|
343
427
|
try:
|
|
344
428
|
result = await self.client_session.list_prompts()
|
|
429
|
+
self._prompts = result.prompts
|
|
345
430
|
return result.prompts
|
|
346
431
|
except McpError as e:
|
|
347
|
-
logger.error(f"Error listing prompts: {e}")
|
|
432
|
+
logger.error(f"Error listing prompts for connector {self.public_identifier}: {e}")
|
|
348
433
|
return []
|
|
349
434
|
|
|
350
435
|
async def get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> GetPromptResult:
|
mcp_use/connectors/http.py
CHANGED
|
@@ -7,7 +7,7 @@ through HTTP APIs with SSE or Streamable HTTP for transport.
|
|
|
7
7
|
|
|
8
8
|
import httpx
|
|
9
9
|
from mcp import ClientSession
|
|
10
|
-
from mcp.client.session import ElicitationFnT, SamplingFnT
|
|
10
|
+
from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
|
|
11
11
|
|
|
12
12
|
from ..logging import logger
|
|
13
13
|
from ..task_managers import SseConnectionManager, StreamableHttpConnectionManager
|
|
@@ -30,6 +30,8 @@ class HttpConnector(BaseConnector):
|
|
|
30
30
|
sse_read_timeout: float = 60 * 5,
|
|
31
31
|
sampling_callback: SamplingFnT | None = None,
|
|
32
32
|
elicitation_callback: ElicitationFnT | None = None,
|
|
33
|
+
message_handler: MessageHandlerFnT | None = None,
|
|
34
|
+
logging_callback: LoggingFnT | None = None,
|
|
33
35
|
):
|
|
34
36
|
"""Initialize a new HTTP connector.
|
|
35
37
|
|
|
@@ -42,7 +44,12 @@ class HttpConnector(BaseConnector):
|
|
|
42
44
|
sampling_callback: Optional sampling callback.
|
|
43
45
|
elicitation_callback: Optional elicitation callback.
|
|
44
46
|
"""
|
|
45
|
-
super().__init__(
|
|
47
|
+
super().__init__(
|
|
48
|
+
sampling_callback=sampling_callback,
|
|
49
|
+
elicitation_callback=elicitation_callback,
|
|
50
|
+
message_handler=message_handler,
|
|
51
|
+
logging_callback=logging_callback,
|
|
52
|
+
)
|
|
46
53
|
self.base_url = base_url.rstrip("/")
|
|
47
54
|
self.auth_token = auth_token
|
|
48
55
|
self.headers = headers or {}
|
|
@@ -78,6 +85,8 @@ class HttpConnector(BaseConnector):
|
|
|
78
85
|
write_stream,
|
|
79
86
|
sampling_callback=self.sampling_callback,
|
|
80
87
|
elicitation_callback=self.elicitation_callback,
|
|
88
|
+
message_handler=self._internal_message_handler,
|
|
89
|
+
logging_callback=self.logging_callback,
|
|
81
90
|
client_info=self.client_info,
|
|
82
91
|
)
|
|
83
92
|
await test_client.__aenter__()
|
|
@@ -162,6 +171,8 @@ class HttpConnector(BaseConnector):
|
|
|
162
171
|
write_stream,
|
|
163
172
|
sampling_callback=self.sampling_callback,
|
|
164
173
|
elicitation_callback=self.elicitation_callback,
|
|
174
|
+
message_handler=self._internal_message_handler,
|
|
175
|
+
logging_callback=self.logging_callback,
|
|
165
176
|
client_info=self.client_info,
|
|
166
177
|
)
|
|
167
178
|
await self.client_session.__aenter__()
|
mcp_use/connectors/sandbox.py
CHANGED
|
@@ -12,7 +12,7 @@ import time
|
|
|
12
12
|
|
|
13
13
|
import aiohttp
|
|
14
14
|
from mcp import ClientSession
|
|
15
|
-
from mcp.client.session import ElicitationFnT, SamplingFnT
|
|
15
|
+
from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
|
|
16
16
|
|
|
17
17
|
from ..logging import logger
|
|
18
18
|
from ..task_managers import SseConnectionManager
|
|
@@ -20,10 +20,7 @@ from ..task_managers import SseConnectionManager
|
|
|
20
20
|
# Import E2B SDK components (optional dependency)
|
|
21
21
|
try:
|
|
22
22
|
logger.debug("Attempting to import e2b_code_interpreter...")
|
|
23
|
-
from e2b_code_interpreter import
|
|
24
|
-
CommandHandle,
|
|
25
|
-
Sandbox,
|
|
26
|
-
)
|
|
23
|
+
from e2b_code_interpreter import CommandHandle, Sandbox
|
|
27
24
|
|
|
28
25
|
logger.debug("Successfully imported e2b_code_interpreter")
|
|
29
26
|
except ImportError as e:
|
|
@@ -53,6 +50,8 @@ class SandboxConnector(BaseConnector):
|
|
|
53
50
|
sse_read_timeout: float = 60 * 5,
|
|
54
51
|
sampling_callback: SamplingFnT | None = None,
|
|
55
52
|
elicitation_callback: ElicitationFnT | None = None,
|
|
53
|
+
message_handler: MessageHandlerFnT | None = None,
|
|
54
|
+
logging_callback: LoggingFnT | None = None,
|
|
56
55
|
):
|
|
57
56
|
"""Initialize a new sandbox connector.
|
|
58
57
|
|
|
@@ -67,7 +66,12 @@ class SandboxConnector(BaseConnector):
|
|
|
67
66
|
sampling_callback: Optional sampling callback.
|
|
68
67
|
elicitation_callback: Optional elicitation callback.
|
|
69
68
|
"""
|
|
70
|
-
super().__init__(
|
|
69
|
+
super().__init__(
|
|
70
|
+
sampling_callback=sampling_callback,
|
|
71
|
+
elicitation_callback=elicitation_callback,
|
|
72
|
+
message_handler=message_handler,
|
|
73
|
+
logging_callback=logging_callback,
|
|
74
|
+
)
|
|
71
75
|
if Sandbox is None:
|
|
72
76
|
raise ImportError(
|
|
73
77
|
"E2B SDK (e2b-code-interpreter) not found. Please install it with "
|
|
@@ -227,6 +231,8 @@ class SandboxConnector(BaseConnector):
|
|
|
227
231
|
write_stream,
|
|
228
232
|
sampling_callback=self.sampling_callback,
|
|
229
233
|
elicitation_callback=self.elicitation_callback,
|
|
234
|
+
message_handler=self._internal_message_handler,
|
|
235
|
+
logging_callback=self.logging_callback,
|
|
230
236
|
client_info=self.client_info,
|
|
231
237
|
)
|
|
232
238
|
await self.client_session.__aenter__()
|
mcp_use/connectors/stdio.py
CHANGED
|
@@ -8,7 +8,7 @@ through the standard input/output streams.
|
|
|
8
8
|
import sys
|
|
9
9
|
|
|
10
10
|
from mcp import ClientSession, StdioServerParameters
|
|
11
|
-
from mcp.client.session import ElicitationFnT, SamplingFnT
|
|
11
|
+
from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
|
|
12
12
|
|
|
13
13
|
from ..logging import logger
|
|
14
14
|
from ..task_managers import StdioConnectionManager
|
|
@@ -31,6 +31,8 @@ class StdioConnector(BaseConnector):
|
|
|
31
31
|
errlog=sys.stderr,
|
|
32
32
|
sampling_callback: SamplingFnT | None = None,
|
|
33
33
|
elicitation_callback: ElicitationFnT | None = None,
|
|
34
|
+
message_handler: MessageHandlerFnT | None = None,
|
|
35
|
+
logging_callback: LoggingFnT | None = None,
|
|
34
36
|
):
|
|
35
37
|
"""Initialize a new stdio connector.
|
|
36
38
|
|
|
@@ -42,7 +44,12 @@ class StdioConnector(BaseConnector):
|
|
|
42
44
|
sampling_callback: Optional callback to sample the client.
|
|
43
45
|
elicitation_callback: Optional callback to elicit the client.
|
|
44
46
|
"""
|
|
45
|
-
super().__init__(
|
|
47
|
+
super().__init__(
|
|
48
|
+
sampling_callback=sampling_callback,
|
|
49
|
+
elicitation_callback=elicitation_callback,
|
|
50
|
+
message_handler=message_handler,
|
|
51
|
+
logging_callback=logging_callback,
|
|
52
|
+
)
|
|
46
53
|
self.command = command
|
|
47
54
|
self.args = args or [] # Ensure args is never None
|
|
48
55
|
self.env = env
|
|
@@ -69,6 +76,8 @@ class StdioConnector(BaseConnector):
|
|
|
69
76
|
write_stream,
|
|
70
77
|
sampling_callback=self.sampling_callback,
|
|
71
78
|
elicitation_callback=self.elicitation_callback,
|
|
79
|
+
message_handler=self._internal_message_handler,
|
|
80
|
+
logging_callback=self.logging_callback,
|
|
72
81
|
client_info=self.client_info,
|
|
73
82
|
)
|
|
74
83
|
await self.client_session.__aenter__()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import traceback
|
|
2
|
+
|
|
3
|
+
from ..logging import logger
|
|
4
|
+
|
|
5
|
+
retryable_exceptions = (TimeoutError, ConnectionError) # We can add more exceptions here
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def format_error(error: Exception, **context) -> dict:
|
|
9
|
+
"""
|
|
10
|
+
Formats an exception into a structured format that can be understood by LLMs.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
error: The exception to format.
|
|
14
|
+
**context: Additional context to include in the formatted error.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
A dictionary containing the formatted error.
|
|
18
|
+
"""
|
|
19
|
+
formatted_context = {
|
|
20
|
+
"error": type(error).__name__,
|
|
21
|
+
"details": str(error),
|
|
22
|
+
"isRetryable": isinstance(error, retryable_exceptions),
|
|
23
|
+
"stack": traceback.format_exc(),
|
|
24
|
+
"code": getattr(error, "code", "UNKNOWN"),
|
|
25
|
+
}
|
|
26
|
+
formatted_context.update(context)
|
|
27
|
+
|
|
28
|
+
logger.error(f"Structured error: {formatted_context}") # For observability (maybe remove later)
|
|
29
|
+
return formatted_context
|
|
@@ -4,13 +4,7 @@ from mcp_use.client import MCPClient
|
|
|
4
4
|
from mcp_use.logging import logger
|
|
5
5
|
|
|
6
6
|
from ..adapters.base import BaseAdapter
|
|
7
|
-
from .tools import
|
|
8
|
-
ConnectServerTool,
|
|
9
|
-
DisconnectServerTool,
|
|
10
|
-
GetActiveServerTool,
|
|
11
|
-
ListServersTool,
|
|
12
|
-
SearchToolsTool,
|
|
13
|
-
)
|
|
7
|
+
from .tools import ConnectServerTool, DisconnectServerTool, GetActiveServerTool, ListServersTool, SearchToolsTool
|
|
14
8
|
|
|
15
9
|
|
|
16
10
|
class ServerManager:
|
|
@@ -2,6 +2,7 @@ from typing import ClassVar
|
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel, Field
|
|
4
4
|
|
|
5
|
+
from mcp_use.errors.error_formatting import format_error
|
|
5
6
|
from mcp_use.logging import logger
|
|
6
7
|
|
|
7
8
|
from .base_tool import MCPServerTool
|
|
@@ -62,7 +63,7 @@ class ConnectServerTool(MCPServerTool):
|
|
|
62
63
|
|
|
63
64
|
except Exception as e:
|
|
64
65
|
logger.error(f"Error connecting to server '{server_name}': {e}")
|
|
65
|
-
return
|
|
66
|
+
return format_error(e, server_name=server_name)
|
|
66
67
|
|
|
67
68
|
def _run(self, server_name: str) -> str:
|
|
68
69
|
"""Synchronous version that raises a NotImplementedError - use _arun instead."""
|
|
@@ -2,6 +2,7 @@ from typing import ClassVar
|
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel
|
|
4
4
|
|
|
5
|
+
from mcp_use.errors.error_formatting import format_error
|
|
5
6
|
from mcp_use.logging import logger
|
|
6
7
|
|
|
7
8
|
from .base_tool import MCPServerTool
|
|
@@ -36,7 +37,7 @@ class DisconnectServerTool(MCPServerTool):
|
|
|
36
37
|
return f"Successfully disconnected from MCP server '{server_name}'."
|
|
37
38
|
except Exception as e:
|
|
38
39
|
logger.error(f"Error disconnecting from server '{server_name}': {e}")
|
|
39
|
-
return
|
|
40
|
+
return format_error(e, server_name=server_name)
|
|
40
41
|
|
|
41
42
|
async def _arun(self, **kwargs) -> str:
|
|
42
43
|
"""Async implementation of _run."""
|
|
@@ -2,6 +2,7 @@ from typing import ClassVar
|
|
|
2
2
|
|
|
3
3
|
from pydantic import BaseModel
|
|
4
4
|
|
|
5
|
+
from mcp_use.errors.error_formatting import format_error
|
|
5
6
|
from mcp_use.logging import logger
|
|
6
7
|
|
|
7
8
|
from .base_tool import MCPServerTool
|
|
@@ -44,6 +45,7 @@ class ListServersTool(MCPServerTool):
|
|
|
44
45
|
result += f" {tool_count} tools available for this server\n"
|
|
45
46
|
except Exception as e:
|
|
46
47
|
logger.error(f"Unexpected error listing tools for server '{server_name}': {e}")
|
|
48
|
+
return format_error(e, server=server_name, operation="list_tools")
|
|
47
49
|
|
|
48
50
|
return result
|
|
49
51
|
|
mcp_use/session.py
CHANGED
|
@@ -5,8 +5,12 @@ This module provides a session manager for MCP connections,
|
|
|
5
5
|
which handles authentication, initialization, and tool discovery.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from datetime import timedelta
|
|
8
9
|
from typing import Any
|
|
9
10
|
|
|
11
|
+
from mcp.types import CallToolResult, GetPromptResult, Prompt, ReadResourceResult, Resource, Tool
|
|
12
|
+
from pydantic import AnyUrl
|
|
13
|
+
|
|
10
14
|
from .connectors.base import BaseConnector
|
|
11
15
|
|
|
12
16
|
|
|
@@ -82,3 +86,69 @@ class MCPSession:
|
|
|
82
86
|
True if the connector is connected, False otherwise.
|
|
83
87
|
"""
|
|
84
88
|
return self.connector.is_connected
|
|
89
|
+
|
|
90
|
+
# Convenience methods for MCP operations
|
|
91
|
+
async def call_tool(
|
|
92
|
+
self, name: str, arguments: dict[str, Any], read_timeout_seconds: timedelta | None = None
|
|
93
|
+
) -> CallToolResult:
|
|
94
|
+
"""Call an MCP tool.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
name: The name of the tool to call.
|
|
98
|
+
arguments: The arguments to pass to the tool.
|
|
99
|
+
read_timeout_seconds: Optional timeout for the tool call.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The result of the tool call.
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
RuntimeError: If the connection is lost and cannot be reestablished.
|
|
106
|
+
"""
|
|
107
|
+
return await self.connector.call_tool(name, arguments, read_timeout_seconds)
|
|
108
|
+
|
|
109
|
+
async def list_tools(self) -> list[Tool]:
|
|
110
|
+
"""List all available tools from the MCP server.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
List of available tools.
|
|
114
|
+
"""
|
|
115
|
+
return await self.connector.list_tools()
|
|
116
|
+
|
|
117
|
+
async def list_resources(self) -> list[Resource]:
|
|
118
|
+
"""List all available resources from the MCP server.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
List of available resources.
|
|
122
|
+
"""
|
|
123
|
+
return await self.connector.list_resources()
|
|
124
|
+
|
|
125
|
+
async def read_resource(self, uri: AnyUrl) -> ReadResourceResult:
|
|
126
|
+
"""Read a resource by URI.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
uri: The URI of the resource to read.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
The resource content.
|
|
133
|
+
"""
|
|
134
|
+
return await self.connector.read_resource(uri)
|
|
135
|
+
|
|
136
|
+
async def list_prompts(self) -> list[Prompt]:
|
|
137
|
+
"""List all available prompts from the MCP server.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
List of available prompts.
|
|
141
|
+
"""
|
|
142
|
+
return await self.connector.list_prompts()
|
|
143
|
+
|
|
144
|
+
async def get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> GetPromptResult:
|
|
145
|
+
"""Get a prompt by name.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
name: The name of the prompt to get.
|
|
149
|
+
arguments: Optional arguments for the prompt.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
The prompt result with messages.
|
|
153
|
+
"""
|
|
154
|
+
return await self.connector.get_prompt(name, arguments)
|
mcp_use/telemetry/telemetry.py
CHANGED
|
@@ -11,10 +11,7 @@ from posthog import Posthog
|
|
|
11
11
|
from scarf import ScarfEventLogger
|
|
12
12
|
|
|
13
13
|
from mcp_use.logging import MCP_USE_DEBUG
|
|
14
|
-
from mcp_use.telemetry.events import
|
|
15
|
-
BaseTelemetryEvent,
|
|
16
|
-
MCPAgentExecutionEvent,
|
|
17
|
-
)
|
|
14
|
+
from mcp_use.telemetry.events import BaseTelemetryEvent, MCPAgentExecutionEvent
|
|
18
15
|
from mcp_use.telemetry.utils import get_package_version
|
|
19
16
|
from mcp_use.utils import singleton
|
|
20
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-use
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.9
|
|
4
4
|
Summary: MCP Library for LLMs
|
|
5
5
|
Author-email: Pietro Zullo <pietro.zullo@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -46,25 +46,30 @@ Description-Content-Type: text/markdown
|
|
|
46
46
|
<div align="center">
|
|
47
47
|
<div align="center" style="margin: 0 auto; max-width: 80%;">
|
|
48
48
|
<picture>
|
|
49
|
-
<source media="(prefers-color-scheme: dark)" srcset="static/
|
|
50
|
-
<source media="(prefers-color-scheme: light)" srcset="static/
|
|
51
|
-
<img alt="mcp use logo" src="./static/logo-
|
|
49
|
+
<source media="(prefers-color-scheme: dark)" srcset="static/logo-gh.jpg">
|
|
50
|
+
<source media="(prefers-color-scheme: light)" srcset="static/logo-gh.jpg">
|
|
51
|
+
<img alt="mcp use logo" src="./static/logo-gh.jpg" width="80%" style="margin: 20px auto;">
|
|
52
52
|
</picture>
|
|
53
53
|
</div>
|
|
54
54
|
|
|
55
55
|
<br>
|
|
56
56
|
|
|
57
|
-
#
|
|
57
|
+
# Create MCP Clients and Agents
|
|
58
58
|
|
|
59
|
+
<p align="center">
|
|
60
|
+
<a href="https://www.producthunt.com/products/mcp-use?embed=true&utm_source=badge-featured&utm_medium=badge&utm_source=badge-mcp-use" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1002629&theme=neutral&t=1754609432704" alt="mcp-use - Open source SDK and infra for MCP servers & agents | Product Hunt" style="width: 150px; height: 32px;" width="150" height="32" /></a>
|
|
61
|
+
</p>
|
|
59
62
|
<p align="center">
|
|
60
63
|
<a href="https://github.com/pietrozullo/mcp-use/stargazers" alt="GitHub stars">
|
|
61
64
|
<img src="https://img.shields.io/github/stars/pietrozullo/mcp-use?style=social" /></a>
|
|
65
|
+
<a href="https://pypi.org/project/mcp_use/" alt="PyPI Downloads">
|
|
66
|
+
<img src="https://static.pepy.tech/badge/mcp-use" /></a>
|
|
62
67
|
<a href="https://pypi.org/project/mcp_use/" alt="PyPI Version">
|
|
63
68
|
<img src="https://img.shields.io/pypi/v/mcp_use.svg"/></a>
|
|
69
|
+
<a href="https://github.com/mcp-use/mcp-use-ts" alt="TypeScript">
|
|
70
|
+
<img src="https://img.shields.io/badge/TypeScript-mcp--use-3178C6?logo=typescript&logoColor=white" /></a>
|
|
64
71
|
<a href="https://github.com/pietrozullo/mcp-use/blob/main/LICENSE" alt="License">
|
|
65
72
|
<img src="https://img.shields.io/github/license/pietrozullo/mcp-use" /></a>
|
|
66
|
-
<a href="https://pypi.org/project/mcp_use/" alt="PyPI Downloads">
|
|
67
|
-
<img src="https://static.pepy.tech/badge/mcp-use" /></a>
|
|
68
73
|
<a href="https://docs.mcp-use.com" alt="Documentation">
|
|
69
74
|
<img src="https://img.shields.io/badge/docs-mcp--use.com-blue" /></a>
|
|
70
75
|
<a href="https://mcp-use.com" alt="Website">
|
|
@@ -88,10 +93,10 @@ Description-Content-Type: text/markdown
|
|
|
88
93
|
- Visit the [mcp-use docs](https://docs.mcp-use.com/) to get started with mcp-use library
|
|
89
94
|
- For the TypeScript version, visit [mcp-use-ts](https://github.com/mcp-use/mcp-use-ts)
|
|
90
95
|
|
|
91
|
-
| Supports
|
|
92
|
-
|
|
|
96
|
+
| Supports | |
|
|
97
|
+
| :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
93
98
|
| **Primitives** | [](https://github.com/pietrozullo/mcp-use/actions/workflows/tests.yml) [](https://github.com/pietrozullo/mcp-use/actions/workflows/tests.yml) [](https://github.com/pietrozullo/mcp-use/actions/workflows/tests.yml) [](https://github.com/pietrozullo/mcp-use/actions/workflows/tests.yml) [](https://github.com/pietrozullo/mcp-use/actions/workflows/tests.yml) |
|
|
94
|
-
| **Transports** | [](https://github.com/pietrozullo/mcp-use/actions/workflows/tests.yml) [](https://github.com/pietrozullo/mcp-use/actions/workflows/tests.yml) [](https://github.com/pietrozullo/mcp-use/actions/workflows/tests.yml)
|
|
99
|
+
| **Transports** | [](https://github.com/pietrozullo/mcp-use/actions/workflows/tests.yml) [](https://github.com/pietrozullo/mcp-use/actions/workflows/tests.yml) [](https://github.com/pietrozullo/mcp-use/actions/workflows/tests.yml) |
|
|
95
100
|
|
|
96
101
|
## Features
|
|
97
102
|
|
|
@@ -417,29 +422,7 @@ if __name__ == "__main__":
|
|
|
417
422
|
asyncio.run(run_blender_example())
|
|
418
423
|
```
|
|
419
424
|
|
|
420
|
-
# Configuration
|
|
421
|
-
|
|
422
|
-
MCP-Use supports initialization from configuration files, making it easy to manage and switch between different MCP server setups:
|
|
423
|
-
|
|
424
|
-
```python
|
|
425
|
-
import asyncio
|
|
426
|
-
from mcp_use import create_session_from_config
|
|
427
|
-
|
|
428
|
-
async def main():
|
|
429
|
-
# Create an MCP session from a config file
|
|
430
|
-
session = create_session_from_config("mcp-config.json")
|
|
431
|
-
|
|
432
|
-
# Initialize the session
|
|
433
|
-
await session.initialize()
|
|
434
|
-
|
|
435
|
-
# Use the session...
|
|
436
|
-
|
|
437
|
-
# Disconnect when done
|
|
438
|
-
await session.disconnect()
|
|
439
|
-
|
|
440
|
-
if __name__ == "__main__":
|
|
441
|
-
asyncio.run(main())
|
|
442
|
-
```
|
|
425
|
+
# Configuration Support
|
|
443
426
|
|
|
444
427
|
## HTTP Connection Example
|
|
445
428
|
|
|
@@ -695,6 +678,47 @@ The `SandboxOptions` type provides configuration for the sandbox environment:
|
|
|
695
678
|
- **Consistent environment**: Ensure consistent behavior across different systems
|
|
696
679
|
- **Resource efficiency**: Offload resource-intensive tasks to cloud infrastructure
|
|
697
680
|
|
|
681
|
+
# Direct Tool Calls (Without LLM)
|
|
682
|
+
|
|
683
|
+
You can call MCP server tools directly without an LLM when you need programmatic control:
|
|
684
|
+
|
|
685
|
+
```python
|
|
686
|
+
import asyncio
|
|
687
|
+
from mcp_use import MCPClient
|
|
688
|
+
|
|
689
|
+
async def call_tool_example():
|
|
690
|
+
config = {
|
|
691
|
+
"mcpServers": {
|
|
692
|
+
"everything": {
|
|
693
|
+
"command": "npx",
|
|
694
|
+
"args": ["-y", "@modelcontextprotocol/server-everything"],
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
client = MCPClient.from_dict(config)
|
|
700
|
+
|
|
701
|
+
try:
|
|
702
|
+
await client.create_all_sessions()
|
|
703
|
+
session = client.get_session("everything")
|
|
704
|
+
|
|
705
|
+
# Call tool directly
|
|
706
|
+
result = await session.call_tool(
|
|
707
|
+
name="add",
|
|
708
|
+
arguments={"a": 1, "b": 2}
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
print(f"Result: {result.content[0].text}") # Output: 3
|
|
712
|
+
|
|
713
|
+
finally:
|
|
714
|
+
await client.close_all_sessions()
|
|
715
|
+
|
|
716
|
+
if __name__ == "__main__":
|
|
717
|
+
asyncio.run(call_tool_example())
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
See the complete example: [examples/direct_tool_call.py](examples/direct_tool_call.py)
|
|
721
|
+
|
|
698
722
|
# Build a Custom Agent:
|
|
699
723
|
|
|
700
724
|
You can also build your own custom agent using the LangChain adapter:
|
|
@@ -813,23 +837,27 @@ Thanks to all our amazing contributors!
|
|
|
813
837
|
</tr>
|
|
814
838
|
<tr>
|
|
815
839
|
<td><img src="https://avatars.githubusercontent.com/u/38653995?s=40&v=4" width="20" height="20" style="vertical-align: middle; margin-right: 8px;"> <a href="https://github.com/patchy631/ai-engineering-hub"><strong>patchy631/ai-engineering-hub</strong></a></td>
|
|
816
|
-
<td>⭐
|
|
840
|
+
<td>⭐ 15920</td>
|
|
817
841
|
</tr>
|
|
818
842
|
<tr>
|
|
819
843
|
<td><img src="https://avatars.githubusercontent.com/u/170207473?s=40&v=4" width="20" height="20" style="vertical-align: middle; margin-right: 8px;"> <a href="https://github.com/tavily-ai/meeting-prep-agent"><strong>tavily-ai/meeting-prep-agent</strong></a></td>
|
|
820
844
|
<td>⭐ 129</td>
|
|
821
845
|
</tr>
|
|
822
846
|
<tr>
|
|
823
|
-
<td><img src="https://avatars.githubusercontent.com/u/
|
|
824
|
-
<td>⭐
|
|
847
|
+
<td><img src="https://avatars.githubusercontent.com/u/164294848?s=40&v=4" width="20" height="20" style="vertical-align: middle; margin-right: 8px;"> <a href="https://github.com/buildfastwithai/gen-ai-experiments"><strong>buildfastwithai/gen-ai-experiments</strong></a></td>
|
|
848
|
+
<td>⭐ 93</td>
|
|
849
|
+
</tr>
|
|
850
|
+
<tr>
|
|
851
|
+
<td><img src="https://avatars.githubusercontent.com/u/187057607?s=40&v=4" width="20" height="20" style="vertical-align: middle; margin-right: 8px;"> <a href="https://github.com/hud-evals/hud-python"><strong>hud-evals/hud-python</strong></a></td>
|
|
852
|
+
<td>⭐ 76</td>
|
|
825
853
|
</tr>
|
|
826
854
|
<tr>
|
|
827
855
|
<td><img src="https://avatars.githubusercontent.com/u/20041231?s=40&v=4" width="20" height="20" style="vertical-align: middle; margin-right: 8px;"> <a href="https://github.com/krishnaik06/MCP-CRASH-Course"><strong>krishnaik06/MCP-CRASH-Course</strong></a></td>
|
|
828
|
-
<td>⭐
|
|
856
|
+
<td>⭐ 61</td>
|
|
829
857
|
</tr>
|
|
830
858
|
<tr>
|
|
831
859
|
<td><img src="https://avatars.githubusercontent.com/u/54944174?s=40&v=4" width="20" height="20" style="vertical-align: middle; margin-right: 8px;"> <a href="https://github.com/larksuite/lark-samples"><strong>larksuite/lark-samples</strong></a></td>
|
|
832
|
-
<td>⭐
|
|
860
|
+
<td>⭐ 35</td>
|
|
833
861
|
</tr>
|
|
834
862
|
<tr>
|
|
835
863
|
<td><img src="https://avatars.githubusercontent.com/u/892404?s=40&v=4" width="20" height="20" style="vertical-align: middle; margin-right: 8px;"> <a href="https://github.com/truemagic-coder/solana-agent-app"><strong>truemagic-coder/solana-agent-app</strong></a></td>
|
|
@@ -845,11 +873,7 @@ Thanks to all our amazing contributors!
|
|
|
845
873
|
</tr>
|
|
846
874
|
<tr>
|
|
847
875
|
<td><img src="https://avatars.githubusercontent.com/u/100749943?s=40&v=4" width="20" height="20" style="vertical-align: middle; margin-right: 8px;"> <a href="https://github.com/Deniscartin/mcp-cli"><strong>Deniscartin/mcp-cli</strong></a></td>
|
|
848
|
-
<td>⭐
|
|
849
|
-
</tr>
|
|
850
|
-
<tr>
|
|
851
|
-
<td><img src="https://avatars.githubusercontent.com/u/6688805?s=40&v=4" width="20" height="20" style="vertical-align: middle; margin-right: 8px;"> <a href="https://github.com/innovaccer/Healthcare-MCP"><strong>innovaccer/Healthcare-MCP</strong></a></td>
|
|
852
|
-
<td>⭐ 15</td>
|
|
876
|
+
<td>⭐ 19</td>
|
|
853
877
|
</tr>
|
|
854
878
|
</table>
|
|
855
879
|
|
|
@@ -1,33 +1,35 @@
|
|
|
1
|
-
mcp_use/__init__.py,sha256=
|
|
2
|
-
mcp_use/client.py,sha256=
|
|
3
|
-
mcp_use/config.py,sha256=
|
|
1
|
+
mcp_use/__init__.py,sha256=vyjxKmfVDtkbJ6sthDEH_m-uJGXxkYdUBMwBpCt72zA,1021
|
|
2
|
+
mcp_use/client.py,sha256=4WnFrbBBa3YX3brfBgZrhb_OgAT8mMfVzLUHwnnKi8o,11701
|
|
3
|
+
mcp_use/config.py,sha256=yRgUPCMUzkFqROyccG2wjuhGxneCcbgnrHWHQ6z_hoc,3487
|
|
4
4
|
mcp_use/logging.py,sha256=CRtkPwR-bkXK_kQ0QOL86RikMWOHzEOi7A8VRHkNsZw,4270
|
|
5
|
-
mcp_use/session.py,sha256=
|
|
5
|
+
mcp_use/session.py,sha256=DpH_z0a3xYemV9yWzlrf-IPZtpR27CI2S-gAm2jbCAc,4700
|
|
6
6
|
mcp_use/utils.py,sha256=QavJcVq2WxUUUCCpPCUeOB5bqIS0FFmpK-RAZkGc6aA,720
|
|
7
7
|
mcp_use/adapters/__init__.py,sha256=-xCrgPThuX7x0PHGFDdjb7M-mgw6QV3sKu5PM7ShnRg,275
|
|
8
|
-
mcp_use/adapters/base.py,sha256=
|
|
9
|
-
mcp_use/adapters/langchain_adapter.py,sha256=
|
|
8
|
+
mcp_use/adapters/base.py,sha256=8XB3xWZ6nJPhhmHwVtHT8-HO0D_9nnxzOkbVDP-fI3k,6940
|
|
9
|
+
mcp_use/adapters/langchain_adapter.py,sha256=zGEVMXLj_jpSXUMHOh5u-fxkkrK2zpSibOSGCy_VMr0,11033
|
|
10
10
|
mcp_use/agents/__init__.py,sha256=FzkntihbAqzixWdWe99zIrrcIfd4N3YWltNniutG9VA,267
|
|
11
11
|
mcp_use/agents/base.py,sha256=EN-dRbwOi9vIqofFg3jmi5yT2VKlwEr9Cwi1DZgB3eE,1591
|
|
12
12
|
mcp_use/agents/mcpagent.py,sha256=Vh4VOxxh-6sJwK1tTtJgUWZcp1bd3hb_JnATc7x9sKk,46698
|
|
13
|
-
mcp_use/agents/remote.py,sha256=
|
|
13
|
+
mcp_use/agents/remote.py,sha256=peSw3aixybneTWgrGlSZedUvo_FuISSiWqFgySZSwEM,13011
|
|
14
14
|
mcp_use/agents/prompts/system_prompt_builder.py,sha256=E86STmxcl2Ic763_114awNqFB2RyLrQlbvgRmJajQjI,4116
|
|
15
15
|
mcp_use/agents/prompts/templates.py,sha256=acg2Q-_uQDL-3q5ZUwwwFrP7wqqf-SEyq0XWDDHt69s,1906
|
|
16
16
|
mcp_use/connectors/__init__.py,sha256=cUF4yT0bNr8qeLkSzg28SHueiV5qDaHEB1l1GZ2K0dc,536
|
|
17
|
-
mcp_use/connectors/base.py,sha256=
|
|
18
|
-
mcp_use/connectors/http.py,sha256=
|
|
19
|
-
mcp_use/connectors/sandbox.py,sha256=
|
|
20
|
-
mcp_use/connectors/stdio.py,sha256=
|
|
17
|
+
mcp_use/connectors/base.py,sha256=R1Qh9D6btullQUGiMBVZewP3M7d-0VrsIt4bSw3bHxI,17482
|
|
18
|
+
mcp_use/connectors/http.py,sha256=eiX5NAsT9mnzqWRAoxb6qG3nWxPiVyw5MVcwRY8D6lE,8436
|
|
19
|
+
mcp_use/connectors/sandbox.py,sha256=oXs4Q_1bQJ10XOrJLjFUBKvFy2VmWmyzLhotczl44Po,11804
|
|
20
|
+
mcp_use/connectors/stdio.py,sha256=4gXdXyaeA3B-ywAjPmbEEbHxP2Gg5cWsXNC2kHkubDA,3766
|
|
21
21
|
mcp_use/connectors/utils.py,sha256=zQ8GdNQx0Twz3by90BoU1RsWPf9wODGof4K3-NxPXeA,366
|
|
22
22
|
mcp_use/connectors/websocket.py,sha256=G7ZeLJNPVl9AG6kCmiNJz1N2Ing_QxT7pSswigTKi8Y,9650
|
|
23
|
+
mcp_use/errors/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
24
|
+
mcp_use/errors/error_formatting.py,sha256=17lhj5goGHuTbJ5oLCUXyJ2SuIbzsuSM1Wk8LPeqY9k,911
|
|
23
25
|
mcp_use/managers/__init__.py,sha256=FRTuJw5kYtY1Eo7wN9Aeqeqo1euiR5slvrx5Fl_SGvk,383
|
|
24
|
-
mcp_use/managers/server_manager.py,sha256=
|
|
26
|
+
mcp_use/managers/server_manager.py,sha256=eWxiuP0yL3HWSyCWdHqNZ1isZfa3IZDWVEjWSfYOs_A,5253
|
|
25
27
|
mcp_use/managers/tools/__init__.py,sha256=zcpm4HXsp8NUMRJeyT6DdB8cgIMDs46pBfoTD-odhGU,437
|
|
26
28
|
mcp_use/managers/tools/base_tool.py,sha256=Jbbp7SwmHKDk8jT_6yVIv7iNsn6KaV_PljWuhhLcbXg,509
|
|
27
|
-
mcp_use/managers/tools/connect_server.py,sha256
|
|
28
|
-
mcp_use/managers/tools/disconnect_server.py,sha256=
|
|
29
|
+
mcp_use/managers/tools/connect_server.py,sha256=-PlqgJDSMzairK90aDg1WTDjpqrFMoTiyekwoPDWNrw,2964
|
|
30
|
+
mcp_use/managers/tools/disconnect_server.py,sha256=dLa5PH-QZ30Dw3n5lzqilyHA8PuQ6xvMkXd-T5EwpU8,1622
|
|
29
31
|
mcp_use/managers/tools/get_active_server.py,sha256=tCaib76gYU3L5G82tEOTq4Io2cuCXWjOjPselb-92i8,964
|
|
30
|
-
mcp_use/managers/tools/list_servers_tool.py,sha256=
|
|
32
|
+
mcp_use/managers/tools/list_servers_tool.py,sha256=P_Z96Ab8ELLyo3GQfTMfyemTPJwt0VR1s_iMnWE1GCg,2037
|
|
31
33
|
mcp_use/managers/tools/search_tools.py,sha256=4vso7ln-AfG6lQAMq9FA_CyeVtSEDYEWlHtdHtfnLps,12911
|
|
32
34
|
mcp_use/observability/__init__.py,sha256=kTUcP0d6L5_3ktfldhdAk-3AWckzVHs7ztG-R6cye64,186
|
|
33
35
|
mcp_use/observability/laminar.py,sha256=WWjmVXP55yCfAlqlayeuJmym1gdrv8is7UyrIp4Tbn0,839
|
|
@@ -40,10 +42,10 @@ mcp_use/task_managers/streamable_http.py,sha256=Zky821Ston5CX0DQVyeRxc2uUqALD8so
|
|
|
40
42
|
mcp_use/task_managers/websocket.py,sha256=9JTw705rhYbP6x2xAPF6PwtNgF5yEWTQhx-dYSPMoaI,2154
|
|
41
43
|
mcp_use/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
44
|
mcp_use/telemetry/events.py,sha256=K5xqbmkum30r4gM2PWtTiUWGF8oZzGZw2DYwco1RfOQ,3113
|
|
43
|
-
mcp_use/telemetry/telemetry.py,sha256=
|
|
45
|
+
mcp_use/telemetry/telemetry.py,sha256=oM_QAbZwOStKkeccvEfKmJLKhL2neFkPn5yM5rL2qHc,11711
|
|
44
46
|
mcp_use/telemetry/utils.py,sha256=kDVTqt2oSeWNJbnTOlXOehr2yFO0PMyx2UGkrWkfJiw,1769
|
|
45
47
|
mcp_use/types/sandbox.py,sha256=opJ9r56F1FvaqVvPovfAj5jZbsOexgwYx5wLgSlN8_U,712
|
|
46
|
-
mcp_use-1.3.
|
|
47
|
-
mcp_use-1.3.
|
|
48
|
-
mcp_use-1.3.
|
|
49
|
-
mcp_use-1.3.
|
|
48
|
+
mcp_use-1.3.9.dist-info/METADATA,sha256=U-uwkjLkdcK1IkliQ6UGY_jKRUJEgN1K_2xQEHTB9Yk,34122
|
|
49
|
+
mcp_use-1.3.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
50
|
+
mcp_use-1.3.9.dist-info/licenses/LICENSE,sha256=7Pw7dbwJSBw8zH-WE03JnR5uXvitRtaGTP9QWPcexcs,1068
|
|
51
|
+
mcp_use-1.3.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|