mcp-use 1.3.9__py3-none-any.whl → 1.3.10__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/cli.py ADDED
@@ -0,0 +1,581 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ MCP-Use CLI Tool - All-in-one CLI for creating and deploying MCP projects.
4
+ """
5
+
6
+ import argparse
7
+ import sys
8
+ import threading
9
+ import time
10
+ from pathlib import Path
11
+
12
+ from mcp_use import __version__
13
+
14
+ # ============= SPINNER CLASS =============
15
+
16
+
17
+ class Spinner:
18
+ """Simple loading spinner similar to UV's style."""
19
+
20
+ def __init__(self, message: str = "Loading"):
21
+ self.message = message
22
+ self.running = False
23
+ self.thread = None
24
+ self.frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
25
+ self.current = 0
26
+
27
+ def start(self):
28
+ """Start the spinner."""
29
+ self.running = True
30
+ self.thread = threading.Thread(target=self._spin)
31
+ self.thread.daemon = True
32
+ self.thread.start()
33
+
34
+ def _spin(self):
35
+ """Spin animation loop."""
36
+ while self.running:
37
+ frame = self.frames[self.current % len(self.frames)]
38
+ print(f"\r{frame} {self.message}...", end="", flush=True)
39
+ self.current += 1
40
+ time.sleep(0.1)
41
+
42
+ def stop(self, success_message=None):
43
+ """Stop the spinner and optionally print success message."""
44
+ self.running = False
45
+ if self.thread:
46
+ self.thread.join()
47
+ if success_message:
48
+ print(f"\r✓ {success_message} ")
49
+ else:
50
+ print("\r" + " " * (len(self.message) + 10), end="\r")
51
+
52
+
53
+ # ============= PROJECT CREATION FUNCTIONS =============
54
+
55
+
56
+ def print_header():
57
+ """Print the CLI header."""
58
+ print("\n mcp-use create")
59
+ print("━" * 50)
60
+ print()
61
+
62
+
63
+ def get_project_name() -> str:
64
+ """Get project name from user."""
65
+ while True:
66
+ name = input("📝 Project name: ").strip().replace("-", "_")
67
+ if not name:
68
+ print(" ⚠️ Project name cannot be empty")
69
+ continue
70
+ if " " in name:
71
+ print(" ⚠️ Project name cannot contain spaces")
72
+ continue
73
+ if Path(name).exists():
74
+ print(f" ⚠️ Directory '{name}' already exists")
75
+ continue
76
+ return name
77
+
78
+
79
+ def get_project_type() -> str:
80
+ """Get project type from user."""
81
+ print("\n📦 What would you like to create?")
82
+ print(" 1) Server + Agent")
83
+ print(" 2) Server only")
84
+ print(" 3) Agent only")
85
+
86
+ while True:
87
+ choice = input("\n Choice (1-3): ").strip()
88
+ if choice == "1":
89
+ return "server_agent"
90
+ elif choice == "2":
91
+ return "server"
92
+ elif choice == "3":
93
+ return "agent"
94
+ else:
95
+ print(" ⚠️ Please enter 1, 2, or 3")
96
+
97
+
98
+ def create_server_structure(project_dir: Path, project_name: str):
99
+ """Create server file in nested project folder."""
100
+ # Create nested project folder
101
+ nested_dir = project_dir / project_name
102
+ nested_dir.mkdir(parents=True)
103
+
104
+ # Create server.py
105
+ server_content = f'''"""
106
+ MCP Server for {project_name}
107
+ """
108
+
109
+ from mcp.server import FastMCP
110
+
111
+ # Create server instance
112
+ server = FastMCP("{project_name}-server")
113
+
114
+
115
+ # ============= TOOLS =============
116
+
117
+
118
+ @server.tool()
119
+ async def add_numbers(a: float, b: float) -> str:
120
+ """Add two numbers together."""
121
+ result = a + b
122
+ return f"{{result}}"
123
+
124
+
125
+ # ============= RESOURCES =============
126
+
127
+
128
+ @server.resource("config://app")
129
+ async def get_app_config() -> str:
130
+ """Get the application configuration."""
131
+ return "App: {project_name}, Version: 0.1.0, Status: Active"
132
+
133
+
134
+ # ============= PROMPTS =============
135
+
136
+
137
+ @server.prompt()
138
+ async def assistant_prompt() -> str:
139
+ """Generate a helpful assistant prompt."""
140
+ return "You are a helpful assistant for {project_name}. Be concise and friendly."
141
+
142
+
143
+ # ============= MAIN =============
144
+
145
+ if __name__ == "__main__":
146
+ server.run("stdio")
147
+ '''
148
+ (nested_dir / "server.py").write_text(server_content)
149
+
150
+
151
+ def create_agent_structure(project_dir: Path, project_name: str, project_type: str):
152
+ """Create agent file in nested project folder."""
153
+ # Create nested project folder if it doesn't exist
154
+ nested_dir = project_dir / project_name
155
+ if not nested_dir.exists():
156
+ nested_dir.mkdir(parents=True)
157
+
158
+ if project_type == "server_agent":
159
+ # For server_agent mode, embed config directly in agent.py
160
+ agent_content = f'''"""
161
+ MCP Agent implementation for {project_name}
162
+ """
163
+
164
+ from langchain_openai import ChatOpenAI
165
+
166
+ from mcp_use import MCPAgent, MCPClient
167
+
168
+ config = {{
169
+ "mcpServers": {{
170
+ "{project_name}": {{
171
+ "command": "python",
172
+ "args": ["{project_name}/server.py"],
173
+ }}
174
+ }}
175
+ }}
176
+
177
+ client = MCPClient(config=config)
178
+ agent = MCPAgent(llm=ChatOpenAI(model="gpt-4o"), client=client, max_steps=10, memory_enabled=True)
179
+ '''
180
+ else:
181
+ # For agent-only mode, use external JSON config file
182
+ agent_content = f'''"""
183
+ MCP Agent implementation for {project_name}
184
+ """
185
+
186
+ from langchain_openai import ChatOpenAI
187
+
188
+ from mcp_use import MCPAgent, MCPClient
189
+
190
+ client = MCPClient.from_config_file("{project_name}/mcp_servers.json")
191
+ agent = MCPAgent(llm=ChatOpenAI(model="gpt-4o"), client=client, max_steps=10, memory_enabled=True)
192
+ '''
193
+ # Create mcp_servers.json for agent-only mode
194
+ mcp_servers_json = (
195
+ '''{
196
+ "mcpServers": {
197
+ "'''
198
+ + project_name
199
+ + """": {
200
+ "command": "npx",
201
+ "args": ["-y", "@modelcontextprotocol/server-everything"]
202
+ }
203
+ }
204
+ }"""
205
+ )
206
+ (nested_dir / "mcp_servers.json").write_text(mcp_servers_json)
207
+
208
+ (nested_dir / "agent.py").write_text(agent_content)
209
+
210
+
211
+ def create_common_files(project_dir: Path, project_name: str, project_type: str):
212
+ """Create common project files."""
213
+
214
+ # pyproject.toml
215
+ pyproject = f"""[project]
216
+ name = "{project_name}"
217
+ version = "0.1.0"
218
+ description = "An MCP project"
219
+ requires-python = ">=3.10"
220
+ dependencies = [
221
+ "mcp>=1.0.0",
222
+ "mcp-use>=0.1.0",
223
+ "langchain-openai>=0.1.0",
224
+ "python-dotenv>=1.0.0",
225
+ ]
226
+ """
227
+ (project_dir / "pyproject.toml").write_text(pyproject)
228
+
229
+ # requirements.txt
230
+ requirements = """mcp>=1.0.0
231
+ mcp-use>=0.1.0
232
+ langchain-openai>=0.1.0
233
+ python-dotenv>=1.0.0
234
+ """
235
+ (project_dir / "requirements.txt").write_text(requirements)
236
+
237
+ # .gitignore
238
+ gitignore = """__pycache__/
239
+ *.py[cod]
240
+ .env
241
+ venv/
242
+ .venv/
243
+ *.egg-info/
244
+ dist/
245
+ build/
246
+ .pytest_cache/
247
+ .coverage
248
+ """
249
+ (project_dir / ".gitignore").write_text(gitignore)
250
+
251
+ # README.md
252
+ readme = f"""# {project_name}
253
+
254
+ An MCP project created with mcp-use.
255
+
256
+ ## Project Structure
257
+
258
+ ```
259
+ {project_name}/
260
+ """
261
+
262
+ if project_type in ["server_agent", "server"]:
263
+ readme += f"""├── {project_name}/
264
+ │ ├── server.py # MCP server with all components
265
+ """
266
+
267
+ if project_type in ["server_agent", "agent"]:
268
+ if project_type == "agent":
269
+ readme += f"""├── {project_name}/
270
+ │ ├── agent.py # MCP agent implementation
271
+ │ └── mcp_servers.json # Server configuration
272
+ """
273
+ else:
274
+ readme += """│ └── agent.py # MCP agent implementation
275
+ """
276
+
277
+ if project_type in ["server_agent", "agent"]:
278
+ readme += """├── run.py # Simple example
279
+ ├── chat.py # Interactive chat
280
+ """
281
+
282
+ readme += """├── pyproject.toml
283
+ ├── requirements.txt
284
+ ├── .gitignore
285
+ └── README.md
286
+ ```
287
+
288
+ ## Setup
289
+
290
+ 1. Install dependencies:
291
+ ```bash
292
+ pip install -r requirements.txt
293
+ ```
294
+
295
+ 2. Configure environment:
296
+ ```bash
297
+ export OPENAI_API_KEY=your-api-key-here
298
+ ```
299
+ """
300
+
301
+ if project_type in ["server_agent", "server"]:
302
+ readme += f"""
303
+ ## Running the Server
304
+
305
+ ```bash
306
+ python {project_name}/server.py
307
+ ```
308
+
309
+ The server uses FastMCP and includes:
310
+ - **Tools**: Simple tool functions (e.g., add_numbers)
311
+ - **Resources**: Data resources (e.g., config)
312
+ - **Prompts**: Prompt templates for the LLM
313
+ """
314
+
315
+ if project_type in ["server_agent", "agent"]:
316
+ readme += f"""
317
+ ## Using the Agent
318
+
319
+ ```python
320
+ from {project_name}.agent import agent
321
+
322
+ result = await agent.run("Your prompt")
323
+ ```
324
+ """
325
+
326
+ (project_dir / "README.md").write_text(readme)
327
+
328
+
329
+ def create_example_files(project_dir: Path, project_name: str):
330
+ """Create example files."""
331
+
332
+ # run.py
333
+ run_content = f'''"""
334
+ Example usage of {project_name}.
335
+ """
336
+
337
+ import asyncio
338
+ import os
339
+
340
+ from dotenv import load_dotenv
341
+
342
+ from {project_name}.agent import agent
343
+
344
+
345
+ async def main():
346
+ load_dotenv()
347
+
348
+ if not os.getenv("OPENAI_API_KEY"):
349
+ print("Error: OPENAI_API_KEY not found")
350
+ return
351
+
352
+ result = await agent.run("What tools are available?")
353
+ print(f"Result: {{result}}")
354
+
355
+
356
+ if __name__ == "__main__":
357
+ asyncio.run(main())
358
+ '''
359
+ (project_dir / "run.py").write_text(run_content)
360
+
361
+ # chat.py
362
+ chat_content = f'''"""
363
+ Interactive chat for {project_name}.
364
+ """
365
+
366
+ import asyncio
367
+ import os
368
+
369
+ from dotenv import load_dotenv
370
+
371
+ from {project_name}.agent import agent
372
+
373
+
374
+ async def chat():
375
+ load_dotenv()
376
+
377
+ if not os.getenv("OPENAI_API_KEY"):
378
+ print("Error: OPENAI_API_KEY not found")
379
+ return
380
+
381
+ print("Chat started (type 'exit' to quit)")
382
+
383
+ while True:
384
+ user_input = input("\\nYou: ")
385
+ if user_input.lower() == "exit":
386
+ break
387
+
388
+ print("Assistant: ", end="", flush=True)
389
+ response = await agent.run(user_input)
390
+ print(response)
391
+
392
+
393
+ if __name__ == "__main__":
394
+ asyncio.run(chat())
395
+ '''
396
+ (project_dir / "chat.py").write_text(chat_content)
397
+
398
+
399
+ def create_project(project_name: str, project_type: str) -> bool:
400
+ """Create the project structure."""
401
+ project_dir = Path.cwd() / project_name
402
+
403
+ try:
404
+ # Create main directory
405
+ spinner = Spinner("Creating project directory")
406
+ spinner.start()
407
+ time.sleep(0.5) # Simulate work
408
+ project_dir.mkdir(parents=True)
409
+ spinner.stop("Created project directory")
410
+
411
+ # Create server if needed
412
+ if project_type in ["server_agent", "server"]:
413
+ spinner = Spinner("Creating server")
414
+ spinner.start()
415
+ time.sleep(0.3)
416
+ create_server_structure(project_dir, project_name)
417
+ spinner.stop("Created server structure")
418
+
419
+ # Create agent if needed
420
+ if project_type in ["server_agent", "agent"]:
421
+ spinner = Spinner("Creating agent")
422
+ spinner.start()
423
+ time.sleep(0.3)
424
+ create_agent_structure(project_dir, project_name, project_type)
425
+ spinner.stop("Created agent structure")
426
+
427
+ # Create common files
428
+ spinner = Spinner("Creating configuration files")
429
+ spinner.start()
430
+ time.sleep(0.3)
431
+ create_common_files(project_dir, project_name, project_type)
432
+ spinner.stop("Created configuration files")
433
+
434
+ # Create examples for server_agent and agent
435
+ if project_type in ["server_agent", "agent"]:
436
+ spinner = Spinner("Creating example files")
437
+ spinner.start()
438
+ time.sleep(0.3)
439
+ create_example_files(project_dir, project_name)
440
+ spinner.stop("Created example files")
441
+
442
+ return True
443
+
444
+ except Exception as e:
445
+ print(f"\n❌ Error: {str(e)}")
446
+ return False
447
+
448
+
449
+ # ============= MAIN CLI FUNCTIONS =============
450
+
451
+
452
+ def show_help():
453
+ """Show the main help message."""
454
+ help_text = """
455
+ ╔══════════════════════════════════════════════════════════════════╗
456
+ ║ MCP-Use CLI Tool ║
457
+ ║ ║
458
+ ║ Create and deploy MCP servers and agents with ease ║
459
+ ╚══════════════════════════════════════════════════════════════════╝
460
+
461
+ Usage: uvx mcp-use <command> [options]
462
+
463
+ Available Commands:
464
+ create 🚀 Create a new MCP project (server, agent, or both)
465
+ Interactive wizard to scaffold your MCP project
466
+
467
+ deploy ☁️ Deploy your MCP project to the cloud
468
+ (Coming soon - Cloud deployment from CLI)
469
+
470
+ Examples:
471
+ uvx mcp-use create # Start interactive project creation
472
+ uvx mcp-use deploy # Deploy to cloud (coming soon)
473
+ uvx mcp-use --help # Show this help message
474
+ uvx mcp-use --version # Show version information
475
+
476
+ For more information, visit: https://mcp-use.com
477
+ """
478
+ print(help_text)
479
+
480
+
481
+ def handle_create():
482
+ """Handle the create command."""
483
+ print_header()
484
+
485
+ # Get project configuration
486
+ project_name = get_project_name()
487
+ project_type = get_project_type()
488
+
489
+ print(f"\n⚙️ Creating {project_type.replace('_', ' + ')} project: {project_name}")
490
+ print()
491
+
492
+ # Create the project
493
+ if create_project(project_name, project_type):
494
+ print(f"\n✨ Successfully created '{project_name}'!")
495
+ print("\n📋 Next steps:")
496
+ print(f" cd {project_name}")
497
+ print(" pip install -r requirements.txt")
498
+ print(" export OPENAI_API_KEY=your-api-key-here")
499
+
500
+ if project_type in ["server_agent", "server"]:
501
+ print("\n # Test the server:")
502
+ print(f" python {project_name}/server.py")
503
+
504
+ if project_type in ["server_agent", "agent"]:
505
+ print("\n # Run examples:")
506
+ print(" python run.py # Simple example")
507
+ print(" python chat.py # Interactive chat")
508
+
509
+ print()
510
+ else:
511
+ sys.exit(1)
512
+
513
+
514
+ def handle_deploy():
515
+ """Handle the deploy command (placeholder for future implementation)."""
516
+ print("\n" + "=" * 60)
517
+ print("🚀 MCP Cloud Deployment")
518
+ print("=" * 60)
519
+
520
+ print("\n📝 Please login to MCP Cloud to continue...")
521
+ print(" Visit: https://cloud.mcp-use.com/login")
522
+ print()
523
+
524
+ # Simulate login prompt
525
+ print("Enter your MCP Cloud credentials:")
526
+ email = input("Email: ")
527
+
528
+ if email:
529
+ print(f"\n✨ Welcome {email}!")
530
+ print()
531
+ print("ℹ️ Deployment from CLI is coming soon!")
532
+ print(" For now, please use the web interface at:")
533
+ print(" https://cloud.mcp-use.com/deploy")
534
+ print()
535
+ print("Stay tuned for updates! 🎉")
536
+ else:
537
+ print("\n❌ Login cancelled")
538
+
539
+ print("=" * 60)
540
+
541
+
542
+ def main(args=None):
543
+ """Main entry point for the CLI."""
544
+ parser = argparse.ArgumentParser(
545
+ prog="mcp-use",
546
+ description="MCP-Use CLI Tool - Create and deploy MCP projects",
547
+ add_help=False, # We'll handle help ourselves
548
+ )
549
+
550
+ # Add version argument
551
+ parser.add_argument(
552
+ "--version", "-v", action="version", version=f"mcp-use {__version__}", help="Show version information"
553
+ )
554
+
555
+ # Add help argument
556
+ parser.add_argument("--help", "-h", action="store_true", help="Show help message")
557
+
558
+ # Add subcommand as positional argument
559
+ parser.add_argument("command", nargs="?", choices=["create", "deploy"], help="Command to execute")
560
+
561
+ # Parse arguments
562
+ parsed_args = parser.parse_args(args)
563
+
564
+ # Handle help flag or no command
565
+ if parsed_args.help or not parsed_args.command:
566
+ show_help()
567
+ sys.exit(0)
568
+
569
+ # Handle commands
570
+ if parsed_args.command == "create":
571
+ handle_create()
572
+ elif parsed_args.command == "deploy":
573
+ handle_deploy()
574
+ else:
575
+ print(f"Unknown command: {parsed_args.command}")
576
+ show_help()
577
+ sys.exit(1)
578
+
579
+
580
+ if __name__ == "__main__":
581
+ main()
mcp_use/logging.py CHANGED
@@ -80,6 +80,9 @@ class Logger:
80
80
 
