Spaces:
Running
Running
| """FastMCP run command implementation.""" | |
| import importlib.util | |
| import re | |
| import sys | |
| from pathlib import Path | |
| from typing import Any, Literal | |
| from fastmcp.utilities.logging import get_logger | |
| logger = get_logger("cli.run") | |
| TransportType = Literal["stdio", "streamable-http", "sse"] | |
| def is_url(path: str) -> bool: | |
| """Check if a string is a URL.""" | |
| url_pattern = re.compile(r"^https?://") | |
| return bool(url_pattern.match(path)) | |
| def parse_file_path(server_spec: str) -> tuple[Path, str | None]: | |
| """Parse a file path that may include a server object specification. | |
| Args: | |
| server_spec: Path to file, optionally with :object suffix | |
| Returns: | |
| Tuple of (file_path, server_object) | |
| """ | |
| # First check if we have a Windows path (e.g., C:\...) | |
| has_windows_drive = len(server_spec) > 1 and server_spec[1] == ":" | |
| # Split on the last colon, but only if it's not part of the Windows drive letter | |
| # and there's actually another colon in the string after the drive letter | |
| if ":" in (server_spec[2:] if has_windows_drive else server_spec): | |
| file_str, server_object = server_spec.rsplit(":", 1) | |
| else: | |
| file_str, server_object = server_spec, None | |
| # Resolve the file path | |
| file_path = Path(file_str).expanduser().resolve() | |
| if not file_path.exists(): | |
| logger.error(f"File not found: {file_path}") | |
| sys.exit(1) | |
| if not file_path.is_file(): | |
| logger.error(f"Not a file: {file_path}") | |
| sys.exit(1) | |
| return file_path, server_object | |
| def import_server(file: Path, server_object: str | None = None) -> Any: | |
| """Import a MCP server from a file. | |
| Args: | |
| file: Path to the file | |
| server_object: Optional object name in format "module:object" or just "object" | |
| Returns: | |
| The server object | |
| """ | |
| # Add parent directory to Python path so imports can be resolved | |
| file_dir = str(file.parent) | |
| if file_dir not in sys.path: | |
| sys.path.insert(0, file_dir) | |
| # Import the module | |
| spec = importlib.util.spec_from_file_location("server_module", file) | |
| if not spec or not spec.loader: | |
| logger.error("Could not load module", extra={"file": str(file)}) | |
| sys.exit(1) | |
| module = importlib.util.module_from_spec(spec) | |
| spec.loader.exec_module(module) | |
| # If no object specified, try common server names | |
| if not server_object: | |
| # Look for the most common server object names | |
| for name in ["mcp", "server", "app"]: | |
| if hasattr(module, name): | |
| return getattr(module, name) | |
| logger.error( | |
| f"No server object found in {file}. Please either:\n" | |
| "1. Use a standard variable name (mcp, server, or app)\n" | |
| "2. Specify the object name with file:object syntax", | |
| extra={"file": str(file)}, | |
| ) | |
| sys.exit(1) | |
| # Handle module:object syntax | |
| if ":" in server_object: | |
| module_name, object_name = server_object.split(":", 1) | |
| try: | |
| server_module = importlib.import_module(module_name) | |
| server = getattr(server_module, object_name, None) | |
| except ImportError: | |
| logger.error( | |
| f"Could not import module '{module_name}'", | |
| extra={"file": str(file)}, | |
| ) | |
| sys.exit(1) | |
| else: | |
| # Just object name | |
| server = getattr(module, server_object, None) | |
| if server is None: | |
| logger.error( | |
| f"Server object '{server_object}' not found", | |
| extra={"file": str(file)}, | |
| ) | |
| sys.exit(1) | |
| return server | |
| def create_client_server(url: str) -> Any: | |
| """Create a FastMCP server from a client URL. | |
| Args: | |
| url: The URL to connect to | |
| Returns: | |
| A FastMCP server instance | |
| """ | |
| try: | |
| import fastmcp | |
| client = fastmcp.Client(url) | |
| server = fastmcp.FastMCP.from_client(client) | |
| return server | |
| except Exception as e: | |
| logger.error(f"Failed to create client for URL {url}: {e}") | |
| sys.exit(1) | |
| def run_command( | |
| server_spec: str, | |
| transport: str | None = None, | |
| host: str | None = None, | |
| port: int | None = None, | |
| log_level: str | None = None, | |
| ) -> None: | |
| """Run a MCP server or connect to a remote one. | |
| Args: | |
| server_spec: Python file, object specification (file:obj), or URL | |
| transport: Transport protocol to use | |
| host: Host to bind to when using http transport | |
| port: Port to bind to when using http transport | |
| log_level: Log level | |
| """ | |
| if is_url(server_spec): | |
| # Handle URL case | |
| server = create_client_server(server_spec) | |
| logger.debug(f"Created client proxy server for {server_spec}") | |
| else: | |
| # Handle file case | |
| file, server_object = parse_file_path(server_spec) | |
| server = import_server(file, server_object) | |
| logger.debug(f'Found server "{server.name}" in {file}') | |
| # Run the server | |
| kwargs = {} | |
| if transport: | |
| kwargs["transport"] = transport | |
| if host: | |
| kwargs["host"] = host | |
| if port: | |
| kwargs["port"] = port | |
| if log_level: | |
| kwargs["log_level"] = log_level | |
| try: | |
| server.run(**kwargs) | |
| except Exception as e: | |
| logger.error(f"Failed to run server: {e}") | |
| sys.exit(1) | |