mcp-use 1.3.12__py3-none-any.whl → 1.4.0__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 (108) 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 -219
  6. mcp_use/agents/adapters/__init__.py +17 -0
  7. mcp_use/agents/adapters/anthropic.py +93 -0
  8. mcp_use/agents/adapters/base.py +316 -0
  9. mcp_use/agents/adapters/google.py +103 -0
  10. mcp_use/agents/adapters/langchain_adapter.py +212 -0
  11. mcp_use/agents/adapters/openai.py +111 -0
  12. mcp_use/agents/base.py +1 -1
  13. mcp_use/agents/managers/__init__.py +19 -0
  14. mcp_use/agents/managers/base.py +36 -0
  15. mcp_use/agents/managers/server_manager.py +131 -0
  16. mcp_use/agents/managers/tools/__init__.py +15 -0
  17. mcp_use/agents/managers/tools/base_tool.py +19 -0
  18. mcp_use/agents/managers/tools/connect_server.py +69 -0
  19. mcp_use/agents/managers/tools/disconnect_server.py +43 -0
  20. mcp_use/agents/managers/tools/get_active_server.py +29 -0
  21. mcp_use/agents/managers/tools/list_servers_tool.py +53 -0
  22. mcp_use/agents/managers/tools/search_tools.py +328 -0
  23. mcp_use/agents/mcpagent.py +386 -485
  24. mcp_use/agents/prompts/system_prompt_builder.py +1 -1
  25. mcp_use/agents/remote.py +15 -2
  26. mcp_use/auth/.deprecated +0 -0
  27. mcp_use/auth/__init__.py +19 -4
  28. mcp_use/auth/bearer.py +11 -12
  29. mcp_use/auth/oauth.py +11 -620
  30. mcp_use/auth/oauth_callback.py +16 -207
  31. mcp_use/client/__init__.py +1 -0
  32. mcp_use/client/auth/__init__.py +6 -0
  33. mcp_use/client/auth/bearer.py +23 -0
  34. mcp_use/client/auth/oauth.py +629 -0
  35. mcp_use/client/auth/oauth_callback.py +215 -0
  36. mcp_use/client/client.py +356 -0
  37. mcp_use/client/config.py +106 -0
  38. mcp_use/client/connectors/__init__.py +20 -0
  39. mcp_use/client/connectors/base.py +470 -0
  40. mcp_use/client/connectors/http.py +304 -0
  41. mcp_use/client/connectors/sandbox.py +332 -0
  42. mcp_use/client/connectors/stdio.py +109 -0
  43. mcp_use/client/connectors/utils.py +13 -0
  44. mcp_use/client/connectors/websocket.py +257 -0
  45. mcp_use/client/exceptions.py +31 -0
  46. mcp_use/client/middleware/__init__.py +50 -0
  47. mcp_use/client/middleware/logging.py +31 -0
  48. mcp_use/client/middleware/metrics.py +314 -0
  49. mcp_use/client/middleware/middleware.py +266 -0
  50. mcp_use/client/session.py +162 -0
  51. mcp_use/client/task_managers/__init__.py +20 -0
  52. mcp_use/client/task_managers/base.py +145 -0
  53. mcp_use/client/task_managers/sse.py +84 -0
  54. mcp_use/client/task_managers/stdio.py +69 -0
  55. mcp_use/client/task_managers/streamable_http.py +86 -0
  56. mcp_use/client/task_managers/websocket.py +68 -0
  57. mcp_use/client.py +12 -344
  58. mcp_use/config.py +20 -97
  59. mcp_use/connectors/.deprecated +0 -0
  60. mcp_use/connectors/__init__.py +46 -20
  61. mcp_use/connectors/base.py +12 -455
  62. mcp_use/connectors/http.py +13 -300
  63. mcp_use/connectors/sandbox.py +13 -306
  64. mcp_use/connectors/stdio.py +13 -104
  65. mcp_use/connectors/utils.py +15 -8
  66. mcp_use/connectors/websocket.py +13 -252
  67. mcp_use/exceptions.py +33 -18
  68. mcp_use/logging.py +1 -1
  69. mcp_use/managers/.deprecated +0 -0
  70. mcp_use/managers/__init__.py +56 -17
  71. mcp_use/managers/base.py +13 -31
  72. mcp_use/managers/server_manager.py +13 -119
  73. mcp_use/managers/tools/__init__.py +45 -15
  74. mcp_use/managers/tools/base_tool.py +5 -16
  75. mcp_use/managers/tools/connect_server.py +5 -67
  76. mcp_use/managers/tools/disconnect_server.py +5 -41
  77. mcp_use/managers/tools/get_active_server.py +5 -26
  78. mcp_use/managers/tools/list_servers_tool.py +5 -51
  79. mcp_use/managers/tools/search_tools.py +17 -321
  80. mcp_use/middleware/.deprecated +0 -0
  81. mcp_use/middleware/__init__.py +89 -50
  82. mcp_use/middleware/logging.py +14 -26
  83. mcp_use/middleware/metrics.py +30 -303
  84. mcp_use/middleware/middleware.py +39 -246
  85. mcp_use/session.py +13 -149
  86. mcp_use/task_managers/.deprecated +0 -0
  87. mcp_use/task_managers/__init__.py +48 -20
  88. mcp_use/task_managers/base.py +13 -140
  89. mcp_use/task_managers/sse.py +13 -79
  90. mcp_use/task_managers/stdio.py +13 -64
  91. mcp_use/task_managers/streamable_http.py +15 -81
  92. mcp_use/task_managers/websocket.py +13 -63
  93. mcp_use/telemetry/events.py +58 -0
  94. mcp_use/telemetry/telemetry.py +71 -1
  95. mcp_use/telemetry/utils.py +1 -1
  96. mcp_use/types/.deprecated +0 -0
  97. mcp_use/types/sandbox.py +13 -18
  98. {mcp_use-1.3.12.dist-info → mcp_use-1.4.0.dist-info}/METADATA +68 -43
  99. mcp_use-1.4.0.dist-info/RECORD +111 -0
  100. mcp_use/cli.py +0 -581
  101. mcp_use-1.3.12.dist-info/RECORD +0 -64
  102. mcp_use-1.3.12.dist-info/licenses/LICENSE +0 -21
  103. /mcp_use/{observability → agents/observability}/__init__.py +0 -0
  104. /mcp_use/{observability → agents/observability}/callbacks_manager.py +0 -0
  105. /mcp_use/{observability → agents/observability}/laminar.py +0 -0
  106. /mcp_use/{observability → agents/observability}/langfuse.py +0 -0
  107. {mcp_use-1.3.12.dist-info → mcp_use-1.4.0.dist-info}/WHEEL +0 -0
  108. {mcp_use-1.3.12.dist-info → mcp_use-1.4.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,215 @@
1
+ """OAuth callback server implementation."""
2
+
3
+ import asyncio
4
+ import html
5
+ from dataclasses import dataclass
6
+
7
+ import anyio
8
+ import uvicorn
9
+ from starlette.applications import Starlette
10
+ from starlette.requests import Request
11
+ from starlette.responses import HTMLResponse
12
+ from starlette.routing import Route
13
+
14
+ from mcp_use.logging import logger
15
+
16
+
17
+ @dataclass
18
+ class CallbackResponse:
19
+ """Response data from OAuth callback."""
20
+
21
+ code: str | None = None # Authorization code (success)
22
+ state: str | None = None # CSRF protection token
23
+ error: str | None = None # Errors code (if failed)
24
+ error_description: str | None = None
25
+ error_uri: str | None = None
26
+
27
+
28
+ class OAuthCallbackServer:
29
+ """Local server to handle OAuth callback."""
30
+
31
+ def __init__(self, port: int):
32
+ """Initialize the callback server.
33
+
34
+ Args:
35
+ port: Port to listen on.
36
+ """
37
+ self.port = port
38
+ self.redirect_uri: str | None = None
39
+ # Thread safe way to pass callback data to the main OAuth flow
40
+ self.response_queue: asyncio.Queue[CallbackResponse] = asyncio.Queue(maxsize=1)
41
+ self.server: uvicorn.Server | None = None
42
+ self._shutdown_event = anyio.Event()
43
+
44
+ async def start(self) -> str:
45
+ """Start the callback server and return the redirect URI."""
46
+ app = self._create_app()
47
+
48
+ # Create the server
49
+ config = uvicorn.Config(
50
+ app,
51
+ host="127.0.0.1",
52
+ port=self.port,
53
+ log_level="error", # Suppress uvicorn logs
54
+ )
55
+ self.server = uvicorn.Server(config)
56
+
57
+ # Start server in background
58
+ self._server_task = asyncio.create_task(self.server.serve())
59
+
60
+ # Wait a moment for server to start
61
+ await asyncio.sleep(0.1)
62
+
63
+ self.redirect_uri = f"http://localhost:{self.port}/callback"
64
+ return self.redirect_uri
65
+
66
+ async def wait_for_code(self, timeout: float = 300) -> CallbackResponse:
67
+ """Wait for the OAuth callback with a timeout (default 5 minutes)."""
68
+ try:
69
+ response = await asyncio.wait_for(self.response_queue.get(), timeout=timeout)
70
+ return response
71
+ except TimeoutError:
72
+ raise TimeoutError(f"OAuth callback not received within {timeout} seconds") from None
73
+ finally:
74
+ await self.shutdown()
75
+
76
+ async def shutdown(self):
77
+ """Shutdown the callback server."""
78
+ self._shutdown_event.set()
79
+ if self.server:
80
+ self.server.should_exit = True
81
+ if hasattr(self, "_server_task"):
82
+ try:
83
+ await asyncio.wait_for(self._server_task, timeout=5.0)
84
+ except TimeoutError:
85
+ self._server_task.cancel()
86
+
87
+ def _create_app(self) -> Starlette:
88
+ """Create the Starlette application."""
89
+
90
+ async def callback(request: Request) -> HTMLResponse:
91
+ """Handle the OAuth callback."""
92
+ params = request.query_params
93
+
94
+ # Extract OAuth parameters
95
+ response = CallbackResponse(
96
+ code=params.get("code"),
97
+ state=params.get("state"),
98
+ error=params.get("error"),
99
+ error_description=params.get("error_description"),
100
+ error_uri=params.get("error_uri"),
101
+ )
102
+
103
+ # Log the callback response
104
+ logger.debug(
105
+ f"OAuth callback received: error={response.error}, error_description={response.error_description}"
106
+ )
107
+ if response.code:
108
+ logger.debug("OAuth callback received authorization code")
109
+ else:
110
+ logger.error(f"OAuth callback error: {response.error} - {response.error_description}")
111
+
112
+ # Put response in queue
113
+ try:
114
+ self.response_queue.put_nowait(response)
115
+ except asyncio.QueueFull:
116
+ pass # Ignore if queue is already full
117
+
118
+ # Return success page
119
+ if response.code:
120
+ html = self._success_html()
121
+ else:
122
+ html = self._error_html(response.error, response.error_description)
123
+
124
+ return HTMLResponse(content=html)
125
+
126
+ routes = [Route("/callback", callback)]
127
+ return Starlette(routes=routes)
128
+
129
+ def _success_html(self) -> str:
130
+ """HTML response for successful authorization."""
131
+ return """
132
+ <!DOCTYPE html>
133
+ <html>
134
+ <head>
135
+ <title>Authorization Successful</title>
136
+ <style>
137
+ body {
138
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
139
+ display: flex;
140
+ justify-content: center;
141
+ align-items: center;
142
+ height: 100vh;
143
+ margin: 0;
144
+ background-color: #f5f5f5;
145
+ }
146
+ .container {
147
+ text-align: center;
148
+ padding: 2rem;
149
+ background: white;
150
+ border-radius: 8px;
151
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
152
+ }
153
+ h1 { color: #22c55e; margin-bottom: 0.5rem; }
154
+ p { color: #666; margin-top: 0.5rem; }
155
+ .icon { font-size: 48px; margin-bottom: 1rem; }
156
+ </style>
157
+ </head>
158
+ <body>
159
+ <div class="container">
160
+ <div class="icon">✅</div>
161
+ <h1>Authorization Successful!</h1>
162
+ <p>You can now close this window and return to your application.</p>
163
+ </div>
164
+ <script>
165
+ // Auto-close after 3 seconds
166
+ setTimeout(() => window.close(), 3000);
167
+ </script>
168
+ </body>
169
+ </html>
170
+ """
171
+
172
+ def _error_html(self, error: str | None, description: str | None) -> str:
173
+ """HTML response for authorization error."""
174
+ error_msg = html.escape(error or "Unknown error")
175
+ desc_msg = html.escape(description or "Authorization was not completed successfully.")
176
+
177
+ return f"""
178
+ <!DOCTYPE html>
179
+ <html>
180
+ <head>
181
+ <title>Authorization Error</title>
182
+ <style>
183
+ body {{
184
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
185
+ display: flex;
186
+ justify-content: center;
187
+ align-items: center;
188
+ height: 100vh;
189
+ margin: 0;
190
+ background-color: #f5f5f5;
191
+ }}
192
+ .container {{
193
+ text-align: center;
194
+ padding: 2rem;
195
+ background: white;
196
+ border-radius: 8px;
197
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
198
+ max-width: 500px;
199
+ }}
200
+ h1 {{ color: #ef4444; margin-bottom: 0.5rem; }}
201
+ .error {{ color: #dc2626; font-weight: 600; margin: 1rem 0; }}
202
+ .description {{ color: #666; margin-top: 0.5rem; }}
203
+ .icon {{ font-size: 48px; margin-bottom: 1rem; }}
204
+ </style>
205
+ </head>
206
+ <body>
207
+ <div class="container">
208
+ <div class="icon">❌</div>
209
+ <h1>Authorization Error</h1>
210
+ <p class="error">{error_msg}</p>
211
+ <p class="description">{desc_msg}</p>
212
+ </div>
213
+ </body>
214
+ </html>
215
+ """
@@ -0,0 +1,356 @@
1
+ """
2
+ Client for managing MCP servers and sessions.
3
+
4
+ This module provides a high-level client that manages MCP servers, connectors,
5
+ and sessions from configuration.
6
+ """
7
+
8
+ import json
9
+ import warnings
10
+ from typing import Any
11
+
12
+ from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
13
+
14
+ from mcp_use.client.config import create_connector_from_config, load_config_file
15
+ from mcp_use.client.connectors.sandbox import SandboxOptions
16
+ from mcp_use.client.middleware import Middleware, default_logging_middleware
17
+ from mcp_use.client.session import MCPSession
18
+ from mcp_use.logging import logger
19
+ from mcp_use.telemetry.telemetry import telemetry
20
+
21
+
22
+ class MCPClient:
23
+ """Client for managing MCP servers and sessions.
24
+
25
+ This class provides a unified interface for working with MCP servers,
26
+ handling configuration, connector creation, and session management.
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ config: str | dict[str, Any] | None = None,
32
+ allowed_servers: list[str] | None = None,
33
+ sandbox: bool = False,
34
+ sandbox_options: SandboxOptions | None = None,
35
+ sampling_callback: SamplingFnT | None = None,
36
+ elicitation_callback: ElicitationFnT | None = None,
37
+ message_handler: MessageHandlerFnT | None = None,
38
+ logging_callback: LoggingFnT | None = None,
39
+ middleware: list[Middleware] | None = None,
40
+ ) -> None:
41
+ """Initialize a new MCP client.
42
+
43
+ Args:
44
+ config: Either a dict containing configuration or a path to a JSON config file.
45
+ If None, an empty configuration is used.
46
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
47
+ sandbox_options: Optional sandbox configuration options.
48
+ sampling_callback: Optional sampling callback function.
49
+ """
50
+ self.config: dict[str, Any] = {}
51
+ self.allowed_servers: list[str] = allowed_servers
52
+ self.sandbox = sandbox
53
+ self.sandbox_options = sandbox_options
54
+ self.sessions: dict[str, MCPSession] = {}
55
+ self.active_sessions: list[str] = []
56
+ self.sampling_callback = sampling_callback
57
+ self.elicitation_callback = elicitation_callback
58
+ self.message_handler = message_handler
59
+ self.logging_callback = logging_callback
60
+ # Add default logging middleware if no middleware provided, or prepend it to existing middleware
61
+ default_middleware = [default_logging_middleware]
62
+ if middleware:
63
+ self.middleware = default_middleware + middleware
64
+ else:
65
+ self.middleware = default_middleware
66
+ # Load configuration if provided
67
+ if config is not None:
68
+ if isinstance(config, str):
69
+ self.config = load_config_file(config)
70
+ else:
71
+ self.config = config
72
+
73
+ @classmethod
74
+ def from_dict(
75
+ cls,
76
+ config: dict[str, Any],
77
+ sandbox: bool = False,
78
+ sandbox_options: SandboxOptions | None = None,
79
+ sampling_callback: SamplingFnT | None = None,
80
+ elicitation_callback: ElicitationFnT | None = None,
81
+ message_handler: MessageHandlerFnT | None = None,
82
+ logging_callback: LoggingFnT | None = None,
83
+ ) -> "MCPClient":
84
+ """Create a MCPClient from a dictionary.
85
+
86
+ Args:
87
+ config: The configuration dictionary.
88
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
89
+ sandbox_options: Optional sandbox configuration options.
90
+ sampling_callback: Optional sampling callback function.
91
+ elicitation_callback: Optional elicitation callback function.
92
+ """
93
+ return cls(
94
+ config=config,
95
+ sandbox=sandbox,
96
+ sandbox_options=sandbox_options,
97
+ sampling_callback=sampling_callback,
98
+ elicitation_callback=elicitation_callback,
99
+ message_handler=message_handler,
100
+ logging_callback=logging_callback,
101
+ )
102
+
103
+ @classmethod
104
+ def from_config_file(
105
+ cls,
106
+ filepath: str,
107
+ sandbox: bool = False,
108
+ sandbox_options: SandboxOptions | None = None,
109
+ sampling_callback: SamplingFnT | None = None,
110
+ elicitation_callback: ElicitationFnT | None = None,
111
+ message_handler: MessageHandlerFnT | None = None,
112
+ logging_callback: LoggingFnT | None = None,
113
+ ) -> "MCPClient":
114
+ """Create a MCPClient from a configuration file.
115
+
116
+ Args:
117
+ filepath: The path to the configuration file.
118
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
119
+ sandbox_options: Optional sandbox configuration options.
120
+ sampling_callback: Optional sampling callback function.
121
+ elicitation_callback: Optional elicitation callback function.
122
+ """
123
+ return cls(
124
+ config=load_config_file(filepath),
125
+ sandbox=sandbox,
126
+ sandbox_options=sandbox_options,
127
+ sampling_callback=sampling_callback,
128
+ elicitation_callback=elicitation_callback,
129
+ message_handler=message_handler,
130
+ logging_callback=logging_callback,
131
+ )
132
+
133
+ @telemetry("client_add_server")
134
+ def add_server(
135
+ self,
136
+ name: str,
137
+ server_config: dict[str, Any],
138
+ ) -> None:
139
+ """Add a server configuration.
140
+
141
+ Args:
142
+ name: The name to identify this server.
143
+ server_config: The server configuration.
144
+ """
145
+ if "mcpServers" not in self.config:
146
+ self.config["mcpServers"] = {}
147
+
148
+ self.config["mcpServers"][name] = server_config
149
+
150
+ @telemetry("client_remove_server")
151
+ def remove_server(self, name: str) -> None:
152
+ """Remove a server configuration.
153
+
154
+ Args:
155
+ name: The name of the server to remove.
156
+ """
157
+ if "mcpServers" in self.config and name in self.config["mcpServers"]:
158
+ del self.config["mcpServers"][name]
159
+
160
+ # If we removed an active session, remove it from active_sessions
161
+ if name in self.active_sessions:
162
+ self.active_sessions.remove(name)
163
+
164
+ def add_middleware(self, middleware: Middleware) -> None:
165
+ """Add a middleware.
166
+
167
+ Args:
168
+ middleware: The middleware to add
169
+ """
170
+ if len(self.sessions) == 0 and middleware not in self.middleware:
171
+ self.middleware.append(middleware)
172
+ return
173
+
174
+ if middleware not in self.middleware:
175
+ self.middleware.append(middleware)
176
+ for session in self.sessions.values():
177
+ session.connector.middleware_manager.add_middleware(middleware)
178
+
179
+ def get_server_names(self) -> list[str]:
180
+ """Get the list of configured server names.
181
+
182
+ Returns:
183
+ List of server names.
184
+ """
185
+ return list(self.config.get("mcpServers", {}).keys())
186
+
187
+ def save_config(self, filepath: str) -> None:
188
+ """Save the current configuration to a file.
189
+
190
+ Args:
191
+ filepath: The path to save the configuration to.
192
+ """
193
+ with open(filepath, "w") as f:
194
+ json.dump(self.config, f, indent=2)
195
+
196
+ @telemetry("client_create_session")
197
+ async def create_session(self, server_name: str, auto_initialize: bool = True) -> MCPSession:
198
+ """Create a session for the specified server.
199
+
200
+ Args:
201
+ server_name: The name of the server to create a session for.
202
+ auto_initialize: Whether to automatically initialize the session.
203
+
204
+ Returns:
205
+ The created MCPSession.
206
+
207
+ Raises:
208
+ ValueError: If the specified server doesn't exist.
209
+ """
210
+ # Get server config
211
+ servers = self.config.get("mcpServers", {})
212
+ if not servers:
213
+ warnings.warn("No MCP servers defined in config", UserWarning, stacklevel=2)
214
+ return None
215
+
216
+ if server_name not in servers:
217
+ raise ValueError(f"Server '{server_name}' not found in config")
218
+
219
+ server_config = servers[server_name]
220
+
221
+ # Create connector with options and client-level auth
222
+ connector = create_connector_from_config(
223
+ server_config,
224
+ sandbox=self.sandbox,
225
+ sandbox_options=self.sandbox_options,
226
+ sampling_callback=self.sampling_callback,
227
+ elicitation_callback=self.elicitation_callback,
228
+ message_handler=self.message_handler,
229
+ logging_callback=self.logging_callback,
230
+ middleware=self.middleware,
231
+ )
232
+
233
+ # Create the session
234
+ session = MCPSession(connector)
235
+ if auto_initialize:
236
+ await session.initialize()
237
+ self.sessions[server_name] = session
238
+
239
+ # Add to active sessions
240
+ if server_name not in self.active_sessions:
241
+ self.active_sessions.append(server_name)
242
+
243
+ return session
244
+
245
+ @telemetry("client_create_all_sessions")
246
+ async def create_all_sessions(
247
+ self,
248
+ auto_initialize: bool = True,
249
+ ) -> dict[str, MCPSession]:
250
+ """Create sessions for all configured servers.
251
+
252
+ Args:
253
+ auto_initialize: Whether to automatically initialize the sessions.
254
+
255
+ Returns:
256
+ Dictionary mapping server names to their MCPSession instances.
257
+
258
+ Warns:
259
+ UserWarning: If no servers are configured.
260
+ """
261
+ # Get server config
262
+ servers = self.config.get("mcpServers", {})
263
+ if not servers:
264
+ warnings.warn("No MCP servers defined in config", UserWarning, stacklevel=2)
265
+ return {}
266
+
267
+ # Create sessions only for allowed servers if applicable else create for all servers
268
+ for name in servers:
269
+ if self.allowed_servers is None or name in self.allowed_servers:
270
+ await self.create_session(name, auto_initialize)
271
+
272
+ return self.sessions
273
+
274
+ def get_session(self, server_name: str) -> MCPSession:
275
+ """Get an existing session.
276
+
277
+ Args:
278
+ server_name: The name of the server to get the session for.
279
+ If None, uses the first active session.
280
+
281
+ Returns:
282
+ The MCPSession for the specified server.
283
+
284
+ Raises:
285
+ ValueError: If no active sessions exist or the specified session doesn't exist.
286
+ """
287
+ if server_name not in self.sessions:
288
+ raise ValueError(f"No session exists for server '{server_name}'")
289
+
290
+ return self.sessions[server_name]
291
+
292
+ def get_all_active_sessions(self) -> dict[str, MCPSession]:
293
+ """Get all active sessions.
294
+
295
+ Returns:
296
+ Dictionary mapping server names to their MCPSession instances.
297
+ """
298
+ return {name: self.sessions[name] for name in self.active_sessions if name in self.sessions}
299
+
300
+ @telemetry("client_close_session")
301
+ async def close_session(self, server_name: str) -> None:
302
+ """Close a session.
303
+
304
+ Args:
305
+ server_name: The name of the server to close the session for.
306
+ If None, uses the first active session.
307
+
308
+ Raises:
309
+ ValueError: If no active sessions exist or the specified session doesn't exist.
310
+ """
311
+ # Check if the session exists
312
+ if server_name not in self.sessions:
313
+ logger.warning(f"No session exists for server '{server_name}', nothing to close")
314
+ return
315
+
316
+ # Get the session
317
+ session = self.sessions[server_name]
318
+
319
+ try:
320
+ # Disconnect from the session
321
+ logger.debug(f"Closing session for server '{server_name}'")
322
+ await session.disconnect()
323
+ except Exception as e:
324
+ logger.error(f"Error closing session for server '{server_name}': {e}")
325
+ finally:
326
+ # Remove the session regardless of whether disconnect succeeded
327
+ del self.sessions[server_name]
328
+
329
+ # Remove from active_sessions
330
+ if server_name in self.active_sessions:
331
+ self.active_sessions.remove(server_name)
332
+
333
+ @telemetry("client_close_all_sessions")
334
+ async def close_all_sessions(self) -> None:
335
+ """Close all active sessions.
336
+
337
+ This method ensures all sessions are closed even if some fail.
338
+ """
339
+ # Get a list of all session names first to avoid modification during iteration
340
+ server_names = list(self.sessions.keys())
341
+ errors = []
342
+
343
+ for server_name in server_names:
344
+ try:
345
+ logger.debug(f"Closing session for server '{server_name}'")
346
+ await self.close_session(server_name)
347
+ except Exception as e:
348
+ error_msg = f"Failed to close session for server '{server_name}': {e}"
349
+ logger.error(error_msg)
350
+ errors.append(error_msg)
351
+
352
+ # Log summary if there were errors
353
+ if errors:
354
+ logger.error(f"Encountered {len(errors)} errors while closing sessions")
355
+ else:
356
+ logger.debug("All sessions closed successfully")
@@ -0,0 +1,106 @@
1
+ """
2
+ Configuration loader for MCP session.
3
+
4
+ This module provides functionality to load MCP configuration from JSON files.
5
+ """
6
+
7
+ import json
8
+ from typing import Any
9
+
10
+ from mcp.client.session import ElicitationFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
11
+
12
+ from mcp_use.client.connectors.base import BaseConnector
13
+ from mcp_use.client.connectors.http import HttpConnector
14
+ from mcp_use.client.connectors.sandbox import SandboxConnector, SandboxOptions
15
+ from mcp_use.client.connectors.stdio import StdioConnector
16
+ from mcp_use.client.connectors.utils import is_stdio_server
17
+ from mcp_use.client.connectors.websocket import WebSocketConnector
18
+ from mcp_use.client.middleware import Middleware
19
+
20
+
21
+ def load_config_file(filepath: str) -> dict[str, Any]:
22
+ """Load a configuration file.
23
+
24
+ Args:
25
+ filepath: Path to the configuration file
26
+
27
+ Returns:
28
+ The parsed configuration
29
+ """
30
+ with open(filepath) as f:
31
+ return json.load(f)
32
+
33
+
34
+ def create_connector_from_config(
35
+ server_config: dict[str, Any],
36
+ sandbox: bool = False,
37
+ sandbox_options: SandboxOptions | None = None,
38
+ sampling_callback: SamplingFnT | None = None,
39
+ elicitation_callback: ElicitationFnT | None = None,
40
+ message_handler: MessageHandlerFnT | None = None,
41
+ logging_callback: LoggingFnT | None = None,
42
+ middleware: list[Middleware] | None = None,
43
+ ) -> BaseConnector:
44
+ """Create a connector based on server configuration.
45
+ This function can be called with just the server_config parameter:
46
+ create_connector_from_config(server_config)
47
+ Args:
48
+ server_config: The server configuration section
49
+ sandbox: Whether to use sandboxed execution mode for running MCP servers.
50
+ sandbox_options: Optional sandbox configuration options.
51
+ sampling_callback: Optional sampling callback function.
52
+ Returns:
53
+ A configured connector instance
54
+ """
55
+
56
+ # Stdio connector (command-based)
57
+ if is_stdio_server(server_config) and not sandbox:
58
+ return StdioConnector(
59
+ command=server_config["command"],
60
+ args=server_config["args"],
61
+ env=server_config.get("env", None),
62
+ sampling_callback=sampling_callback,
63
+ elicitation_callback=elicitation_callback,
64
+ message_handler=message_handler,
65
+ logging_callback=logging_callback,
66
+ middleware=middleware,
67
+ )
68
+
69
+ # Sandboxed connector
70
+ elif is_stdio_server(server_config) and sandbox:
71
+ return SandboxConnector(
72
+ command=server_config["command"],
73
+ args=server_config["args"],
74
+ env=server_config.get("env", None),
75
+ e2b_options=sandbox_options,
76
+ sampling_callback=sampling_callback,
77
+ elicitation_callback=elicitation_callback,
78
+ message_handler=message_handler,
79
+ logging_callback=logging_callback,
80
+ middleware=middleware,
81
+ )
82
+
83
+ # HTTP connector
84
+ elif "url" in server_config:
85
+ return HttpConnector(
86
+ base_url=server_config["url"],
87
+ headers=server_config.get("headers", None),
88
+ auth=server_config.get("auth", {}),
89
+ timeout=server_config.get("timeout", 5),
90
+ sse_read_timeout=server_config.get("sse_read_timeout", 60 * 5),
91
+ sampling_callback=sampling_callback,
92
+ elicitation_callback=elicitation_callback,
93
+ message_handler=message_handler,
94
+ logging_callback=logging_callback,
95
+ middleware=middleware,
96
+ )
97
+
98
+ # WebSocket connector
99
+ elif "ws_url" in server_config:
100
+ return WebSocketConnector(
101
+ url=server_config["ws_url"],
102
+ headers=server_config.get("headers", None),
103
+ auth=server_config.get("auth", {}),
104
+ )
105
+
106
+ raise ValueError("Cannot determine connector type from config")
@@ -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 BaseConnector # noqa: F401
9
+ from .http import HttpConnector # noqa: F401
10
+ from .sandbox import SandboxConnector # noqa: F401
11
+ from .stdio import StdioConnector # noqa: F401
12
+ from .websocket import WebSocketConnector # noqa: F401
13
+
14
+ __all__ = [
15
+ "BaseConnector",
16
+ "StdioConnector",
17
+ "HttpConnector",
18
+ "WebSocketConnector",
19
+ "SandboxConnector",
20
+ ]