81
81
  root_logger.setLevel(level)
82
82
 
83
+ # Set propagate to True to ensure child loggers inherit settings
84
+ root_logger.propagate = True
85
+
83
86
  # Clear existing handlers
84
87
  for handler in root_logger.handlers[:]:
85
88
  root_logger.removeHandler(handler)
@@ -91,6 +94,7 @@ class Logger:
91
94
  if log_to_console:
92
95
  console_handler = logging.StreamHandler(sys.stdout)
93
96
  console_handler.setFormatter(formatter)
97
+ console_handler.setLevel(level) # Ensure handler respects the level
94
98
  root_logger.addHandler(console_handler)
95
99
 
96
100
  # Add file handler if requested
@@ -102,6 +106,7 @@ class Logger:
102
106
 
103
107
  file_handler = logging.FileHandler(log_to_file)
104
108
  file_handler.setFormatter(formatter)
109
+ file_handler.setLevel(level) # Ensure handler respects the level
105
110
  root_logger.addHandler(file_handler)
106
111
 
107
112
  @classmethod
@@ -114,24 +119,34 @@ class Logger:
114
119
  global MCP_USE_DEBUG
115
120
  MCP_USE_DEBUG = debug_level
116
121
 
117
- # Update log level for existing loggers
122
+ # Determine the target level
118
123
  if debug_level == 2:
