mcp-use 1.2.5__py3-none-any.whl → 1.2.7__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/adapters/__init__.py +7 -2
- mcp_use/adapters/base.py +178 -0
- mcp_use/adapters/langchain_adapter.py +103 -154
- mcp_use/agents/mcpagent.py +17 -10
- mcp_use/agents/server_manager.py +4 -2
- {mcp_use-1.2.5.dist-info → mcp_use-1.2.7.dist-info}/METADATA +38 -2
- {mcp_use-1.2.5.dist-info → mcp_use-1.2.7.dist-info}/RECORD +9 -8
- {mcp_use-1.2.5.dist-info → mcp_use-1.2.7.dist-info}/WHEEL +0 -0
- {mcp_use-1.2.5.dist-info → mcp_use-1.2.7.dist-info}/licenses/LICENSE +0 -0
mcp_use/adapters/__init__.py
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
"""
|
|
2
|
-
MCP
|
|
2
|
+
Adapters for converting MCP tools to different frameworks.
|
|
3
3
|
|
|
4
|
-
This package
|
|
4
|
+
This package provides adapters for converting MCP tools to different frameworks.
|
|
5
5
|
"""
|
|
6
|
+
|
|
7
|
+
from .base import BaseAdapter
|
|
8
|
+
from .langchain_adapter import LangChainAdapter
|
|
9
|
+
|
|
10
|
+
__all__ = ["BaseAdapter", "LangChainAdapter"]
|
mcp_use/adapters/base.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base adapter interface for MCP tools.
|
|
3
|
+
|
|
4
|
+
This module provides the abstract base class that all MCP tool adapters should inherit from.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Any, TypeVar
|
|
9
|
+
|
|
10
|
+
from ..client import MCPClient
|
|
11
|
+
from ..connectors.base import BaseConnector
|
|
12
|
+
from ..logging import logger
|
|
13
|
+
|
|
14
|
+
# Generic type for the tools created by the adapter
|
|
15
|
+
T = TypeVar("T")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BaseAdapter(ABC):
|
|
19
|
+
"""Abstract base class for converting MCP tools to other framework formats.
|
|
20
|
+
|
|
21
|
+
This class defines the common interface that all adapter implementations
|
|
22
|
+
should follow to ensure consistency across different frameworks.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, disallowed_tools: list[str] | None = None) -> None:
|
|
26
|
+
"""Initialize a new adapter.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
disallowed_tools: list of tool names that should not be available.
|
|
30
|
+
"""
|
|
31
|
+
self.disallowed_tools = disallowed_tools or []
|
|
32
|
+
self._connector_tool_map: dict[BaseConnector, list[T]] = {}
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
async def create_tools(
|
|
36
|
+
cls, client: "MCPClient", disallowed_tools: list[str] | None = None
|
|
37
|
+
) -> list[T]:
|
|
38
|
+
"""Create tools from an MCPClient instance.
|
|
39
|
+
|
|
40
|
+
This is the recommended way to create tools from an MCPClient, as it handles
|
|
41
|
+
session creation and connector extraction automatically.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
client: The MCPClient to extract tools from.
|
|
45
|
+
disallowed_tools: Optional list of tool names to exclude.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
A list of tools in the target framework's format.
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
```python
|
|
52
|
+
from mcp_use.client import MCPClient
|
|
53
|
+
from mcp_use.adapters import YourAdapter
|
|
54
|
+
|
|
55
|
+
client = MCPClient.from_config_file("config.json")
|
|
56
|
+
tools = await YourAdapter.create_tools(client)
|
|
57
|
+
```
|
|
58
|
+
"""
|
|
59
|
+
# Create the adapter
|
|
60
|
+
adapter = cls(disallowed_tools=disallowed_tools)
|
|
61
|
+
|
|
62
|
+
# Ensure we have active sessions
|
|
63
|
+
if not client.active_sessions:
|
|
64
|
+
logger.info("No active sessions found, creating new ones...")
|
|
65
|
+
await client.create_all_sessions()
|
|
66
|
+
|
|
67
|
+
# Get all active sessions
|
|
68
|
+
sessions = client.get_all_active_sessions()
|
|
69
|
+
|
|
70
|
+
# Extract connectors from sessions
|
|
71
|
+
connectors = [session.connector for session in sessions.values()]
|
|
72
|
+
|
|
73
|
+
# Create tools from connectors
|
|
74
|
+
return await adapter._create_tools_from_connectors(connectors)
|
|
75
|
+
|
|
76
|
+
async def load_tools_for_connector(self, connector: BaseConnector) -> list[T]:
|
|
77
|
+
"""Dynamically load tools for a specific connector.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
connector: The connector to load tools for.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
The list of tools that were loaded in the target framework's format.
|
|
84
|
+
"""
|
|
85
|
+
# Check if we already have tools for this connector
|
|
86
|
+
if connector in self._connector_tool_map:
|
|
87
|
+
logger.debug(
|
|
88
|
+
f"Returning {len(self._connector_tool_map[connector])} existing tools for connector"
|
|
89
|
+
)
|
|
90
|
+
return self._connector_tool_map[connector]
|
|
91
|
+
|
|
92
|
+
# Create tools for this connector
|
|
93
|
+
connector_tools = []
|
|
94
|
+
|
|
95
|
+
# Make sure the connector is initialized and has tools
|
|
96
|
+
success = await self._ensure_connector_initialized(connector)
|
|
97
|
+
if not success:
|
|
98
|
+
return []
|
|
99
|
+
|
|
100
|
+
# Now create tools for each MCP tool
|
|
101
|
+
for tool in connector.tools:
|
|
102
|
+
# Convert the tool and add it to the list if conversion was successful
|
|
103
|
+
converted_tool = self._convert_tool(tool, connector)
|
|
104
|
+
if converted_tool:
|
|
105
|
+
connector_tools.append(converted_tool)
|
|
106
|
+
|
|
107
|
+
# Store the tools for this connector
|
|
108
|
+
self._connector_tool_map[connector] = connector_tools
|
|
109
|
+
|
|
110
|
+
# Log available tools for debugging
|
|
111
|
+
logger.debug(
|
|
112
|
+
f"Loaded {len(connector_tools)} new tools for connector: "
|
|
113
|
+
f"{[getattr(tool, 'name', str(tool)) for tool in connector_tools]}"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return connector_tools
|
|
117
|
+
|
|
118
|
+
@abstractmethod
|
|
119
|
+
def _convert_tool(self, mcp_tool: dict[str, Any], connector: BaseConnector) -> T:
|
|
120
|
+
"""Convert an MCP tool to the target framework's tool format.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
mcp_tool: The MCP tool to convert.
|
|
124
|
+
connector: The connector that provides this tool.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
A tool in the target framework's format.
|
|
128
|
+
"""
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
async def _create_tools_from_connectors(self, connectors: list[BaseConnector]) -> list[T]:
|
|
132
|
+
"""Create tools from MCP tools in all provided connectors.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
connectors: list of MCP connectors to create tools from.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
A list of tools in the target framework's format.
|
|
139
|
+
"""
|
|
140
|
+
tools = []
|
|
141
|
+
for connector in connectors:
|
|
142
|
+
# Create tools for this connector
|
|
143
|
+
connector_tools = await self.load_tools_for_connector(connector)
|
|
144
|
+
tools.extend(connector_tools)
|
|
145
|
+
|
|
146
|
+
# Log available tools for debugging
|
|
147
|
+
logger.debug(f"Available tools: {len(tools)}")
|
|
148
|
+
return tools
|
|
149
|
+
|
|
150
|
+
def _check_connector_initialized(self, connector: BaseConnector) -> bool:
|
|
151
|
+
"""Check if a connector is initialized and has tools.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
connector: The connector to check.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
True if the connector is initialized and has tools, False otherwise.
|
|
158
|
+
"""
|
|
159
|
+
return hasattr(connector, "tools") and connector.tools
|
|
160
|
+
|
|
161
|
+
async def _ensure_connector_initialized(self, connector: BaseConnector) -> bool:
|
|
162
|
+
"""Ensure a connector is initialized.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
connector: The connector to initialize.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
True if initialization succeeded, False otherwise.
|
|
169
|
+
"""
|
|
170
|
+
if not self._check_connector_initialized(connector):
|
|
171
|
+
logger.debug("Connector doesn't have tools, initializing it")
|
|
172
|
+
try:
|
|
173
|
+
await connector.initialize()
|
|
174
|
+
return True
|
|
175
|
+
except Exception as e:
|
|
176
|
+
logger.error(f"Error initializing connector: {e}")
|
|
177
|
+
return False
|
|
178
|
+
return True
|
|
@@ -13,65 +13,19 @@ from pydantic import BaseModel
|
|
|
13
13
|
|
|
14
14
|
from ..connectors.base import BaseConnector
|
|
15
15
|
from ..logging import logger
|
|
16
|
+
from .base import BaseAdapter
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
"""Parse the content of a CallToolResult into a string.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
tool_result: The result object from calling an MCP tool.
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
A string representation of the tool result content.
|
|
26
|
-
|
|
27
|
-
Raises:
|
|
28
|
-
ToolException: If the tool execution failed, returned no content,
|
|
29
|
-
or contained unexpected content types.
|
|
30
|
-
"""
|
|
31
|
-
if tool_result.isError:
|
|
32
|
-
raise ToolException(f"Tool execution failed: {tool_result.content}")
|
|
33
|
-
|
|
34
|
-
if not tool_result.content:
|
|
35
|
-
raise ToolException("Tool execution returned no content")
|
|
36
|
-
|
|
37
|
-
decoded_result = ""
|
|
38
|
-
for item in tool_result.content:
|
|
39
|
-
match item.type:
|
|
40
|
-
case "text":
|
|
41
|
-
item: TextContent
|
|
42
|
-
decoded_result += item.text
|
|
43
|
-
case "image":
|
|
44
|
-
item: ImageContent
|
|
45
|
-
decoded_result += item.data # Assuming data is string-like or base64
|
|
46
|
-
case "resource":
|
|
47
|
-
resource: EmbeddedResource = item.resource
|
|
48
|
-
if hasattr(resource, "text"):
|
|
49
|
-
decoded_result += resource.text
|
|
50
|
-
elif hasattr(resource, "blob"):
|
|
51
|
-
# Assuming blob needs decoding or specific handling; adjust as needed
|
|
52
|
-
decoded_result += (
|
|
53
|
-
resource.blob.decode()
|
|
54
|
-
if isinstance(resource.blob, bytes)
|
|
55
|
-
else str(resource.blob)
|
|
56
|
-
)
|
|
57
|
-
else:
|
|
58
|
-
raise ToolException(f"Unexpected resource type: {resource.type}")
|
|
59
|
-
case _:
|
|
60
|
-
raise ToolException(f"Unexpected content type: {item.type}")
|
|
61
|
-
|
|
62
|
-
return decoded_result
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class LangChainAdapter:
|
|
19
|
+
class LangChainAdapter(BaseAdapter):
|
|
66
20
|
"""Adapter for converting MCP tools to LangChain tools."""
|
|
67
21
|
|
|
68
22
|
def __init__(self, disallowed_tools: list[str] | None = None) -> None:
|
|
69
23
|
"""Initialize a new LangChain adapter.
|
|
70
24
|
|
|
71
25
|
Args:
|
|
72
|
-
disallowed_tools:
|
|
26
|
+
disallowed_tools: list of tool names that should not be available.
|
|
73
27
|
"""
|
|
74
|
-
|
|
28
|
+
super().__init__(disallowed_tools)
|
|
75
29
|
self._connector_tool_map: dict[BaseConnector, list[BaseTool]] = {}
|
|
76
30
|
|
|
77
31
|
def fix_schema(self, schema: dict) -> dict:
|
|
@@ -91,122 +45,117 @@ class LangChainAdapter:
|
|
|
91
45
|
schema[key] = self.fix_schema(value) # Apply recursively
|
|
92
46
|
return schema
|
|
93
47
|
|
|
94
|
-
|
|
95
|
-
"""
|
|
48
|
+
def _parse_mcp_tool_result(self, tool_result: CallToolResult) -> str:
|
|
49
|
+
"""Parse the content of a CallToolResult into a string.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
tool_result: The result object from calling an MCP tool.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
A string representation of the tool result content.
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
ToolException: If the tool execution failed, returned no content,
|
|
59
|
+
or contained unexpected content types.
|
|
60
|
+
"""
|
|
61
|
+
if tool_result.isError:
|
|
62
|
+
raise ToolException(f"Tool execution failed: {tool_result.content}")
|
|
63
|
+
|
|
64
|
+
if not tool_result.content:
|
|
65
|
+
raise ToolException("Tool execution returned no content")
|
|
66
|
+
|
|
67
|
+
decoded_result = ""
|
|
68
|
+
for item in tool_result.content:
|
|
69
|
+
match item.type:
|
|
70
|
+
case "text":
|
|
71
|
+
item: TextContent
|
|
72
|
+
decoded_result += item.text
|
|
73
|
+
case "image":
|
|
74
|
+
item: ImageContent
|
|
75
|
+
decoded_result += item.data # Assuming data is string-like or base64
|
|
76
|
+
case "resource":
|
|
77
|
+
resource: EmbeddedResource = item.resource
|
|
78
|
+
if hasattr(resource, "text"):
|
|
79
|
+
decoded_result += resource.text
|
|
80
|
+
elif hasattr(resource, "blob"):
|
|
81
|
+
# Assuming blob needs decoding or specific handling; adjust as needed
|
|
82
|
+
decoded_result += (
|
|
83
|
+
resource.blob.decode()
|
|
84
|
+
if isinstance(resource.blob, bytes)
|
|
85
|
+
else str(resource.blob)
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
raise ToolException(f"Unexpected resource type: {resource.type}")
|
|
89
|
+
case _:
|
|
90
|
+
raise ToolException(f"Unexpected content type: {item.type}")
|
|
91
|
+
|
|
92
|
+
return decoded_result
|
|
93
|
+
|
|
94
|
+
def _convert_tool(self, mcp_tool: dict[str, Any], connector: BaseConnector) -> BaseTool:
|
|
95
|
+
"""Convert an MCP tool to LangChain's tool format.
|
|
96
96
|
|
|
97
97
|
Args:
|
|
98
|
-
|
|
98
|
+
mcp_tool: The MCP tool to convert.
|
|
99
|
+
connector: The connector that provides this tool.
|
|
99
100
|
|
|
100
101
|
Returns:
|
|
101
|
-
|
|
102
|
+
A LangChain BaseTool.
|
|
102
103
|
"""
|
|
103
|
-
#
|
|
104
|
-
if
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
# Skip disallowed tools
|
|
105
|
+
if mcp_tool.name in self.disallowed_tools:
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
# This is a dynamic class creation, we need to work with the self reference
|
|
109
|
+
adapter_self = self
|
|
110
|
+
|
|
111
|
+
class McpToLangChainAdapter(BaseTool):
|
|
112
|
+
name: str = mcp_tool.name or "NO NAME"
|
|
113
|
+
description: str = mcp_tool.description or ""
|
|
114
|
+
# Convert JSON schema to Pydantic model for argument validation
|
|
115
|
+
args_schema: type[BaseModel] = jsonschema_to_pydantic(
|
|
116
|
+
adapter_self.fix_schema(mcp_tool.inputSchema) # Apply schema conversion
|
|
107
117
|
)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
# Create tools for this connector
|
|
111
|
-
connector_tools = []
|
|
112
|
-
|
|
113
|
-
# Make sure the connector is initialized and has tools
|
|
114
|
-
if not hasattr(connector, "tools") or not connector.tools:
|
|
115
|
-
logger.debug("Connector doesn't have tools, initializing it")
|
|
116
|
-
try:
|
|
117
|
-
await connector.initialize()
|
|
118
|
-
except Exception as e:
|
|
119
|
-
logger.error(f"Error initializing connector: {e}")
|
|
120
|
-
return []
|
|
121
|
-
|
|
122
|
-
# Now create tools for each MCP tool
|
|
123
|
-
for tool in connector.tools:
|
|
124
|
-
# Skip disallowed tools
|
|
125
|
-
if tool.name in self.disallowed_tools:
|
|
126
|
-
continue
|
|
127
|
-
|
|
128
|
-
class McpToLangChainAdapter(BaseTool):
|
|
129
|
-
name: str = tool.name or "NO NAME"
|
|
130
|
-
description: str = tool.description or ""
|
|
131
|
-
# Convert JSON schema to Pydantic model for argument validation
|
|
132
|
-
args_schema: type[BaseModel] = jsonschema_to_pydantic(
|
|
133
|
-
self.fix_schema(tool.inputSchema) # Apply schema conversion
|
|
134
|
-
)
|
|
135
|
-
tool_connector: BaseConnector = connector # Renamed variable to avoid name conflict
|
|
136
|
-
handle_tool_error: bool = True
|
|
137
|
-
|
|
138
|
-
def _run(self, **kwargs: Any) -> NoReturn:
|
|
139
|
-
"""Synchronous run method that always raises an error.
|
|
140
|
-
|
|
141
|
-
Raises:
|
|
142
|
-
NotImplementedError: Always raises this error because MCP tools
|
|
143
|
-
only support async operations.
|
|
144
|
-
"""
|
|
145
|
-
raise NotImplementedError("MCP tools only support async operations")
|
|
146
|
-
|
|
147
|
-
async def _arun(self, **kwargs: Any) -> Any:
|
|
148
|
-
"""Asynchronously execute the tool with given arguments.
|
|
149
|
-
|
|
150
|
-
Args:
|
|
151
|
-
kwargs: The arguments to pass to the tool.
|
|
152
|
-
|
|
153
|
-
Returns:
|
|
154
|
-
The result of the tool execution.
|
|
155
|
-
|
|
156
|
-
Raises:
|
|
157
|
-
ToolException: If tool execution fails.
|
|
158
|
-
"""
|
|
159
|
-
logger.debug(f'MCP tool: "{self.name}" received input: {kwargs}')
|
|
118
|
+
tool_connector: BaseConnector = connector # Renamed variable to avoid name conflict
|
|
119
|
+
handle_tool_error: bool = True
|
|
160
120
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
self.name, kwargs
|
|
164
|
-
)
|
|
165
|
-
try:
|
|
166
|
-
# Use the helper function to parse the result
|
|
167
|
-
return _parse_mcp_tool_result(tool_result)
|
|
168
|
-
except Exception as e:
|
|
169
|
-
# Log the exception for debugging
|
|
170
|
-
logger.error(f"Error parsing tool result: {e}")
|
|
171
|
-
# Shortened line:
|
|
172
|
-
return (
|
|
173
|
-
f"Error parsing result: {e!s};"
|
|
174
|
-
f" Raw content: {tool_result.content!r}"
|
|
175
|
-
)
|
|
121
|
+
def _run(self, **kwargs: Any) -> NoReturn:
|
|
122
|
+
"""Synchronous run method that always raises an error.
|
|
176
123
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
124
|
+
Raises:
|
|
125
|
+
NotImplementedError: Always raises this error because MCP tools
|
|
126
|
+
only support async operations.
|
|
127
|
+
"""
|
|
128
|
+
raise NotImplementedError("MCP tools only support async operations")
|
|
181
129
|
|
|
182
|
-
|
|
130
|
+
async def _arun(self, **kwargs: Any) -> Any:
|
|
131
|
+
"""Asynchronously execute the tool with given arguments.
|
|
183
132
|
|
|
184
|
-
|
|
185
|
-
|
|
133
|
+
Args:
|
|
134
|
+
kwargs: The arguments to pass to the tool.
|
|
186
135
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
f"Loaded {len(connector_tools)} new tools for"
|
|
190
|
-
f" connector: {[tool.name for tool in connector_tools]}"
|
|
191
|
-
)
|
|
136
|
+
Returns:
|
|
137
|
+
The result of the tool execution.
|
|
192
138
|
|
|
193
|
-
|
|
139
|
+
Raises:
|
|
140
|
+
ToolException: If tool execution fails.
|
|
141
|
+
"""
|
|
142
|
+
logger.debug(f'MCP tool: "{self.name}" received input: {kwargs}')
|
|
194
143
|
|
|
195
|
-
|
|
196
|
-
|
|
144
|
+
try:
|
|
145
|
+
tool_result: CallToolResult = await self.tool_connector.call_tool(
|
|
146
|
+
self.name, kwargs
|
|
147
|
+
)
|
|
148
|
+
try:
|
|
149
|
+
# Use the helper function to parse the result
|
|
150
|
+
return adapter_self._parse_mcp_tool_result(tool_result)
|
|
151
|
+
except Exception as e:
|
|
152
|
+
# Log the exception for debugging
|
|
153
|
+
logger.error(f"Error parsing tool result: {e}")
|
|
154
|
+
return f"Error parsing result: {e!s}; Raw content: {tool_result.content!r}"
|
|
197
155
|
|
|
198
|
-
|
|
199
|
-
|
|
156
|
+
except Exception as e:
|
|
157
|
+
if self.handle_tool_error:
|
|
158
|
+
return f"Error executing MCP tool: {str(e)}"
|
|
159
|
+
raise
|
|
200
160
|
|
|
201
|
-
|
|
202
|
-
A list of LangChain tools that wrap MCP tools.
|
|
203
|
-
"""
|
|
204
|
-
tools = []
|
|
205
|
-
for connector in connectors:
|
|
206
|
-
# Create tools for this connector
|
|
207
|
-
connector_tools = await self.load_tools_for_connector(connector)
|
|
208
|
-
tools.extend(connector_tools)
|
|
209
|
-
|
|
210
|
-
# Log available tools for debugging
|
|
211
|
-
logger.debug(f"Available tools: {[tool.name for tool in tools]}")
|
|
212
|
-
return tools
|
|
161
|
+
return McpToLangChainAdapter()
|
mcp_use/agents/mcpagent.py
CHANGED
|
@@ -134,7 +134,10 @@ class MCPAgent:
|
|
|
134
134
|
logger.info("🔄 No active sessions found, creating new ones...")
|
|
135
135
|
self._sessions = await self.client.create_all_sessions()
|
|
136
136
|
logger.info(f"✅ Created {len(self._sessions)} new sessions")
|
|
137
|
-
|
|
137
|
+
|
|
138
|
+
# Create LangChain tools directly from the client using the adapter
|
|
139
|
+
self._tools = await self.adapter.create_tools(self.client)
|
|
140
|
+
logger.info(f"🛠️ Created {len(self._tools)} LangChain tools from client")
|
|
138
141
|
else:
|
|
139
142
|
# Using direct connector - only establish connection
|
|
140
143
|
# LangChainAdapter will handle initialization
|
|
@@ -144,16 +147,16 @@ class MCPAgent:
|
|
|
144
147
|
if not hasattr(connector, "client") or connector.client is None:
|
|
145
148
|
await connector.connect()
|
|
146
149
|
|
|
147
|
-
|
|
148
|
-
|
|
150
|
+
# Create LangChain tools using the adapter with connectors
|
|
151
|
+
self._tools = await self.adapter._create_tools_from_connectors(connectors_to_use)
|
|
152
|
+
logger.info(f"🛠️ Created {len(self._tools)} LangChain tools from connectors")
|
|
149
153
|
|
|
150
|
-
#
|
|
151
|
-
|
|
154
|
+
# Get all tools for system message generation
|
|
155
|
+
all_tools = self._tools
|
|
156
|
+
logger.info(f"🧰 Found {len(all_tools)} tools across all connectors")
|
|
152
157
|
|
|
153
|
-
# Create
|
|
154
|
-
|
|
155
|
-
self._tools = await self.adapter.create_langchain_tools(connectors_to_use)
|
|
156
|
-
logger.info(f"🛠️ Created {len(self._tools)} LangChain tools")
|
|
158
|
+
# Create the system message based on available tools
|
|
159
|
+
await self._create_system_message_from_tools(all_tools)
|
|
157
160
|
|
|
158
161
|
# Create the agent
|
|
159
162
|
self._agent_executor = self._create_agent()
|
|
@@ -502,7 +505,7 @@ class MCPAgent:
|
|
|
502
505
|
self._tools = []
|
|
503
506
|
|
|
504
507
|
# If using client with session, close the session through client
|
|
505
|
-
if self.client
|
|
508
|
+
if self.client:
|
|
506
509
|
logger.info("🔄 Closing sessions through client")
|
|
507
510
|
await self.client.close_all_sessions()
|
|
508
511
|
self._sessions = {}
|
|
@@ -512,6 +515,10 @@ class MCPAgent:
|
|
|
512
515
|
logger.info("🔄 Disconnecting connector")
|
|
513
516
|
await connector.disconnect()
|
|
514
517
|
|
|
518
|
+
# Clear adapter tool cache
|
|
519
|
+
if hasattr(self.adapter, "_connector_tool_map"):
|
|
520
|
+
self.adapter._connector_tool_map = {}
|
|
521
|
+
|
|
515
522
|
self._initialized = False
|
|
516
523
|
logger.info("👋 Agent closed successfully")
|
|
517
524
|
|
mcp_use/agents/server_manager.py
CHANGED
|
@@ -143,7 +143,9 @@ class ServerManager:
|
|
|
143
143
|
if session:
|
|
144
144
|
try:
|
|
145
145
|
connector = session.connector
|
|
146
|
-
fetched_tools = await self.adapter.
|
|
146
|
+
fetched_tools = await self.adapter._create_tools_from_connectors(
|
|
147
|
+
[connector]
|
|
148
|
+
)
|
|
147
149
|
self._server_tools[server_name] = fetched_tools # Cache tools
|
|
148
150
|
self.initialized_servers[server_name] = True # Mark as initialized
|
|
149
151
|
tools = fetched_tools
|
|
@@ -199,7 +201,7 @@ class ServerManager:
|
|
|
199
201
|
# Initialize server tools if not already initialized
|
|
200
202
|
if server_name not in self._server_tools:
|
|
201
203
|
connector = session.connector
|
|
202
|
-
self._server_tools[server_name] = await self.adapter.
|
|
204
|
+
self._server_tools[server_name] = await self.adapter._create_tools_from_connectors(
|
|
203
205
|
[connector]
|
|
204
206
|
)
|
|
205
207
|
self.initialized_servers[server_name] = True
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-use
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.7
|
|
4
4
|
Summary: MCP Library for LLMs
|
|
5
5
|
Author-email: Pietro Zullo <pietro.zullo@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -18,7 +18,7 @@ Requires-Dist: aiohttp>=3.9.0
|
|
|
18
18
|
Requires-Dist: jsonschema-pydantic>=0.1.0
|
|
19
19
|
Requires-Dist: langchain-community>=0.0.10
|
|
20
20
|
Requires-Dist: langchain>=0.1.0
|
|
21
|
-
Requires-Dist: mcp
|
|
21
|
+
Requires-Dist: mcp>=1.5.0
|
|
22
22
|
Requires-Dist: pydantic>=2.0.0
|
|
23
23
|
Requires-Dist: python-dotenv>=1.0.0
|
|
24
24
|
Requires-Dist: typing-extensions>=4.8.0
|
|
@@ -69,6 +69,7 @@ Description-Content-Type: text/markdown
|
|
|
69
69
|
| ⚙️ **Dynamic Server Selection** | Agents can dynamically choose the most appropriate MCP server for a given task from the available pool |
|
|
70
70
|
| 🧩 **Multi-Server Support** | Use multiple MCP servers simultaneously in a single agent |
|
|
71
71
|
| 🛡️ **Tool Restrictions** | Restrict potentially dangerous tools like file system or network access |
|
|
72
|
+
| 🔧 **Custom Agents** | Build your own agents with any framework using the LangChain adapter or create new adapters |
|
|
72
73
|
|
|
73
74
|
|
|
74
75
|
# Quick start
|
|
@@ -503,6 +504,41 @@ if __name__ == "__main__":
|
|
|
503
504
|
asyncio.run(main())
|
|
504
505
|
```
|
|
505
506
|
|
|
507
|
+
# Build a Custom Agent:
|
|
508
|
+
|
|
509
|
+
You can also build your own custom agent using the LangChain adapter:
|
|
510
|
+
|
|
511
|
+
```python
|
|
512
|
+
import asyncio
|
|
513
|
+
from langchain_openai import ChatOpenAI
|
|
514
|
+
from mcp_use.client import MCPClient
|
|
515
|
+
from mcp_use.adapters.langchain_adapter import LangChainAdapter
|
|
516
|
+
from dotenv import load_dotenv
|
|
517
|
+
|
|
518
|
+
load_dotenv()
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
async def main():
|
|
522
|
+
# Initialize MCP client
|
|
523
|
+
client = MCPClient.from_config_file("examples/browser_mcp.json")
|
|
524
|
+
llm = ChatOpenAI(model="gpt-4o")
|
|
525
|
+
|
|
526
|
+
# Create adapter instance
|
|
527
|
+
adapter = LangChainAdapter()
|
|
528
|
+
# Get LangChain tools with a single line
|
|
529
|
+
tools = await adapter.create_tools(client)
|
|
530
|
+
|
|
531
|
+
# Create a custom LangChain agent
|
|
532
|
+
llm_with_tools = llm.bind_tools(tools)
|
|
533
|
+
result = await llm_with_tools.ainvoke("What tools do you have avilable ? ")
|
|
534
|
+
print(result)
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
if __name__ == "__main__":
|
|
538
|
+
asyncio.run(main())
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
```
|
|
506
542
|
|
|
507
543
|
# Debugging
|
|
508
544
|
|
|
@@ -3,12 +3,13 @@ mcp_use/client.py,sha256=RoOOpCzMCjpqkkyAIEDOVc6Sn_HsET1rbn_J_J778q4,8278
|
|
|
3
3
|
mcp_use/config.py,sha256=O9V4pa-shZ2mPokRTrd7KZQ2GpuTcYBGUslefl1fosw,1653
|
|
4
4
|
mcp_use/logging.py,sha256=UhQdMx0H0q08-ZPjY_hAJVErkEUAkU1oahHqwdfdK_U,4274
|
|
5
5
|
mcp_use/session.py,sha256=Z4EZTUnQUX0QyGMzkJIrMRTX4SDk6qQUoBld408LIJE,3449
|
|
6
|
-
mcp_use/adapters/__init__.py,sha256
|
|
7
|
-
mcp_use/adapters/
|
|
6
|
+
mcp_use/adapters/__init__.py,sha256=-xCrgPThuX7x0PHGFDdjb7M-mgw6QV3sKu5PM7ShnRg,275
|
|
7
|
+
mcp_use/adapters/base.py,sha256=ixLHXp8FWdyZPx7Kh6s-4jEVs3qT4DWrApSLXfqzNws,6141
|
|
8
|
+
mcp_use/adapters/langchain_adapter.py,sha256=mbOkWHMgHJJNJYFXYLCk3JjIT0CRW_iiu5eZtxsWEmk,6309
|
|
8
9
|
mcp_use/agents/__init__.py,sha256=7QCfjE9WA50r-W8CS7IzUZMuhLgm8xSuKH1kYWdFU64,324
|
|
9
10
|
mcp_use/agents/base.py,sha256=bfuldi_89AbSbNc8KeTiCArRT9V62CNxHOWYkLHWjyA,1605
|
|
10
|
-
mcp_use/agents/mcpagent.py,sha256=
|
|
11
|
-
mcp_use/agents/server_manager.py,sha256
|
|
11
|
+
mcp_use/agents/mcpagent.py,sha256=kSNqmF728LrEYfzfvW8h8llqC877vJLcn-DJodYVucU,23988
|
|
12
|
+
mcp_use/agents/server_manager.py,sha256=ShmjrvDtmU7dJtfVlw_srC3t5f_B-QtifzIiV4mfsRA,11315
|
|
12
13
|
mcp_use/agents/prompts/system_prompt_builder.py,sha256=GH5Pvl49IBpKpZA9YTI83xMsdYSkRN_hw4LFHkKtxbg,4122
|
|
13
14
|
mcp_use/agents/prompts/templates.py,sha256=AZKrGWuI516C-PmyOPvxDBibNdqJtN24sOHTGR06bi4,1933
|
|
14
15
|
mcp_use/connectors/__init__.py,sha256=jnd-7pPPJMb0UNJ6aD9lInj5Tlamc8lA_mFyG8RWJpo,385
|
|
@@ -21,7 +22,7 @@ mcp_use/task_managers/base.py,sha256=ksNdxTwq8N-zqymxVoKGnWXq9iqkLYC61uB91o6Mh-4
|
|
|
21
22
|
mcp_use/task_managers/sse.py,sha256=WysmjwqRI3meXMZY_F4y9tSBMvSiUZfTJQfitM5l6jQ,2529
|
|
22
23
|
mcp_use/task_managers/stdio.py,sha256=DEISpXv4mo3d5a-WT8lkWbrXJwUh7QW0nMT_IM3fHGg,2269
|
|
23
24
|
mcp_use/task_managers/websocket.py,sha256=ZbCqdGgzCRtsXzRGFws-f2OzH8cPAkN4sJNDwEpRmCc,1915
|
|
24
|
-
mcp_use-1.2.
|
|
25
|
-
mcp_use-1.2.
|
|
26
|
-
mcp_use-1.2.
|
|
27
|
-
mcp_use-1.2.
|
|
25
|
+
mcp_use-1.2.7.dist-info/METADATA,sha256=GsNTID4AHT6Th_2Z1Us2tlcE9dKu4F9BxNTtFZK05jk,18163
|
|
26
|
+
mcp_use-1.2.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
27
|
+
mcp_use-1.2.7.dist-info/licenses/LICENSE,sha256=7Pw7dbwJSBw8zH-WE03JnR5uXvitRtaGTP9QWPcexcs,1068
|
|
28
|
+
mcp_use-1.2.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|