Skip to content

Commit ddf691c

Browse files
authored
Support multiple --with-editable flags in CLI commands (#1634)
1 parent 953d3eb commit ddf691c

File tree

8 files changed

+104
-77
lines changed

8 files changed

+104
-77
lines changed

src/fastmcp/cli/claude.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def update_claude_config(
3434
file_spec: str,
3535
server_name: str,
3636
*,
37-
with_editable: Path | None = None,
37+
with_editable: list[Path] | None = None,
3838
with_packages: list[str] | None = None,
3939
env_vars: dict[str, str] | None = None,
4040
) -> bool:
@@ -43,7 +43,7 @@ def update_claude_config(
4343
Args:
4444
file_spec: Path to the server file, optionally with :object suffix
4545
server_name: Name for the server in Claude's config
46-
with_editable: Optional directory to install in editable mode
46+
with_editable: Optional list of directories to install in editable mode
4747
with_packages: Optional list of additional packages to install
4848
env_vars: Optional dictionary of environment variables. These are merged with
4949
any existing variables, with new values taking precedence.
@@ -101,7 +101,7 @@ def update_claude_config(
101101
# Build uv run command using Environment.build_uv_args()
102102
env_config = Environment(
103103
dependencies=deduplicated_packages,
104-
editable=[str(with_editable)] if with_editable else None,
104+
editable=[str(p) for p in with_editable] if with_editable else None,
105105
)
106106
args = env_config.build_uv_args()
107107

src/fastmcp/cli/cli.py

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -126,20 +126,21 @@ async def dev(
126126
server_spec: str | None = None,
127127
*,
128128
with_editable: Annotated[
129-
Path | None,
129+
list[Path] | None,
130130
cyclopts.Parameter(
131-
name=["--with-editable", "-e"],
132-
help="Directory containing pyproject.toml to install in editable mode",
131+
"--with-editable",
132+
help="Directory containing pyproject.toml to install in editable mode (can be used multiple times)",
133+
negative="",
133134
),
134135
] = None,
135136
with_packages: Annotated[
136-
list[str],
137+
list[str] | None,
137138
cyclopts.Parameter(
138139
"--with",
139-
help="Additional packages to install",
140+
help="Additional packages to install (can be used multiple times)",
140141
negative="",
141142
),
142-
] = [],
143+
] = None,
143144
inspector_version: Annotated[
144145
str | None,
145146
cyclopts.Parameter(
@@ -188,6 +189,9 @@ async def dev(
188189
Args:
189190
server_spec: Python file to run, optionally with :object suffix, or None to auto-detect fastmcp.json
190191
"""
192+
# Convert None to empty lists for list parameters
193+
with_editable = with_editable or []
194+
with_packages = with_packages or []
191195
from pathlib import Path
192196

193197
from fastmcp.utilities.fastmcp_config import FastMCPConfig
@@ -229,13 +233,9 @@ async def dev(
229233
if config.environment.requirements
230234
else None
231235
)
232-
# Note: config.environment.editable is a list, but CLI only supports single path
233-
# Take the first editable path if available
234-
with_editable = with_editable or (
235-
Path(config.environment.editable[0])
236-
if config.environment.editable and config.environment.editable[0]
237-
else None
238-
)
236+
# Merge editable paths from config with CLI args
237+
if config.environment.editable and not with_editable:
238+
with_editable = [Path(p) for p in config.environment.editable]
239239

240240
# Merge packages from both sources
241241
if config.environment.dependencies:
@@ -256,7 +256,7 @@ async def dev(
256256
"Starting dev server",
257257
extra={
258258
"server_spec": server_spec,
259-
"with_editable": str(with_editable) if with_editable else None,
259+
"with_editable": [str(p) for p in with_editable] if with_editable else None,
260260
"with_packages": with_packages,
261261
"ui_port": ui_port,
262262
"server_port": server_port,
@@ -305,7 +305,7 @@ async def dev(
305305
dependencies=with_packages if with_packages else None,
306306
requirements=str(with_requirements) if with_requirements else None,
307307
project=str(project) if project else None,
308-
editable=[str(with_editable)] if with_editable else None,
308+
editable=[str(p) for p in with_editable] if with_editable else None,
309309
)
310310
uv_cmd = ["uv"] + env_config.build_uv_args(["fastmcp", "run", server_spec])
311311

@@ -396,13 +396,13 @@ async def run(
396396
),
397397
] = None,
398398
with_packages: Annotated[
399-
list[str],
399+
list[str] | None,
400400
cyclopts.Parameter(
401401
"--with",
402402
help="Additional packages to install (can be used multiple times)",
403403
negative="",
404404
),
405-
] = [],
405+
] = None,
406406
project: Annotated[
407407
Path | None,
408408
cyclopts.Parameter(
@@ -450,6 +450,8 @@ async def run(
450450
Args:
451451
server_spec: Python file, object specification (file:obj), config file, URL, or None to auto-detect
452452
"""
453+
# Convert None to empty lists for list parameters
454+
with_packages = with_packages or []
453455
# Load configuration if needed
454456
from pathlib import Path
455457

@@ -631,13 +633,13 @@ async def inspect(
631633
),
632634
] = None,
633635
with_packages: Annotated[
634-
list[str],
636+
list[str] | None,
635637
cyclopts.Parameter(
636638
"--with",
637639
help="Additional packages to install (can be used multiple times)",
638640
negative="",
639641
),
640-
] = [],
642+
] = None,
641643
project: Annotated[
642644
Path | None,
643645
cyclopts.Parameter(
@@ -670,6 +672,8 @@ async def inspect(
670672
Args:
671673
server_spec: Python file to inspect, optionally with :object suffix, or fastmcp.json
672674
"""
675+
# Convert None to empty lists for list parameters
676+
with_packages = with_packages or []
673677
from pathlib import Path
674678

675679
from fastmcp.utilities.fastmcp_config import FastMCPConfig

src/fastmcp/cli/install/claude_code.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def install_claude_code(
7575
server_object: str | None,
7676
name: str,
7777
*,
78-
with_editable: Path | None = None,
78+
with_editable: list[Path] | None = None,
7979
with_packages: list[str] | None = None,
8080
env_vars: dict[str, str] | None = None,
8181
python_version: str | None = None,
@@ -88,7 +88,7 @@ def install_claude_code(
8888
file: Path to the server file
8989
server_object: Optional server object name (for :object suffix)
9090
name: Name for the server in Claude Code
91-
with_editable: Optional directory to install in editable mode
91+
with_editable: Optional list of directories to install in editable mode
9292
with_packages: Optional list of additional packages to install
9393
env_vars: Optional dictionary of environment variables
9494
python_version: Optional Python version to use
@@ -121,7 +121,7 @@ def install_claude_code(
121121
dependencies=deduplicated_packages,
122122
requirements=str(with_requirements) if with_requirements else None,
123123
project=str(project) if project else None,
124-
editable=[str(with_editable)] if with_editable else None,
124+
editable=[str(p) for p in with_editable] if with_editable else None,
125125
)
126126
args = env_config.build_uv_args()
127127

@@ -171,28 +171,29 @@ async def claude_code_command(
171171
),
172172
] = None,
173173
with_editable: Annotated[
174-
Path | None,
174+
list[Path] | None,
175175
cyclopts.Parameter(
176-
name=["--with-editable", "-e"],
177-
help="Directory with pyproject.toml to install in editable mode",
176+
"--with-editable",
177+
help="Directory with pyproject.toml to install in editable mode (can be used multiple times)",
178+
negative="",
178179
),
179180
] = None,
180181
with_packages: Annotated[
181-
list[str],
182+
list[str] | None,
182183
cyclopts.Parameter(
183184
"--with",
184-
help="Additional packages to install",
185+
help="Additional packages to install (can be used multiple times)",
185186
negative="",
186187
),
187-
] = [],
188+
] = None,
188189
env_vars: Annotated[
189-
list[str],
190+
list[str] | None,
190191
cyclopts.Parameter(
191192
"--env",
192-
help="Environment variables in KEY=VALUE format",
193+
help="Environment variables in KEY=VALUE format (can be used multiple times)",
193194
negative="",
194195
),
195-
] = [],
196+
] = None,
196197
env_file: Annotated[
197198
Path | None,
198199
cyclopts.Parameter(
@@ -227,6 +228,10 @@ async def claude_code_command(
227228
Args:
228229
server_spec: Python file to install, optionally with :object suffix
229230
"""
231+
# Convert None to empty lists for list parameters
232+
with_editable = with_editable or []
233+
with_packages = with_packages or []
234+
env_vars = env_vars or []
230235
file, server_object, name, packages, env_dict = await process_common_args(
231236
server_spec, server_name, with_packages, env_vars, env_file
232237
)

src/fastmcp/cli/install/claude_desktop.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def install_claude_desktop(
4040
server_object: str | None,
4141
name: str,
4242
*,
43-
with_editable: Path | None = None,
43+
with_editable: list[Path] | None = None,
4444
with_packages: list[str] | None = None,
4545
env_vars: dict[str, str] | None = None,
4646
python_version: str | None = None,
@@ -53,7 +53,7 @@ def install_claude_desktop(
5353
file: Path to the server file
5454
server_object: Optional server object name (for :object suffix)
5555
name: Name for the server in Claude's config
56-
with_editable: Optional directory to install in editable mode
56+
with_editable: Optional list of directories to install in editable mode
5757
with_packages: Optional list of additional packages to install
5858
env_vars: Optional dictionary of environment variables
5959
python_version: Optional Python version to use
@@ -86,7 +86,7 @@ def install_claude_desktop(
8686
dependencies=deduplicated_packages,
8787
requirements=str(with_requirements) if with_requirements else None,
8888
project=str(project) if project else None,
89-
editable=[str(with_editable)] if with_editable else None,
89+
editable=[str(p) for p in with_editable] if with_editable else None,
9090
)
9191
args = env_config.build_uv_args()
9292

@@ -143,28 +143,29 @@ async def claude_desktop_command(
143143
),
144144
] = None,
145145
with_editable: Annotated[
146-
Path | None,
146+
list[Path] | None,
147147
cyclopts.Parameter(
148-
name=["--with-editable", "-e"],
149-
help="Directory with pyproject.toml to install in editable mode",
148+
"--with-editable",
149+
help="Directory with pyproject.toml to install in editable mode (can be used multiple times)",
150+
negative="",
150151
),
151152
] = None,
152153
with_packages: Annotated[
153-
list[str],
154+
list[str] | None,
154155
cyclopts.Parameter(
155156
"--with",
156-
help="Additional packages to install",
157+
help="Additional packages to install (can be used multiple times)",
157158
negative="",
158159
),
159-
] = [],
160+
] = None,
160161
env_vars: Annotated[
161-
list[str],
162+
list[str] | None,
162163
cyclopts.Parameter(
163164
"--env",
164-
help="Environment variables in KEY=VALUE format",
165+
help="Environment variables in KEY=VALUE format (can be used multiple times)",
165166
negative="",
166167
),
167-
] = [],
168+
] = None,
168169
env_file: Annotated[
169170
Path | None,
170171
cyclopts.Parameter(
@@ -199,6 +200,10 @@ async def claude_desktop_command(
199200
Args:
200201
server_spec: Python file to install, optionally with :object suffix
201202
"""
203+
# Convert None to empty lists for list parameters
204+
with_editable = with_editable or []
205+
with_packages = with_packages or []
206+
env_vars = env_vars or []
202207
file, server_object, name, with_packages, env_dict = await process_common_args(
203208
server_spec, server_name, with_packages, env_vars, env_file
204209
)

0 commit comments

Comments
 (0)