119
- for logger in cls._loggers.values():
120
- logger.setLevel(logging.DEBUG)
121
- langchain_set_debug(True)
124
+ target_level = logging.DEBUG
125
+ langchain_set_debug(True)
122
126
  elif debug_level == 1:
123
- for logger in cls._loggers.values():
124
- logger.setLevel(logging.INFO)
125
- langchain_set_debug(False)
127
+ target_level = logging.INFO
128
+ langchain_set_debug(False)
126
129
  else:
127
- # Reset to default (WARNING)
128
- for logger in cls._loggers.values():
129
- logger.setLevel(logging.WARNING)
130
- langchain_set_debug(False)
130
+ target_level = logging.WARNING
131
+ langchain_set_debug(False)
132
+
133
+ # Update log level for existing loggers in our registry
134
+ for logger in cls._loggers.values():
135
+ logger.setLevel(target_level)
136
+ # Also update handler levels
137
+ for handler in logger.handlers:
138
+ handler.setLevel(target_level)
139
+
140
+ # Also update all mcp_use child loggers that might exist
141
+ # This ensures loggers created with logging.getLogger() are also updated
142
+ base_logger = logging.getLogger("mcp_use")
143
+ base_logger.setLevel(target_level)
144
+ for handler in base_logger.handlers:
145
+ handler.setLevel(target_level)
131
146
 
