mcp-use 1.3.11__py3-none-any.whl → 1.3.13__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.

Files changed (101) hide show
  1. mcp_use/__init__.py +1 -1
  2. mcp_use/adapters/.deprecated +0 -0
  3. mcp_use/adapters/__init__.py +18 -7
  4. mcp_use/adapters/base.py +12 -185
  5. mcp_use/adapters/langchain_adapter.py +12 -264
  6. mcp_use/agents/adapters/__init__.py +10 -0
  7. mcp_use/agents/adapters/base.py +193 -0
  8. mcp_use/agents/adapters/langchain_adapter.py +228 -0
  9. mcp_use/agents/base.py +1 -1
  10. mcp_use/agents/managers/__init__.py +19 -0
  11. mcp_use/agents/managers/base.py +36 -0
  12. mcp_use/agents/managers/server_manager.py +131 -0
  13. mcp_use/agents/managers/tools/__init__.py +15 -0
  14. mcp_use/agents/managers/tools/base_tool.py +19 -0
  15. mcp_use/agents/managers/tools/connect_server.py +69 -0
  16. mcp_use/agents/managers/tools/disconnect_server.py +43 -0
  17. mcp_use/agents/managers/tools/get_active_server.py +29 -0
  18. mcp_use/agents/managers/tools/list_servers_tool.py +53 -0
  19. mcp_use/agents/managers/tools/search_tools.py +328 -0
  20. mcp_use/agents/mcpagent.py +88 -47
  21. mcp_use/agents/remote.py +168 -129
  22. mcp_use/auth/.deprecated +0 -0
  23. mcp_use/auth/__init__.py +19 -4
  24. mcp_use/auth/bearer.py +11 -12
  25. mcp_use/auth/oauth.py +11 -620
  26. mcp_use/auth/oauth_callback.py +16 -207
  27. mcp_use/client/__init__.py +1 -0
  28. mcp_use/client/auth/__init__.py +6 -0
  29. mcp_use/client/auth/bearer.py +23 -0
  30. mcp_use/client/auth/oauth.py +629 -0
  31. mcp_use/client/auth/oauth_callback.py +214 -0
  32. mcp_use/client/client.py +356 -0
  33. mcp_use/client/config.py +106 -0
  34. mcp_use/client/connectors/__init__.py +20 -0
  35. mcp_use/client/connectors/base.py +470 -0
  36. mcp_use/client/connectors/http.py +304 -0
  37. mcp_use/client/connectors/sandbox.py +332 -0
  38. mcp_use/client/connectors/stdio.py +109 -0
  39. mcp_use/client/connectors/utils.py +13 -0
  40. mcp_use/client/connectors/websocket.py +257 -0
  41. mcp_use/client/exceptions.py +31 -0
  42. mcp_use/client/middleware/__init__.py +50 -0
  43. mcp_use/client/middleware/logging.py +31 -0
  44. mcp_use/client/middleware/metrics.py +314 -0
  45. mcp_use/client/middleware/middleware.py +266 -0
  46. mcp_use/client/session.py +162 -0
  47. mcp_use/client/task_managers/__init__.py +20 -0
  48. mcp_use/client/task_managers/base.py +145 -0
  49. mcp_use/client/task_managers/sse.py +84 -0
  50. mcp_use/client/task_managers/stdio.py +69 -0
  51. mcp_use/client/task_managers/streamable_http.py +86 -0
  52. mcp_use/client/task_managers/websocket.py +68 -0
  53. mcp_use/client.py +12 -320
  54. mcp_use/config.py +20 -92
  55. mcp_use/connectors/.deprecated +0 -0
  56. mcp_use/connectors/__init__.py +46 -20
  57. mcp_use/connectors/base.py +12 -447
  58. mcp_use/connectors/http.py +13 -288
  59. mcp_use/connectors/sandbox.py +13 -297
  60. mcp_use/connectors/stdio.py +13 -96
  61. mcp_use/connectors/utils.py +15 -8
  62. mcp_use/connectors/websocket.py +13 -252
  63. mcp_use/exceptions.py +33 -18
  64. mcp_use/managers/.deprecated +0 -0
  65. mcp_use/managers/__init__.py +56 -17
  66. mcp_use/managers/base.py +13 -31
  67. mcp_use/managers/server_manager.py +13 -119
  68. mcp_use/managers/tools/__init__.py +45 -15
  69. mcp_use/managers/tools/base_tool.py +5 -16
  70. mcp_use/managers/tools/connect_server.py +5 -67
  71. mcp_use/managers/tools/disconnect_server.py +5 -41
  72. mcp_use/managers/tools/get_active_server.py +5 -26
  73. mcp_use/managers/tools/list_servers_tool.py +5 -51
  74. mcp_use/managers/tools/search_tools.py +17 -321
  75. mcp_use/middleware/.deprecated +0 -0
  76. mcp_use/middleware/__init__.py +89 -0
  77. mcp_use/middleware/logging.py +19 -0
  78. mcp_use/middleware/metrics.py +41 -0
  79. mcp_use/middleware/middleware.py +55 -0
  80. mcp_use/session.py +13 -149
  81. mcp_use/task_managers/.deprecated +0 -0
  82. mcp_use/task_managers/__init__.py +48 -20
  83. mcp_use/task_managers/base.py +13 -140
  84. mcp_use/task_managers/sse.py +13 -79
  85. mcp_use/task_managers/stdio.py +13 -64
  86. mcp_use/task_managers/streamable_http.py +15 -81
  87. mcp_use/task_managers/websocket.py +13 -63
  88. mcp_use/telemetry/events.py +58 -0
  89. mcp_use/telemetry/telemetry.py +71 -1
  90. mcp_use/types/.deprecated +0 -0
  91. mcp_use/types/sandbox.py +13 -18
  92. {mcp_use-1.3.11.dist-info → mcp_use-1.3.13.dist-info}/METADATA +66 -40
  93. mcp_use-1.3.13.dist-info/RECORD +109 -0
  94. mcp_use-1.3.11.dist-info/RECORD +0 -60
  95. mcp_use-1.3.11.dist-info/licenses/LICENSE +0 -21
  96. /mcp_use/{observability → agents/observability}/__init__.py +0 -0
  97. /mcp_use/{observability → agents/observability}/callbacks_manager.py +0 -0
  98. /mcp_use/{observability → agents/observability}/laminar.py +0 -0
  99. /mcp_use/{observability → agents/observability}/langfuse.py +0 -0
  100. {mcp_use-1.3.11.dist-info → mcp_use-1.3.13.dist-info}/WHEEL +0 -0
  101. {mcp_use-1.3.11.dist-info → mcp_use-1.3.13.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,162 @@
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 datetime import timedelta
9
+ from typing import Any
10
+
11
+ from mcp.types import CallToolResult, GetPromptResult, Prompt, ReadResourceResult, Resource, Tool
12
+ from pydantic import AnyUrl
13
+
14
+ from mcp_use.client.connectors.base import BaseConnector
15
+ from mcp_use.telemetry.telemetry import telemetry
16
+
17
+
18
+ class MCPSession:
19
+ """Session manager for MCP connections.
20
+
21
+ This class manages the lifecycle of an MCP connection, including
22
+ authentication, initialization, and tool discovery.
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ connector: BaseConnector,
28
+ auto_connect: bool = True,
29
+ ) -> None:
30
+ """Initialize a new MCP session.
31
+
32
+ Args:
33
+ connector: The connector to use for communicating with the MCP implementation.
34
+ auto_connect: Whether to automatically connect to the MCP implementation.
35
+ """
36
+ self.connector = connector
37
+ self.session_info: dict[str, Any] | None = None
38
+ self.auto_connect = auto_connect
39
+
40
+ async def __aenter__(self) -> "MCPSession":
41
+ """Enter the async context manager.
42
+
43
+ Returns:
44
+ The session instance.
45
+ """
46
+ await self.connect()
47
+ return self
48
+
49
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
50
+ """Exit the async context manager.
51
+
52
+ Args:
53
+ exc_type: The exception type, if an exception was raised.
54
+ exc_val: The exception value, if an exception was raised.
55
+ exc_tb: The exception traceback, if an exception was raised.
56
+ """
57
+ await self.disconnect()
58
+
59
+ async def connect(self) -> None:
60
+ """Connect to the MCP implementation."""
61
+ await self.connector.connect()
62
+
63
+ async def disconnect(self) -> None:
64
+ """Disconnect from the MCP implementation."""
65
+ await self.connector.disconnect()
66
+
67
+ @telemetry("session_initialize")
68
+ async def initialize(self) -> dict[str, Any]:
69
+ """Initialize the MCP session and discover available tools.
70
+
71
+ Returns:
72
+ The session information returned by the MCP implementation.
73
+ """
74
+ # Make sure we're connected
75
+ if not self.is_connected and self.auto_connect:
76
+ await self.connect()
77
+
78
+ # Initialize the session
79
+ self.session_info = await self.connector.initialize()
80
+
81
+ return self.session_info
82
+
83
+ @property
84
+ def is_connected(self) -> bool:
85
+ """Check if the connector is connected.
86
+
87
+ Returns:
88
+ True if the connector is connected, False otherwise.
89
+ """
90
+ return self.connector.is_connected
91
+
92
+ # Convenience methods for MCP operations
93
+ @telemetry("session_call_tool")
94
+ async def call_tool(
95
+ self, name: str, arguments: dict[str, Any], read_timeout_seconds: timedelta | None = None
96
+ ) -> CallToolResult:
97
+ """Call an MCP tool.
98
+
99
+ Args:
100
+ name: The name of the tool to call.
101
+ arguments: The arguments to pass to the tool.
102
+ read_timeout_seconds: Optional timeout for the tool call.
103
+
104
+ Returns:
105
+ The result of the tool call.
106
+
107
+ Raises:
108
+ RuntimeError: If the connection is lost and cannot be reestablished.
109
+ """
110
+ return await self.connector.call_tool(name, arguments, read_timeout_seconds)
111
+
112
+ @telemetry("session_list_tools")
113
+ async def list_tools(self) -> list[Tool]:
114
+ """List all available tools from the MCP server.
115
+
116
+ Returns:
117
+ List of available tools.
118
+ """
119
+ return await self.connector.list_tools()
120
+
121
+ @telemetry("session_list_resources")
122
+ async def list_resources(self) -> list[Resource]:
123
+ """List all available resources from the MCP server.
124
+
125
+ Returns:
126
+ List of available resources.
127
+ """
128
+ return await self.connector.list_resources()
129
+
130
+ @telemetry("session_read_resource")
131
+ async def read_resource(self, uri: AnyUrl) -> ReadResourceResult:
132
+ """Read a resource by URI.
133
+
134
+ Args:
135
+ uri: The URI of the resource to read.
136
+
137
+ Returns:
138
+ The resource content.
139
+ """
140
+ return await self.connector.read_resource(uri)
141
+
142
+ @telemetry("session_list_prompts")
143
+ async def list_prompts(self) -> list[Prompt]:
144
+ """List all available prompts from the MCP server.
145
+
146
+ Returns:
147
+ List of available prompts.
148
+ """
149
+ return await self.connector.list_prompts()
150
+
151
+ @telemetry("session_get_prompt")
152
+ async def get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> GetPromptResult:
153
+ """Get a prompt by name.
154
+
155
+ Args:
156
+ name: The name of the prompt to get.
157
+ arguments: Optional arguments for the prompt.
158
+
159
+ Returns:
160
+ The prompt result with messages.
161
+ """
162
+ return await self.connector.get_prompt(name, arguments)
@@ -0,0 +1,20 @@
1
+ """
2
+ Connectors for various MCP transports.
3
+
4
+ This module provides interfaces for connecting to MCP implementations
5
+ through different transport mechanisms.
6
+ """
7
+
8
+ from .base import ConnectionManager
9
+ from .sse import SseConnectionManager
10
+ from .stdio import StdioConnectionManager
11
+ from .streamable_http import StreamableHttpConnectionManager
12
+ from .websocket import WebSocketConnectionManager
13
+
14
+ __all__ = [
15
+ "ConnectionManager",
16
+ "StdioConnectionManager",
17
+ "WebSocketConnectionManager",
18
+ "SseConnectionManager",
19
+ "StreamableHttpConnectionManager",
20
+ ]
@@ -0,0 +1,145 @@
1
+ """
2
+ Connection management for MCP implementations.
3
+
4
+ This module provides an abstract base class for different types of connection
5
+ managers used in MCP connectors.
6
+ """
7
+
8
+ import asyncio
9
+ from abc import ABC, abstractmethod
10
+ from typing import Generic, TypeVar
11
+
12
+ from mcp_use.logging import logger
13
+
14
+ # Type variable for connection types
15
+ T = TypeVar("T")
16
+
17
+
18
+ class ConnectionManager(Generic[T], ABC):
19
+ """Abstract base class for connection managers.
20
+
21
+ This class defines the interface for different types of connection managers
22
+ used with MCP connectors.
23
+ """
24
+
25
+ def __init__(self) -> None:
26
+ """Initialize a new connection manager."""
27
+ self._ready_event = asyncio.Event()
28
+ self._done_event = asyncio.Event()
29
+ self._stop_event = asyncio.Event()
30
+ self._exception: Exception | None = None
31
+ self._connection: T | None = None
32
+ self._task: asyncio.Task[None] | None = None
33
+
34
+ @abstractmethod
35
+ async def _establish_connection(self) -> T:
36
+ """Establish the connection.
37
+
38
+ This method should be implemented by subclasses to establish
39
+ the specific type of connection needed.
40
+
41
+ Returns:
42
+ The established connection.
43
+
44
+ Raises:
45
+ Exception: If connection cannot be established.
46
+ """
47
+ pass
48
+
49
+ @abstractmethod
50
+ async def _close_connection(self) -> None:
51
+ """Close the connection.
52
+
53
+ This method should be implemented by subclasses to close
54
+ the specific type of connection.
55
+
56
+ """
57
+ pass
58
+
59
+ async def start(self) -> T:
60
+ """Start the connection manager and establish a connection.
61
+
62
+ Returns:
63
+ The established connection.
64
+
65
+ Raises:
66
+ Exception: If connection cannot be established.
67
+ """
68
+ # Reset state
69
+ self._ready_event.clear()
70
+ self._done_event.clear()
71
+ self._exception = None
72
+
73
+ # Create a task to establish and maintain the connection
74
+ self._task = asyncio.create_task(self._connection_task(), name=f"{self.__class__.__name__}_task")
75
+
76
+ # Wait for the connection to be ready or fail
77
+ await self._ready_event.wait()
78
+
79
+ # If there was an exception, raise it
80
+ if self._exception:
81
+ raise self._exception
82
+
83
+ # Return the connection
84
+ if self._connection is None:
85
+ raise RuntimeError("Connection was not established")
86
+ return self._connection
87
+
88
+ async def stop(self) -> None:
89
+ """Stop the connection manager and close the connection."""
90
+ # Signal stop to the connection task instead of cancelling it, avoids
91
+ # propagating CancelledError to unrelated tasks.
92
+ if self._task and not self._task.done():
93
+ logger.debug(f"Signaling stop to {self.__class__.__name__} task")
94
+ self._stop_event.set()
95
+ # Wait for it to finish gracefully
96
+ await self._task
97
+
98
+ # Ensure cleanup completed
99
+ await self._done_event.wait()
100
+ logger.debug(f"{self.__class__.__name__} task completed")
101
+
102
+ def get_streams(self) -> T | None:
103
+ """Get the current connection streams.
104
+
105
+ Returns:
106
+ The current connection (typically a tuple of read_stream, write_stream) or None if not connected.
107
+ """
108
+ return self._connection
109
+
110
+ async def _connection_task(self) -> None:
111
+ """Run the connection task.
112
+
113
+ This task establishes and maintains the connection until cancelled.
114
+ """
115
+ logger.debug(f"Starting {self.__class__.__name__} task")
116
+ try:
117
+ # Establish the connection
118
+ self._connection = await self._establish_connection()
119
+ logger.debug(f"{self.__class__.__name__} connected successfully")
120
+
121
+ # Signal that the connection is ready
122
+ self._ready_event.set()
123
+
124
+ # Wait until stop is requested
125
+ await self._stop_event.wait()
126
+
127
+ except Exception as e:
128
+ # Store the exception
129
+ self._exception = e
130
+ logger.error(f"Error in {self.__class__.__name__} task: {e}")
131
+
132
+ # Signal that the connection is ready (with error)
133
+ self._ready_event.set()
134
+
135
+ finally:
136
+ # Close the connection if it was established
137
+ if self._connection is not None:
138
+ try:
139
+ await self._close_connection()
140
+ except Exception as e:
141
+ logger.warning(f"Error closing connection in {self.__class__.__name__}: {e}")
142
+ self._connection = None
143
+
144
+ # Signal that the connection is done
145
+ self._done_event.set()
@@ -0,0 +1,84 @@
1
+ """
2
+ SSE connection management for MCP implementations.
3
+
4
+ This module provides a connection manager for SSE-based MCP connections
5
+ that ensures proper task isolation and resource cleanup.
6
+ """
7
+
8
+ from typing import Any
9
+
10
+ import httpx
11
+ from mcp.client.sse import sse_client
12
+
13
+ from mcp_use.client.task_managers.base import ConnectionManager
14
+ from mcp_use.logging import logger
15
+
16
+
17
+ class SseConnectionManager(ConnectionManager[tuple[Any, Any]]):
18
+ """Connection manager for SSE-based MCP connections.
19
+
20
+ This class handles the proper task isolation for sse_client context managers
21
+ to prevent the "cancel scope in different task" error. It runs the sse_client
22
+ in a dedicated task and manages its lifecycle.
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ url: str,
28
+ headers: dict[str, str] | None = None,
29
+ timeout: float = 5,
30
+ sse_read_timeout: float = 60 * 5,
31
+ auth: httpx.Auth | None = None,
32
+ ):
33
+ """Initialize a new SSE connection manager.
34
+
35
+ Args:
36
+ url: The SSE endpoint URL
37
+ headers: Optional HTTP headers
38
+ timeout: Timeout for HTTP operations in seconds
39
+ sse_read_timeout: Timeout for SSE read operations in seconds
40
+ auth: Optional httpx.Auth instance for authentication
41
+ """
42
+ super().__init__()
43
+ self.url = url
44
+ self.headers = headers or {}
45
+ self.timeout = timeout
46
+ self.sse_read_timeout = sse_read_timeout
47
+ self.auth = auth
48
+ self._sse_ctx = None
49
+
50
+ async def _establish_connection(self) -> tuple[Any, Any]:
51
+ """Establish an SSE connection.
52
+
53
+ Returns:
54
+ A tuple of (read_stream, write_stream)
55
+
56
+ Raises:
57
+ Exception: If connection cannot be established.
58
+ """
59
+ # Create the context manager
60
+ self._sse_ctx = sse_client(
61
+ url=self.url,
62
+ headers=self.headers,
63
+ timeout=self.timeout,
64
+ sse_read_timeout=self.sse_read_timeout,
65
+ auth=self.auth,
66
+ )
67
+
68
+ # Enter the context manager
69
+ read_stream, write_stream = await self._sse_ctx.__aenter__()
70
+
71
+ # Return the streams
72
+ return (read_stream, write_stream)
73
+
74
+ async def _close_connection(self) -> None:
75
+ """Close the SSE connection."""
76
+
77
+ if self._sse_ctx:
78
+ # Exit the context manager
79
+ try:
80
+ await self._sse_ctx.__aexit__(None, None, None)
81
+ except Exception as e:
82
+ logger.warning(f"Error closing SSE context: {e}")
83
+ finally:
84
+ self._sse_ctx = None
@@ -0,0 +1,69 @@
1
+ """
2
+ StdIO connection management for MCP implementations.
3
+
4
+ This module provides a connection manager for stdio-based MCP connections
5
+ that ensures proper task isolation and resource cleanup.
6
+ """
7
+
8
+ import sys
9
+ from typing import Any, TextIO
10
+
11
+ from mcp import StdioServerParameters
12
+ from mcp.client.stdio import stdio_client
13
+
14
+ from mcp_use.client.task_managers.base import ConnectionManager
15
+ from mcp_use.logging import logger
16
+
17
+
18
+ class StdioConnectionManager(ConnectionManager[tuple[Any, Any]]):
19
+ """Connection manager for stdio-based MCP connections.
20
+
21
+ This class handles the proper task isolation for stdio_client context managers
22
+ to prevent the "cancel scope in different task" error. It runs the stdio_client
23
+ in a dedicated task and manages its lifecycle.
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ server_params: StdioServerParameters,
29
+ errlog: TextIO = sys.stderr,
30
+ ):
31
+ """Initialize a new stdio connection manager.
32
+
33
+ Args:
34
+ server_params: The parameters for the stdio server
35
+ errlog: The error log stream
36
+ """
37
+ super().__init__()
38
+ self.server_params = server_params
39
+ self.errlog = errlog
40
+ self._stdio_ctx = None
41
+
42
+ async def _establish_connection(self) -> tuple[Any, Any]:
43
+ """Establish a stdio connection.
44
+
45
+ Returns:
46
+ A tuple of (read_stream, write_stream)
47
+
48
+ Raises:
49
+ Exception: If connection cannot be established.
50
+ """
51
+ # Create the context manager
52
+ self._stdio_ctx = stdio_client(self.server_params, self.errlog)
53
+
54
+ # Enter the context manager
55
+ read_stream, write_stream = await self._stdio_ctx.__aenter__()
56
+
57
+ # Return the streams
58
+ return (read_stream, write_stream)
59
+
60
+ async def _close_connection(self) -> None:
61
+ """Close the stdio connection."""
62
+ if self._stdio_ctx:
63
+ # Exit the context manager
64
+ try:
65
+ await self._stdio_ctx.__aexit__(None, None, None)
66
+ except Exception as e:
67
+ logger.warning(f"Error closing stdio context: {e}")
68
+ finally:
69
+ self._stdio_ctx = None
@@ -0,0 +1,86 @@
1
+ """
2
+ Streamable HTTP connection management for MCP implementations.
3
+
4
+ This module provides a connection manager for streamable HTTP-based MCP connections
5
+ that ensures proper task isolation and resource cleanup.
6
+ """
7
+
8
+ from datetime import timedelta
9
+ from typing import Any
10
+
11
+ import httpx
12
+ from mcp.client.streamable_http import streamablehttp_client
13
+
14
+ from mcp_use.client.task_managers.base import ConnectionManager
15
+ from mcp_use.logging import logger
16
+
17
+
18
+ class StreamableHttpConnectionManager(ConnectionManager[tuple[Any, Any]]):
19
+ """Connection manager for streamable HTTP-based MCP connections.
20
+
21
+ This class handles the proper task isolation for HTTP streaming connections
22
+ to prevent the "cancel scope in different task" error. It runs the http_stream_client
23
+ in a dedicated task and manages its lifecycle.
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ url: str,
29
+ headers: dict[str, str] | None = None,
30
+ timeout: float = 5,
31
+ read_timeout: float = 60 * 5,
32
+ auth: httpx.Auth | None = None,
33
+ ):
34
+ """Initialize a new streamable HTTP connection manager.
35
+
36
+ Args:
37
+ url: The HTTP endpoint URL
38
+ headers: Optional HTTP headers
39
+ timeout: Timeout for HTTP operations in seconds
40
+ read_timeout: Timeout for HTTP read operations in seconds
41
+ auth: Optional httpx.Auth instance for authentication
42
+ """
43
+ super().__init__()
44
+ self.url = url
45
+ self.headers = headers or {}
46
+ self.timeout = timedelta(seconds=timeout)
47
+ self.read_timeout = timedelta(seconds=read_timeout)
48
+ self.auth = auth
49
+ self._http_ctx = None
50
+
51
+ async def _establish_connection(self) -> tuple[Any, Any]:
52
+ """Establish a streamable HTTP connection.
53
+
54
+ Returns:
55
+ A tuple of (read_stream, write_stream)
56
+
57
+ Raises:
58
+ Exception: If connection cannot be established.
59
+ """
60
+ # Create the context manager
61
+ self._http_ctx = streamablehttp_client(
62
+ url=self.url,
63
+ headers=self.headers,
64
+ timeout=self.timeout,
65
+ sse_read_timeout=self.read_timeout,
66
+ auth=self.auth,
67
+ )
68
+
69
+ # Enter the context manager. Ignoring the session id callback
70
+ read_stream, write_stream, _ = await self._http_ctx.__aenter__()
71
+
72
+ # Return the streams
73
+ return (read_stream, write_stream)
74
+
75
+ async def _close_connection(self) -> None:
76
+ """Close the streamable HTTP connection."""
77
+
78
+ if self._http_ctx:
79
+ # Exit the context manager
80
+ try:
81
+ await self._http_ctx.__aexit__(None, None, None)
82
+ except Exception as e:
83
+ # Only log if it's not a normal connection termination
84
+ logger.debug(f"Streamable HTTP context cleanup: {e}")
85
+ finally:
86
+ self._http_ctx = None
@@ -0,0 +1,68 @@
1
+ """
2
+ WebSocket connection management for MCP implementations.
3
+
4
+ This module provides a connection manager for WebSocket-based MCP connections.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from mcp.client.websocket import websocket_client
10
+
11
+ from mcp_use.client.task_managers.base import ConnectionManager
12
+ from mcp_use.logging import logger
13
+
14
+
15
+ class WebSocketConnectionManager(ConnectionManager[tuple[Any, Any]]):
16
+ """Connection manager for WebSocket-based MCP connections.
17
+
18
+ This class handles the lifecycle of WebSocket connections, ensuring proper
19
+ connection establishment and cleanup.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ url: str,
25
+ headers: dict[str, str] | None = None,
26
+ ):
27
+ """Initialize a new WebSocket connection manager.
28
+
29
+ Args:
30
+ url: The WebSocket URL to connect to
31
+ headers: Optional HTTP headers
32
+ """
33
+ super().__init__()
34
+ self.url = url
35
+ self.headers = headers or {}
36
+
37
+ async def _establish_connection(self) -> tuple[Any, Any]:
38
+ """Establish a WebSocket connection.
39
+
40
+ Returns:
41
+ The established WebSocket connection
42
+
43
+ Raises:
44
+ Exception: If connection cannot be established
45
+ """
46
+ logger.debug(f"Connecting to WebSocket: {self.url}")
47
+ # Create the context manager
48
+ # Note: The current MCP websocket_client implementation doesn't support headers
49
+ # If headers need to be passed, this would need to be updated when MCP supports it
50
+ self._ws_ctx = websocket_client(self.url)
51
+
52
+ # Enter the context manager
53
+ read_stream, write_stream = await self._ws_ctx.__aenter__()
54
+
55
+ # Return the streams
56
+ return (read_stream, write_stream)
57
+
58
+ async def _close_connection(self) -> None:
59
+ """Close the WebSocket connection."""
60
+ if self._ws_ctx:
61
+ # Exit the context manager
62
+ try:
63
+ logger.debug("Closing WebSocket connection")
64
+ await self._ws_ctx.__aexit__(None, None, None)
65
+ except Exception as e:
66
+ logger.warning(f"Error closing WebSocket connection: {e}")
67
+ finally:
68
+ self._ws_ctx = None