mcp-use 0.0.3__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 +27 -0
- mcp_use/agents/__init__.py +12 -0
- mcp_use/agents/base.py +63 -0
- mcp_use/agents/langchain_agent.py +240 -0
- mcp_use/agents/mcpagent.py +149 -0
- mcp_use/client.py +222 -0
- mcp_use/config.py +93 -0
- mcp_use/connectors/__init__.py +13 -0
- mcp_use/connectors/base.py +59 -0
- mcp_use/connectors/http.py +126 -0
- mcp_use/connectors/stdio.py +124 -0
- mcp_use/connectors/websocket.py +142 -0
- mcp_use/logging.py +96 -0
- mcp_use/session.py +113 -0
- mcp_use-0.0.3.dist-info/METADATA +368 -0
- mcp_use-0.0.3.dist-info/RECORD +19 -0
- mcp_use-0.0.3.dist-info/WHEEL +5 -0
- mcp_use-0.0.3.dist-info/licenses/LICENSE +21 -0
- mcp_use-0.0.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WebSocket connector for MCP implementations.
|
|
3
|
+
|
|
4
|
+
This module provides a connector for communicating with MCP implementations
|
|
5
|
+
through WebSocket connections.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import uuid
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import websockets
|
|
14
|
+
from websockets.client import WebSocketClientProtocol
|
|
15
|
+
|
|
16
|
+
from .base import BaseConnector
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class WebSocketConnector(BaseConnector):
|
|
20
|
+
"""Connector for MCP implementations using WebSocket transport.
|
|
21
|
+
|
|
22
|
+
This connector uses WebSockets to communicate with remote MCP implementations.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self, url: str, auth_token: str | None = None, headers: dict[str, str] | None = None
|
|
27
|
+
):
|
|
28
|
+
"""Initialize a new WebSocket connector.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
url: The WebSocket URL to connect to.
|
|
32
|
+
auth_token: Optional authentication token.
|
|
33
|
+
headers: Optional additional headers.
|
|
34
|
+
"""
|
|
35
|
+
self.url = url
|
|
36
|
+
self.auth_token = auth_token
|
|
37
|
+
self.headers = headers or {}
|
|
38
|
+
if auth_token:
|
|
39
|
+
self.headers["Authorization"] = f"Bearer {auth_token}"
|
|
40
|
+
self.ws: WebSocketClientProtocol | None = None
|
|
41
|
+
self.pending_requests: dict[str, asyncio.Future] = {}
|
|
42
|
+
|
|
43
|
+
async def connect(self) -> None:
|
|
44
|
+
"""Establish a connection to the MCP implementation."""
|
|
45
|
+
self.ws = await websockets.connect(self.url, extra_headers=self.headers)
|
|
46
|
+
|
|
47
|
+
# Start the message receiver task
|
|
48
|
+
self._receiver_task = asyncio.create_task(self._receive_messages())
|
|
49
|
+
|
|
50
|
+
async def _receive_messages(self) -> None:
|
|
51
|
+
"""Continuously receive and process messages from the WebSocket."""
|
|
52
|
+
if not self.ws:
|
|
53
|
+
raise RuntimeError("WebSocket is not connected")
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
async for message in self.ws:
|
|
57
|
+
# Parse the message
|
|
58
|
+
data = json.loads(message)
|
|
59
|
+
|
|
60
|
+
# Check if this is a response to a pending request
|
|
61
|
+
request_id = data.get("id")
|
|
62
|
+
if request_id and request_id in self.pending_requests:
|
|
63
|
+
future = self.pending_requests.pop(request_id)
|
|
64
|
+
if "result" in data:
|
|
65
|
+
future.set_result(data["result"])
|
|
66
|
+
elif "error" in data:
|
|
67
|
+
future.set_exception(Exception(data["error"]))
|
|
68
|
+
except Exception as e:
|
|
69
|
+
# If the websocket connection was closed or errored,
|
|
70
|
+
# reject all pending requests
|
|
71
|
+
for future in self.pending_requests.values():
|
|
72
|
+
if not future.done():
|
|
73
|
+
future.set_exception(e)
|
|
74
|
+
|
|
75
|
+
async def disconnect(self) -> None:
|
|
76
|
+
"""Close the connection to the MCP implementation."""
|
|
77
|
+
if self._receiver_task:
|
|
78
|
+
self._receiver_task.cancel()
|
|
79
|
+
try:
|
|
80
|
+
await self._receiver_task
|
|
81
|
+
except asyncio.CancelledError:
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
if self.ws:
|
|
85
|
+
await self.ws.close()
|
|
86
|
+
self.ws = None
|
|
87
|
+
|
|
88
|
+
# Reject any pending requests
|
|
89
|
+
for future in self.pending_requests.values():
|
|
90
|
+
if not future.done():
|
|
91
|
+
future.set_exception(ConnectionError("WebSocket disconnected"))
|
|
92
|
+
self.pending_requests.clear()
|
|
93
|
+
|
|
94
|
+
async def _send_request(self, method: str, params: dict[str, Any] | None = None) -> Any:
|
|
95
|
+
"""Send a request and wait for a response."""
|
|
96
|
+
if not self.ws:
|
|
97
|
+
raise RuntimeError("WebSocket is not connected")
|
|
98
|
+
|
|
99
|
+
# Create a request ID
|
|
100
|
+
request_id = str(uuid.uuid4())
|
|
101
|
+
|
|
102
|
+
# Create a future to receive the response
|
|
103
|
+
future = asyncio.Future()
|
|
104
|
+
self.pending_requests[request_id] = future
|
|
105
|
+
|
|
106
|
+
# Send the request
|
|
107
|
+
await self.ws.send(json.dumps({"id": request_id, "method": method, "params": params or {}}))
|
|
108
|
+
|
|
109
|
+
# Wait for the response
|
|
110
|
+
try:
|
|
111
|
+
return await future
|
|
112
|
+
except Exception as e:
|
|
113
|
+
# Remove the request from pending requests
|
|
114
|
+
self.pending_requests.pop(request_id, None)
|
|
115
|
+
raise e
|
|
116
|
+
|
|
117
|
+
async def initialize(self) -> dict[str, Any]:
|
|
118
|
+
"""Initialize the MCP session and return session information."""
|
|
119
|
+
return await self._send_request("initialize")
|
|
120
|
+
|
|
121
|
+
async def list_tools(self) -> list[dict[str, Any]]:
|
|
122
|
+
"""List all available tools from the MCP implementation."""
|
|
123
|
+
result = await self._send_request("tools/list")
|
|
124
|
+
return result.get("tools", [])
|
|
125
|
+
|
|
126
|
+
async def call_tool(self, name: str, arguments: dict[str, Any]) -> Any:
|
|
127
|
+
"""Call an MCP tool with the given arguments."""
|
|
128
|
+
return await self._send_request("tools/call", {"name": name, "arguments": arguments})
|
|
129
|
+
|
|
130
|
+
async def list_resources(self) -> list[dict[str, Any]]:
|
|
131
|
+
"""List all available resources from the MCP implementation."""
|
|
132
|
+
result = await self._send_request("resources/list")
|
|
133
|
+
return result
|
|
134
|
+
|
|
135
|
+
async def read_resource(self, uri: str) -> tuple[bytes, str]:
|
|
136
|
+
"""Read a resource by URI."""
|
|
137
|
+
result = await self._send_request("resources/read", {"uri": uri})
|
|
138
|
+
return result.get("content", b""), result.get("mimeType", "")
|
|
139
|
+
|
|
140
|
+
async def request(self, method: str, params: dict[str, Any] | None = None) -> Any:
|
|
141
|
+
"""Send a raw request to the MCP implementation."""
|
|
142
|
+
return await self._send_request(method, params)
|
mcp_use/logging.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logger module for mcp_use.
|
|
3
|
+
|
|
4
|
+
This module provides a centralized logging configuration for the mcp_use library,
|
|
5
|
+
with customizable log levels and formatters.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Logger:
|
|
14
|
+
"""Centralized logger for mcp_use.
|
|
15
|
+
|
|
16
|
+
This class provides logging functionality with configurable levels,
|
|
17
|
+
formatters, and handlers.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Default log format
|
|
21
|
+
DEFAULT_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
22
|
+
|
|
23
|
+
# Module-specific loggers
|
|
24
|
+
_loggers = {}
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def get_logger(cls, name: str = "mcp_use") -> logging.Logger:
|
|
28
|
+
"""Get a logger instance for the specified name.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
name: Logger name, usually the module name (defaults to 'mcp_use')
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Configured logger instance
|
|
35
|
+
"""
|
|
36
|
+
if name in cls._loggers:
|
|
37
|
+
return cls._loggers[name]
|
|
38
|
+
|
|
39
|
+
# Create new logger
|
|
40
|
+
logger = logging.getLogger(name)
|
|
41
|
+
cls._loggers[name] = logger
|
|
42
|
+
|
|
43
|
+
return logger
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def configure(
|
|
47
|
+
cls,
|
|
48
|
+
level: int | str = logging.INFO,
|
|
49
|
+
format_str: str | None = None,
|
|
50
|
+
log_to_console: bool = True,
|
|
51
|
+
log_to_file: str | None = None,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Configure the root mcp_use logger.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
level: Log level (default: INFO)
|
|
57
|
+
format_str: Log format string (default: DEFAULT_FORMAT)
|
|
58
|
+
log_to_console: Whether to log to console (default: True)
|
|
59
|
+
log_to_file: Path to log file (default: None)
|
|
60
|
+
"""
|
|
61
|
+
root_logger = cls.get_logger()
|
|
62
|
+
|
|
63
|
+
# Set level
|
|
64
|
+
if isinstance(level, str):
|
|
65
|
+
level = getattr(logging, level.upper())
|
|
66
|
+
root_logger.setLevel(level)
|
|
67
|
+
|
|
68
|
+
# Clear existing handlers
|
|
69
|
+
for handler in root_logger.handlers[:]:
|
|
70
|
+
root_logger.removeHandler(handler)
|
|
71
|
+
|
|
72
|
+
# Set formatter
|
|
73
|
+
formatter = logging.Formatter(format_str or cls.DEFAULT_FORMAT)
|
|
74
|
+
|
|
75
|
+
# Add console handler if requested
|
|
76
|
+
if log_to_console:
|
|
77
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
|
78
|
+
console_handler.setFormatter(formatter)
|
|
79
|
+
root_logger.addHandler(console_handler)
|
|
80
|
+
|
|
81
|
+
# Add file handler if requested
|
|
82
|
+
if log_to_file:
|
|
83
|
+
# Ensure directory exists
|
|
84
|
+
log_dir = os.path.dirname(log_to_file)
|
|
85
|
+
if log_dir and not os.path.exists(log_dir):
|
|
86
|
+
os.makedirs(log_dir)
|
|
87
|
+
|
|
88
|
+
file_handler = logging.FileHandler(log_to_file)
|
|
89
|
+
file_handler.setFormatter(formatter)
|
|
90
|
+
root_logger.addHandler(file_handler)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Configure default logger
|
|
94
|
+
Logger.configure()
|
|
95
|
+
|
|
96
|
+
logger = Logger.get_logger()
|
mcp_use/session.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session manager for MCP connections.
|
|
3
|
+
|
|
4
|
+
This module provides a session manager for MCP connections,
|
|
5
|
+
which handles authentication, initialization, and tool discovery.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from .connectors.base import BaseConnector
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MCPSession:
|
|
14
|
+
"""Session manager for MCP connections.
|
|
15
|
+
|
|
16
|
+
This class manages the lifecycle of an MCP connection, including
|
|
17
|
+
authentication, initialization, and tool discovery.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
connector: BaseConnector,
|
|
23
|
+
auto_connect: bool = True,
|
|
24
|
+
) -> None:
|
|
25
|
+
"""Initialize a new MCP session.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
connector: The connector to use for communicating with the MCP implementation.
|
|
29
|
+
auto_connect: Whether to automatically connect to the MCP implementation.
|
|
30
|
+
"""
|
|
31
|
+
self.connector = connector
|
|
32
|
+
self.session_info: dict[str, Any] | None = None
|
|
33
|
+
self.tools: list[dict[str, Any]] = []
|
|
34
|
+
self.auto_connect = auto_connect
|
|
35
|
+
|
|
36
|
+
async def __aenter__(self) -> "MCPSession":
|
|
37
|
+
"""Enter the async context manager.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
The session instance.
|
|
41
|
+
"""
|
|
42
|
+
await self.connect()
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
46
|
+
"""Exit the async context manager.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
exc_type: The exception type, if an exception was raised.
|
|
50
|
+
exc_val: The exception value, if an exception was raised.
|
|
51
|
+
exc_tb: The exception traceback, if an exception was raised.
|
|
52
|
+
"""
|
|
53
|
+
await self.disconnect()
|
|
54
|
+
|
|
55
|
+
async def connect(self) -> None:
|
|
56
|
+
"""Connect to the MCP implementation."""
|
|
57
|
+
await self.connector.connect()
|
|
58
|
+
|
|
59
|
+
async def disconnect(self) -> None:
|
|
60
|
+
"""Disconnect from the MCP implementation."""
|
|
61
|
+
await self.connector.disconnect()
|
|
62
|
+
|
|
63
|
+
async def initialize(self) -> dict[str, Any]:
|
|
64
|
+
"""Initialize the MCP session and discover available tools.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
The session information returned by the MCP implementation.
|
|
68
|
+
"""
|
|
69
|
+
# Make sure we're connected
|
|
70
|
+
if not self.is_connected and self.auto_connect:
|
|
71
|
+
await self.connect()
|
|
72
|
+
|
|
73
|
+
# Initialize the session
|
|
74
|
+
self.session_info = await self.connector.initialize()
|
|
75
|
+
|
|
76
|
+
# Discover available tools
|
|
77
|
+
await self.discover_tools()
|
|
78
|
+
|
|
79
|
+
return self.session_info
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def is_connected(self) -> bool:
|
|
83
|
+
"""Check if the connector is connected.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
True if the connector is connected, False otherwise.
|
|
87
|
+
"""
|
|
88
|
+
return hasattr(self.connector, "client") and self.connector.client is not None
|
|
89
|
+
|
|
90
|
+
async def discover_tools(self) -> list[dict[str, Any]]:
|
|
91
|
+
"""Discover available tools from the MCP implementation.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
The list of available tools in MCP format.
|
|
95
|
+
"""
|
|
96
|
+
self.tools = self.connector.tools
|
|
97
|
+
return self.tools
|
|
98
|
+
|
|
99
|
+
async def call_tool(self, name: str, arguments: dict[str, Any]) -> Any:
|
|
100
|
+
"""Call an MCP tool with the given arguments.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
name: The name of the tool to call.
|
|
104
|
+
arguments: The arguments to pass to the tool.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
The result of the tool call.
|
|
108
|
+
"""
|
|
109
|
+
# Make sure we're connected
|
|
110
|
+
if not self.is_connected and self.auto_connect:
|
|
111
|
+
await self.connect()
|
|
112
|
+
|
|
113
|
+
return await self.connector.call_tool(name, arguments)
|