132
147
 
133
148
  # Check environment variable for debug flag
134
- debug_env = os.environ.get("DEBUG", "").lower()
149
+ debug_env = os.environ.get("MCP_USE_DEBUG", "").lower() or os.environ.get("DEBUG", "").lower()
135
150
  if debug_env == "2":
136
151
  MCP_USE_DEBUG = 2
137
152
  elif debug_env == "1":
@@ -0,0 +1,36 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from langchain_core.tools import BaseTool
4
+
5
+
6
+ class BaseServerManager(ABC):
7
+ """Abstract base class for server managers.
8
+
9
+ This class defines the interface for server managers that can be used with MCPAgent.
10
+ Custom server managers should inherit from this class and implement the required methods.
11
+ """
12
+
13
+ @abstractmethod
14
+ async def initialize(self) -> None:
15
+ """Initialize the server manager."""
16
+ raise NotImplementedError
17
+
18
+ @property
19
+ @abstractmethod
20
+ def tools(self) -> list[BaseTool]:
21
+ """Get all server management tools and tools from the active server.
22
+
23
+ Returns:
24
+ list of LangChain tools for server management plus tools from active server
25
+ """
26
+ raise NotImplementedError
27
+
28
+ @abstractmethod
29
+ def has_tool_changes(self, current_tool_names: set[str]) -> bool:
30
+ """Check if the available tools have changed.
31
+ Args:
32
+ current_tool_names: Set of currently known tool names
33
+ Returns:
34
+ True if tools have changed, False otherwise
35
+ """
36
+ raise NotImplementedError
@@ -4,10 +4,11 @@ from mcp_use.client import MCPClient
4
4
  from mcp_use.logging import logger
5
5
 
6
6
  from ..adapters.base import BaseAdapter
7
+ from .base import BaseServerManager
7
8
  from .tools import ConnectServerTool, DisconnectServerTool, GetActiveServerTool, ListServersTool, SearchToolsTool
8
9
 
9
10
 
10
- class ServerManager:
11
+ class ServerManager(BaseServerManager):
11
12
  """Manages MCP servers and provides tools for server selection and management.
12
13
 
13
14
  This class allows an agent to discover and select which MCP server to use,