Skip to main content
Glama
ingeno
by ingeno
run.py6.88 kB
"""FastMCP run command implementation with enhanced type hints.""" import json import re import sys from pathlib import Path from typing import Any, Literal from mcp.server.fastmcp import FastMCP as FastMCP1x from fastmcp.server.server import FastMCP from fastmcp.utilities.logging import get_logger from fastmcp.utilities.mcp_server_config import ( MCPServerConfig, ) from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource logger = get_logger("cli.run") # Type aliases for better type safety TransportType = Literal["stdio", "http", "sse", "streamable-http"] LogLevelType = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] 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 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.as_proxy(client) return server except Exception as e: logger.error(f"Failed to create client for URL {url}: {e}") sys.exit(1) def create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None]: """Create a FastMCP server from a MCPConfig.""" from fastmcp import FastMCP with mcp_config_path.open() as src: mcp_config = json.load(src) server = FastMCP.as_proxy(mcp_config) return server def load_mcp_server_config(config_path: Path) -> MCPServerConfig: """Load a FastMCP configuration from a fastmcp.json file. Args: config_path: Path to fastmcp.json file Returns: MCPServerConfig object """ config = MCPServerConfig.from_file(config_path) # Apply runtime settings from deployment config config.deployment.apply_runtime_settings(config_path) return config async def run_command( server_spec: str, transport: TransportType | None = None, host: str | None = None, port: int | None = None, path: str | None = None, log_level: LogLevelType | None = None, server_args: list[str] | None = None, show_banner: bool = True, use_direct_import: bool = False, skip_source: bool = False, ) -> None: """Run a MCP server or connect to a remote one. Args: server_spec: Python file, object specification (file:obj), config file, 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 path: Path to bind to when using http transport log_level: Log level server_args: Additional arguments to pass to the server show_banner: Whether to show the server banner use_direct_import: Whether to use direct import instead of subprocess skip_source: Whether to skip source preparation step """ # Special case: URLs if is_url(server_spec): # Handle URL case server = create_client_server(server_spec) logger.debug(f"Created client proxy server for {server_spec}") # Special case: MCPConfig files (legacy) elif server_spec.endswith(".json"): # Load JSON and check which type of config it is config_path = Path(server_spec) with open(config_path) as f: data = json.load(f) # Check if it's an MCPConfig first (has canonical mcpServers key) if "mcpServers" in data: # It's an MCP config server = create_mcp_config_server(config_path) else: # It's a FastMCP config - load it properly config = load_mcp_server_config(config_path) # Merge deployment config with CLI arguments (CLI takes precedence) transport = transport or config.deployment.transport host = host or config.deployment.host port = port or config.deployment.port path = path or config.deployment.path log_level = log_level or config.deployment.log_level server_args = ( server_args if server_args is not None else config.deployment.args ) # Prepare source only (environment is handled by uv run) await config.prepare_source() if not skip_source else None # Load the server using the source from contextlib import nullcontext from fastmcp.cli.cli import with_argv # Use sys.argv context manager if deployment args specified argv_context = with_argv(server_args) if server_args else nullcontext() with argv_context: server = await config.source.load_server() logger.debug(f'Found server "{server.name}" from config {config_path}') else: # Regular file case - create a MCPServerConfig with FileSystemSource source = FileSystemSource(path=server_spec) config = MCPServerConfig(source=source) # Prepare source only (environment is handled by uv run) await config.prepare_source() if not skip_source else None # Load the server from contextlib import nullcontext from fastmcp.cli.cli import with_argv # Use sys.argv context manager if server_args specified argv_context = with_argv(server_args) if server_args else nullcontext() with argv_context: server = await config.source.load_server() logger.debug(f'Found server "{server.name}" in {source.path}') # Run the server # handle v1 servers if isinstance(server, FastMCP1x): run_v1_server(server, host=host, port=port, transport=transport) return kwargs = {} if transport: kwargs["transport"] = transport if host: kwargs["host"] = host if port: kwargs["port"] = port if path: kwargs["path"] = path if log_level: kwargs["log_level"] = log_level if not show_banner: kwargs["show_banner"] = False try: await server.run_async(**kwargs) except Exception as e: logger.error(f"Failed to run server: {e}") sys.exit(1) def run_v1_server( server: FastMCP1x, host: str | None = None, port: int | None = None, transport: TransportType | None = None, ) -> None: from functools import partial if host: server.settings.host = host if port: server.settings.port = port match transport: case "stdio": runner = partial(server.run) case "http" | "streamable-http" | None: runner = partial(server.run, transport="streamable-http") case "sse": runner = partial(server.run, transport="sse") runner()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ingeno/mcp-openapi-lambda'

If you have feedback or need assistance with the MCP directory API, please join our Discord server