mcp-use 1.1.5__py3-none-any.whl → 1.2.6__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 CHANGED
@@ -11,7 +11,7 @@ from .agents.mcpagent import MCPAgent
11
11
  from .client import MCPClient
12
12
  from .config import load_config_file
13
13
  from .connectors import BaseConnector, HttpConnector, StdioConnector, WebSocketConnector
14
- from .logging import logger
14
+ from .logging import MCP_USE_DEBUG, Logger, logger
15
15
  from .session import MCPSession
16
16
 
17
17
  __version__ = version("mcp-use")
@@ -27,4 +27,17 @@ __all__ = [
27
27
  "create_session_from_config",
28
28
  "load_config_file",
29
29
  "logger",
30
+ "MCP_USE_DEBUG",
31
+ "Logger",
32
+ "set_debug",
30
33
  ]
34
+
35
+
36
+ # Helper function to set debug mode
37
+ def set_debug(debug=2):
38
+ """Set the debug mode for mcp_use.
39
+
40
+ Args:
41
+ debug: Whether to enable debug mode (default: True)
42
+ """
43
+ Logger.set_debug(debug)
@@ -0,0 +1,10 @@
1
+ """
2
+ Adapters for converting MCP tools to different frameworks.
3
+
4
+ This package provides adapters for converting MCP tools to different frameworks.
5
+ """
6
+
7
+ from .base import BaseAdapter
8
+ from .langchain_adapter import LangChainAdapter
9
+
10
+ __all__ = ["BaseAdapter", "LangChainAdapter"]
@@ -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
@@ -0,0 +1,161 @@
1
+ """
2
+ LangChain adapter for MCP tools.
3
+
4
+ This module provides utilities to convert MCP tools to LangChain tools.
5
+ """
6
+
7
+ from typing import Any, NoReturn
8
+
9
+ from jsonschema_pydantic import jsonschema_to_pydantic
10
+ from langchain_core.tools import BaseTool, ToolException
11
+ from mcp.types import CallToolResult, EmbeddedResource, ImageContent, TextContent
12
+ from pydantic import BaseModel
13
+
14
+ from ..connectors.base import BaseConnector
15
+ from ..logging import logger
16
+ from .base import BaseAdapter
17
+
18
+
19
+ class LangChainAdapter(BaseAdapter):
20
+ """Adapter for converting MCP tools to LangChain tools."""
21
+
22
+ def __init__(self, disallowed_tools: list[str] | None = None) -> None:
23
+ """Initialize a new LangChain adapter.
24
+
25
+ Args:
26
+ disallowed_tools: list of tool names that should not be available.
27
+ """
28
+ super().__init__(disallowed_tools)
29
+ self._connector_tool_map: dict[BaseConnector, list[BaseTool]] = {}
30
+
31
+ def fix_schema(self, schema: dict) -> dict:
32
+ """Convert JSON Schema 'type': ['string', 'null'] to 'anyOf' format.
33
+
34
+ Args:
35
+ schema: The JSON schema to fix.
36
+
37
+ Returns:
38
+ The fixed JSON schema.
39
+ """
40
+ if isinstance(schema, dict):
41
+ if "type" in schema and isinstance(schema["type"], list):
42
+ schema["anyOf"] = [{"type": t} for t in schema["type"]]
43
+ del schema["type"] # Remove 'type' and standardize to 'anyOf'
44
+ for key, value in schema.items():
45
+ schema[key] = self.fix_schema(value) # Apply recursively
46
+ return schema
47
+
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
+
97
+ Args:
98
+ mcp_tool: The MCP tool to convert.
99
+ connector: The connector that provides this tool.
100
+
101
+ Returns:
102
+ A LangChain BaseTool.
103
+ """
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
117
+ )
118
+ tool_connector: BaseConnector = connector # Renamed variable to avoid name conflict
119
+ handle_tool_error: bool = True
120
+
121
+ def _run(self, **kwargs: Any) -> NoReturn:
122
+ """Synchronous run method that always raises an error.
123
+
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")
129
+
130
+ async def _arun(self, **kwargs: Any) -> Any:
131
+ """Asynchronously execute the tool with given arguments.
132
+
133
+ Args:
134
+ kwargs: The arguments to pass to the tool.
135
+
136
+ Returns:
137
+ The result of the tool execution.
138
+
139
+ Raises:
140
+ ToolException: If tool execution fails.
141
+ """
142
+ logger.debug(f'MCP tool: "{self.name}" received input: {kwargs}')
143
+
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}"
155
+
156
+ except Exception as e:
157
+ if self.handle_tool_error:
158
+ return f"Error executing MCP tool: {str(e)}"
159
+ raise
160
+
161
+ return McpToLangChainAdapter()
@@ -6,7 +6,11 @@ that are pre-configured for using MCP tools.
6
6
  """
7
7
 
8
8
  from .base import BaseAgent
9
- from .langchain_agent import LangChainAgent
10
9
  from .mcpagent import MCPAgent
10
+ from .server_manager import ServerManager
11
11
 
12
- __all__ = ["BaseAgent", "LangChainAgent", "MCPAgent"]
12
+ __all__ = [
13
+ "BaseAgent",
14
+ "MCPAgent",
15
+ "ServerManager",
16
+ ]