Skip to content

Commit 54b7a3b

Browse files
authored
Remove run_with_uv() and inline UV logic in cli.py (#1770)
1 parent 1e13764 commit 54b7a3b

File tree

3 files changed

+40
-365
lines changed

3 files changed

+40
-365
lines changed

src/fastmcp/cli/cli.py

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -465,39 +465,53 @@ async def run(
465465
needs_uv = config.environment.build_command(test_cmd) != test_cmd and not skip_env
466466

467467
if needs_uv:
468-
# Use uv run subprocess - always use run_with_uv which handles output correctly
468+
# Build the inner fastmcp command
469+
inner_cmd = ["fastmcp", "run", server_spec]
470+
471+
# Add transport options to the inner command
472+
if final_transport:
473+
inner_cmd.extend(["--transport", final_transport])
474+
# Only add HTTP-specific options for non-stdio transports
475+
if final_transport != "stdio":
476+
if final_host:
477+
inner_cmd.extend(["--host", final_host])
478+
if final_port:
479+
inner_cmd.extend(["--port", str(final_port)])
480+
if final_path:
481+
inner_cmd.extend(["--path", final_path])
482+
if final_log_level:
483+
inner_cmd.extend(["--log-level", final_log_level])
484+
if no_banner:
485+
inner_cmd.append("--no-banner")
486+
# Add skip-env flag to prevent infinite recursion
487+
inner_cmd.append("--skip-env")
488+
489+
# Add server args if any
490+
if final_server_args:
491+
inner_cmd.append("--")
492+
inner_cmd.extend(final_server_args)
493+
494+
# Build the full uv command using the config's environment
495+
cmd = config.environment.build_command(inner_cmd)
496+
497+
# Set marker to prevent infinite loops when subprocess calls FastMCP again
498+
env = os.environ | {"FASTMCP_UV_SPAWNED": "1"}
499+
500+
# Run the command
501+
logger.debug(f"Running command: {' '.join(cmd)}")
469502
try:
470-
run_module.run_with_uv(
471-
server_spec=server_spec,
472-
python_version=config.environment.python,
473-
with_packages=config.environment.dependencies,
474-
with_requirements=(
475-
Path(config.environment.requirements)
476-
if config.environment.requirements
477-
else None
478-
),
479-
project=(
480-
Path(config.environment.project)
481-
if config.environment.project
482-
else None
483-
),
484-
transport=final_transport,
485-
host=final_host,
486-
port=final_port,
487-
path=final_path,
488-
log_level=final_log_level,
489-
show_banner=not no_banner,
490-
editable=config.environment.editable,
491-
)
492-
except Exception as e:
503+
process = subprocess.run(cmd, check=True, env=env)
504+
sys.exit(process.returncode)
505+
except subprocess.CalledProcessError as e:
493506
logger.error(
494507
f"Failed to run: {e}",
495508
extra={
496509
"server_spec": server_spec,
497510
"error": str(e),
511+
"returncode": e.returncode,
498512
},
499513
)
500-
sys.exit(1)
514+
sys.exit(e.returncode)
501515
else:
502516
# Use direct import for backwards compatibility
503517
try:
@@ -643,6 +657,7 @@ async def inspect(
643657
"fastmcp",
644658
"inspect",
645659
server_spec,
660+
"--skip-env", # Prevent infinite recursion
646661
]
647662

648663
# Add format and output flags if specified

src/fastmcp/cli/run.py

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
"""FastMCP run command implementation with enhanced type hints."""
22

33
import json
4-
import os
54
import re
6-
import subprocess
75
import sys
86
from pathlib import Path
97
from typing import Any, Literal
@@ -15,7 +13,6 @@
1513
from fastmcp.utilities.mcp_server_config import (
1614
MCPServerConfig,
1715
)
18-
from fastmcp.utilities.mcp_server_config.v1.environments.uv import UVEnvironment
1916
from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource
2017

2118
logger = get_logger("cli.run")
@@ -31,86 +28,6 @@ def is_url(path: str) -> bool:
3128
return bool(url_pattern.match(path))
3229

3330

34-
def run_with_uv(
35-
server_spec: str,
36-
python_version: str | None = None,
37-
with_packages: list[str] | None = None,
38-
with_requirements: Path | None = None,
39-
project: Path | None = None,
40-
transport: TransportType | None = None,
41-
host: str | None = None,
42-
port: int | None = None,
43-
path: str | None = None,
44-
log_level: LogLevelType | None = None,
45-
show_banner: bool = True,
46-
editable: str | list[str] | None = None,
47-
) -> None:
48-
"""Run a MCP server using uv run subprocess.
49-
50-
This function is called when we need to set up a Python environment with specific
51-
dependencies before running the server. The config parsing and merging should already
52-
be done by the caller.
53-
54-
Args:
55-
server_spec: Python file, object specification (file:obj), config file, or URL
56-
python_version: Python version to use (e.g. "3.10")
57-
with_packages: Additional packages to install
58-
with_requirements: Requirements file to use
59-
project: Run the command within the given project directory
60-
transport: Transport protocol to use
61-
host: Host to bind to when using http transport
62-
port: Port to bind to when using http transport
63-
path: Path to bind to when using http transport
64-
log_level: Log level
65-
show_banner: Whether to show the server banner
66-
editable: Editable package paths
67-
"""
68-
69-
env_config = UVEnvironment(
70-
python=python_version,
71-
dependencies=with_packages if with_packages else None,
72-
requirements=str(with_requirements.resolve()) if with_requirements else None,
73-
project=str(project.resolve()) if project else None,
74-
editable=editable
75-
if isinstance(editable, list)
76-
else ([editable] if editable else None),
77-
)
78-
79-
# Build the inner fastmcp command (environment variable prevents infinite recursion)
80-
inner_cmd = ["fastmcp", "run", server_spec]
81-
82-
# Add transport options to the inner command
83-
if transport:
84-
inner_cmd.extend(["--transport", transport])
85-
# Only add HTTP-specific options for non-stdio transports
86-
if transport != "stdio":
87-
if host:
88-
inner_cmd.extend(["--host", host])
89-
if port:
90-
inner_cmd.extend(["--port", str(port)])
91-
if path:
92-
inner_cmd.extend(["--path", path])
93-
if log_level:
94-
inner_cmd.extend(["--log-level", log_level])
95-
if not show_banner:
96-
inner_cmd.append("--no-banner")
97-
98-
# Build the full uv command
99-
cmd = env_config.build_command(inner_cmd)
100-
101-
# Set marker to prevent infinite loops when subprocess calls FastMCP again
102-
env = os.environ | {"FASTMCP_UV_SPAWNED": "1"}
103-
104-
# Run the command
105-
logger.debug(f"Running command: {' '.join(cmd)}")
106-
try:
107-
process = subprocess.run(cmd, check=True, env=env)
108-
sys.exit(process.returncode)
109-
except subprocess.CalledProcessError as e:
110-
logger.error(f"Failed to run server: {e}")
111-
sys.exit(e.returncode)
112-
113-
11431
def create_client_server(url: str) -> Any:
11532
"""Create a FastMCP server from a client URL.
11633

0 commit comments

Comments
 (0)