def cli(argv: list[str] = sys.argv[1:]):
from argparse import SUPPRESS, ArgumentParser
parser = ArgumentParser("mcp-hmr", description="Hot Reloading for MCP Servers • Automatically reload on code changes")
if sys.version_info >= (3, 14):
parser.suggest_on_error = True
parser.add_argument("target", help="The import path of the FastMCP instance. Supports module:attr and path:attr")
parser.add_argument("-t", "--transport", choices=["stdio", "http", "sse", "streamable-http"], default="stdio", help="Transport protocol to use (default: stdio)")
parser.add_argument("-l", "--log-level", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], type=str.upper, default=None)
parser.add_argument("--host", default="localhost", help="Host to bind to for http/sse transports (default: localhost)")
parser.add_argument("--port", type=int, default=None, help="Port to bind to for http/sse transports (default: 8000)")
parser.add_argument("--path", default=None, help="Route path for the server (default: /mcp for http, /mcp/sse for sse)")
parser.add_argument("--version", action="version", version=f"mcp-hmr {__version__}", help=SUPPRESS)
if not argv:
parser.print_help()
return
args = parser.parse_args(argv)
target: str = args.target
if ":" not in target[1:-1]:
parser.exit(1, f"The target argument must be in the format 'module:attr' (e.g. 'main:app') or 'path:attr' (e.g. './path/to/main.py:app'). Got: '{target}'")
from asyncio import run
from contextlib import suppress
if (cwd := str(Path.cwd())) not in sys.path:
sys.path.append(cwd)
if (file := Path(module_or_path := target[: target.rindex(":")])).is_file():
sys.path.insert(0, str(file.parent))
else:
if "." in module_or_path: # find_spec may cause implicit imports of parent packages
from reactivity.hmr.core import patch_meta_path
patch_meta_path()
if find_spec(module_or_path) is None:
parser.exit(1, f"The target '{module_or_path}' not found. Please provide a valid module name or a file path.")
with suppress(KeyboardInterrupt):
run(run_with_hmr(**args.__dict__))