Skip to content

Commit afedabe

Browse files
authored
Fix and centralize CLI path resolution (#1590)
1 parent 587e37f commit afedabe

File tree

13 files changed

+384
-386
lines changed

13 files changed

+384
-386
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ repos:
2525
- repo: local
2626
hooks:
2727
- id: ty
28-
name: type check
28+
name: ty check
2929
entry: uv run ty check
3030
language: system
3131
types: [python]
@@ -37,4 +37,5 @@ repos:
3737
rev: v6.0.0
3838
hooks:
3939
- id: no-commit-to-branch
40+
name: prevent commits to main
4041
args: [--branch, main]

src/fastmcp/cli/claude.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pathlib import Path
77
from typing import Any
88

9+
from fastmcp.utilities.cli import build_uv_run_args
910
from fastmcp.utilities.logging import get_logger
1011

1112
logger = get_logger(__name__)
@@ -89,20 +90,11 @@ def update_claude_config(
8990
else:
9091
env_vars = existing_env
9192

92-
# Build uv run command
93-
args = ["run"]
94-
95-
# Collect all packages in a set to deduplicate
96-
packages = {"fastmcp"}
97-
if with_packages:
98-
packages.update(pkg for pkg in with_packages if pkg)
99-
100-
# Add all packages with --with
101-
for pkg in sorted(packages):
102-
args.extend(["--with", pkg])
103-
104-
if with_editable:
105-
args.extend(["--with-editable", str(with_editable)])
93+
# Build uv run command using centralized function
94+
args = build_uv_run_args(
95+
with_editable=with_editable,
96+
with_packages=with_packages,
97+
)
10698

10799
# Convert file path to absolute before adding to command
108100
# Split off any :object suffix first

src/fastmcp/cli/cli.py

Lines changed: 9 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from fastmcp.cli import run as run_module
2020
from fastmcp.cli.install import install_app
2121
from fastmcp.server.server import FastMCP
22+
from fastmcp.utilities.cli import build_uv_command
2223
from fastmcp.utilities.inspect import FastMCPInfo, inspect_fastmcp
2324
from fastmcp.utilities.logging import get_logger
2425

@@ -57,46 +58,8 @@ def _parse_env_var(env_var: str) -> tuple[str, str]:
5758
return key.strip(), value.strip()
5859

5960

60-
def _build_uv_command(
61-
server_spec: str,
62-
with_editable: Path | None = None,
63-
with_packages: list[str] | None = None,
64-
no_banner: bool = False,
65-
python_version: str | None = None,
66-
with_requirements: Path | None = None,
67-
project: Path | None = None,
68-
) -> list[str]:
69-
"""Build the uv run command that runs a MCP server through mcp run."""
70-
cmd = ["uv", "run"]
71-
72-
# Add Python version if specified
73-
if python_version:
74-
cmd.extend(["--python", python_version])
75-
76-
# Add project if specified
77-
if project:
78-
cmd.extend(["--project", str(project)])
79-
80-
cmd.extend(["--with", "fastmcp"])
81-
82-
if with_editable:
83-
cmd.extend(["--with-editable", str(with_editable)])
84-
85-
if with_packages:
86-
for pkg in with_packages:
87-
if pkg:
88-
cmd.extend(["--with", pkg])
89-
90-
if with_requirements:
91-
cmd.extend(["--with-requirements", str(with_requirements)])
92-
93-
# Add mcp run command
94-
cmd.extend(["fastmcp", "run", server_spec])
95-
96-
if no_banner:
97-
cmd.append("--no-banner")
98-
99-
return cmd
61+
# The _build_uv_command function has been moved to cli/utils.py
62+
# and is now imported as build_uv_command
10063

10164

10265
@app.command
@@ -302,16 +265,18 @@ async def dev(
302265
if inspector_version:
303266
inspector_cmd += f"@{inspector_version}"
304267

305-
uv_cmd = _build_uv_command(
268+
uv_cmd = build_uv_command(
306269
server_spec,
307-
with_editable,
308-
with_packages,
309-
no_banner=True,
270+
with_editable=with_editable,
271+
with_packages=with_packages,
310272
python_version=python,
311273
with_requirements=with_requirements,
312274
project=project,
313275
)
314276

277+
# Add --no-banner flag for dev command
278+
uv_cmd.append("--no-banner")
279+
315280
# Run the MCP Inspector command with shell=True on Windows
316281
shell = sys.platform == "win32"
317282
process = subprocess.run(

src/fastmcp/cli/install/claude_code.py

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import cyclopts
1010
from rich import print
1111

12+
from fastmcp.utilities.cli import build_uv_run_args
1213
from fastmcp.utilities.logging import get_logger
1314

1415
from .shared import process_common_args
@@ -106,31 +107,14 @@ def install_claude_code(
106107
)
107108
return False
108109

109-
# Build uv run command
110-
args = ["run"]
111-
112-
# Add Python version if specified
113-
if python_version:
114-
args.extend(["--python", python_version])
115-
116-
# Add project if specified
117-
if project:
118-
args.extend(["--project", str(project)])
119-
120-
# Collect all packages in a set to deduplicate
121-
packages = {"fastmcp"}
122-
if with_packages:
123-
packages.update(pkg for pkg in with_packages if pkg)
124-
125-
# Add all packages with --with
126-
for pkg in sorted(packages):
127-
args.extend(["--with", pkg])
128-
129-
if with_editable:
130-
args.extend(["--with-editable", str(with_editable)])
131-
132-
if with_requirements:
133-
args.extend(["--with-requirements", str(with_requirements)])
110+
# Build uv run command using centralized function
111+
args = build_uv_run_args(
112+
with_editable=with_editable,
113+
with_packages=with_packages,
114+
python_version=python_version,
115+
with_requirements=with_requirements,
116+
project=project,
117+
)
134118

135119
# Build server spec from parsed components
136120
if server_object:

src/fastmcp/cli/install/claude_desktop.py

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from rich import print
1010

1111
from fastmcp.mcp_config import StdioMCPServer, update_config_file
12+
from fastmcp.utilities.cli import build_uv_run_args
1213
from fastmcp.utilities.logging import get_logger
1314

1415
from .shared import process_common_args
@@ -72,31 +73,14 @@ def install_claude_desktop(
7273

7374
config_file = config_dir / "claude_desktop_config.json"
7475

75-
# Build uv run command
76-
args = ["run"]
77-
78-
# Add Python version if specified
79-
if python_version:
80-
args.extend(["--python", python_version])
81-
82-
# Add project if specified
83-
if project:
84-
args.extend(["--project", str(project)])
85-
86-
# Collect all packages in a set to deduplicate
87-
packages = {"fastmcp"}
88-
if with_packages:
89-
packages.update(pkg for pkg in with_packages if pkg)
90-
91-
# Add all packages with --with
92-
for pkg in sorted(packages):
93-
args.extend(["--with", pkg])
94-
95-
if with_editable:
96-
args.extend(["--with-editable", str(with_editable)])
97-
98-
if with_requirements:
99-
args.extend(["--with-requirements", str(with_requirements)])
76+
# Build uv run command using centralized function
77+
args = build_uv_run_args(
78+
with_editable=with_editable,
79+
with_packages=with_packages,
80+
python_version=python_version,
81+
with_requirements=with_requirements,
82+
project=project,
83+
)
10084

10185
# Build server spec from parsed components
10286
if server_object:

src/fastmcp/cli/install/cursor.py

Lines changed: 17 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from rich import print
1111

1212
from fastmcp.mcp_config import StdioMCPServer, update_config_file
13+
from fastmcp.utilities.cli import build_uv_run_args
1314
from fastmcp.utilities.logging import get_logger
1415

1516
from .shared import process_common_args
@@ -106,31 +107,14 @@ def install_cursor_workspace(
106107

107108
config_file = cursor_dir / "mcp.json"
108109

109-
# Build uv run command
110-
args = ["run"]
111-
112-
# Add Python version if specified
113-
if python_version:
114-
args.extend(["--python", python_version])
115-
116-
# Add project if specified
117-
if project:
118-
args.extend(["--project", str(project)])
119-
120-
# Collect all packages in a set to deduplicate
121-
packages = {"fastmcp"}
122-
if with_packages:
123-
packages.update(pkg for pkg in with_packages if pkg)
124-
125-
# Add all packages with --with
126-
for pkg in sorted(packages):
127-
args.extend(["--with", pkg])
128-
129-
if with_editable:
130-
args.extend(["--with-editable", str(with_editable)])
131-
132-
if with_requirements:
133-
args.extend(["--with-requirements", str(with_requirements)])
110+
# Build uv run command using centralized function
111+
args = build_uv_run_args(
112+
with_editable=with_editable,
113+
with_packages=with_packages,
114+
python_version=python_version,
115+
with_requirements=with_requirements,
116+
project=project,
117+
)
134118

135119
# Build server spec from parsed components
136120
if server_object:
@@ -194,31 +178,14 @@ def install_cursor(
194178
Returns:
195179
True if installation was successful, False otherwise
196180
"""
197-
# Build uv run command
198-
args = ["run"]
199-
200-
# Add Python version if specified
201-
if python_version:
202-
args.extend(["--python", python_version])
203-
204-
# Add project if specified
205-
if project:
206-
args.extend(["--project", str(project)])
207-
208-
# Collect all packages in a set to deduplicate
209-
packages = {"fastmcp"}
210-
if with_packages:
211-
packages.update(pkg for pkg in with_packages if pkg)
212-
213-
# Add all packages with --with
214-
for pkg in sorted(packages):
215-
args.extend(["--with", pkg])
216-
217-
if with_editable:
218-
args.extend(["--with-editable", str(with_editable)])
219-
220-
if with_requirements:
221-
args.extend(["--with-requirements", str(with_requirements)])
181+
# Build uv run command using centralized function
182+
args = build_uv_run_args(
183+
with_editable=with_editable,
184+
with_packages=with_packages,
185+
python_version=python_version,
186+
with_requirements=with_requirements,
187+
project=project,
188+
)
222189

223190
# Build server spec from parsed components
224191
if server_object:

src/fastmcp/cli/install/mcp_json.py

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import pyperclip
1010
from rich import print
1111

12+
from fastmcp.utilities.cli import build_uv_run_args
1213
from fastmcp.utilities.logging import get_logger
1314

1415
from .shared import process_common_args
@@ -47,31 +48,14 @@ def install_mcp_json(
4748
True if generation was successful, False otherwise
4849
"""
4950
try:
50-
# Build uv run command
51-
args = ["run"]
52-
53-
# Add Python version if specified
54-
if python_version:
55-
args.extend(["--python", python_version])
56-
57-
# Add project if specified
58-
if project:
59-
args.extend(["--project", str(project)])
60-
61-
# Collect all packages in a set to deduplicate
62-
packages = {"fastmcp"}
63-
if with_packages:
64-
packages.update(pkg for pkg in with_packages if pkg)
65-
66-
# Add all packages with --with
67-
for pkg in sorted(packages):
68-
args.extend(["--with", pkg])
69-
70-
if with_editable:
71-
args.extend(["--with-editable", str(with_editable)])
72-
73-
if with_requirements:
74-
args.extend(["--with-requirements", str(with_requirements)])
51+
# Build uv run command using centralized function
52+
args = build_uv_run_args(
53+
with_editable=with_editable,
54+
with_packages=with_packages,
55+
python_version=python_version,
56+
with_requirements=with_requirements,
57+
project=project,
58+
)
7559

7660
# Build server spec from parsed components
7761
if server_object:

0 commit comments

Comments
 (0)