diff --git a/docs/deployment/running-server.mdx b/docs/deployment/running-server.mdx
index 6436c82da..5d98687ed 100644
--- a/docs/deployment/running-server.mdx
+++ b/docs/deployment/running-server.mdx
@@ -53,12 +53,48 @@ You can specify transport options and other configuration:
fastmcp run server.py --transport sse --port 9000
```
+### Dependency Management with CLI
+
+When using the FastMCP CLI, you can pass additional options to configure how `uv` runs your server:
+
+```bash
+# Run with a specific Python version
+fastmcp run server.py --python 3.11
+
+# Run with additional packages
+fastmcp run server.py --with pandas --with numpy
+
+# Run with dependencies from a requirements file
+fastmcp run server.py --with-requirements requirements.txt
+
+# Combine multiple options
+fastmcp run server.py --python 3.10 --with httpx --transport http
+
+# Run within a specific project directory
+fastmcp run server.py --project /path/to/project
+```
+
+
+When using `--python`, `--with`, `--project`, or `--with-requirements`, the server runs via `uv run` subprocess instead of using your local environment. The `uv` command will manage dependencies based on your project configuration.
+
+
+
+The `--python` option is particularly useful when you need to run a server with a specific Python version that differs from your system's default. This addresses common compatibility issues where servers require a particular Python version to function correctly.
+
+
For development and testing, you can use the `dev` command to run your server with the MCP Inspector:
```bash
fastmcp dev server.py
```
+The `dev` command also supports the same dependency management options:
+
+```bash
+# Dev server with specific Python version and packages
+fastmcp dev server.py --python 3.11 --with pandas
+```
+
See the [CLI documentation](/patterns/cli) for detailed information about all available commands and options.
### Passing Arguments to Servers
diff --git a/docs/integrations/claude-code.mdx b/docs/integrations/claude-code.mdx
index b17c63624..b8c21b87c 100644
--- a/docs/integrations/claude-code.mdx
+++ b/docs/integrations/claude-code.mdx
@@ -62,12 +62,26 @@ The command will automatically configure the server with Claude Code's `claude m
#### Dependencies
-If your server has dependencies, include them with the `--with` flag:
+FastMCP provides flexible dependency management options for your Claude Code servers:
+
+**Individual packages**: Use the `--with` flag to specify packages your server needs. You can use this flag multiple times:
```bash
fastmcp install claude-code server.py --with pandas --with requests
```
+**Requirements file**: If you maintain a `requirements.txt` file with all your dependencies, use `--with-requirements` to install them:
+
+```bash
+fastmcp install claude-code server.py --with-requirements requirements.txt
+```
+
+**Editable packages**: For local packages under development, use `--with-editable` to install them in editable mode:
+
+```bash
+fastmcp install claude-code server.py --with-editable ./my-local-package
+```
+
Alternatively, you can specify dependencies directly in your server code:
```python server.py
@@ -79,14 +93,30 @@ mcp = FastMCP(
)
```
+#### Python Version and Project Configuration
+
+Control the Python environment for your server with these options:
+
+**Python version**: Use `--python` to specify which Python version your server requires. This ensures compatibility when your server needs specific Python features:
+
+```bash
+fastmcp install claude-code server.py --python 3.11
+```
+
+**Project directory**: Use `--project` to run your server within a specific project context. This tells `uv` to use the project's configuration files and virtual environment:
+
+```bash
+fastmcp install claude-code server.py --project /path/to/my-project
+```
+
#### Environment Variables
If your server needs environment variables (like API keys), you must include them:
```bash
fastmcp install claude-code server.py --name "Weather Server" \
- --env-var API_KEY=your-api-key \
- --env-var DEBUG=true
+ --env API_KEY=your-api-key \
+ --env DEBUG=true
```
Or load them from a `.env` file:
@@ -101,7 +131,7 @@ fastmcp install claude-code server.py --name "Weather Server" --env-file .env
### Manual Configuration
-For more control over the configuration, you can manually use Claude Code's built-in MCP management commands:
+For more control over the configuration, you can manually use Claude Code's built-in MCP management commands. This gives you direct control over how your server is launched:
```bash
# Add a server with custom configuration
@@ -114,6 +144,16 @@ claude mcp add weather-server -e API_KEY=secret -e DEBUG=true -- uv run --with f
claude mcp add my-server --scope user -- uv run --with fastmcp fastmcp run server.py
```
+You can also manually specify Python versions and project directories in your Claude Code commands:
+
+```bash
+# With specific Python version
+claude mcp add ml-server -- uv run --python 3.11 --with fastmcp fastmcp run server.py
+
+# Within a project directory
+claude mcp add project-server -- uv run --project /path/to/project --with fastmcp fastmcp run server.py
+```
+
## Using the Server
Once your server is installed, you can start using your FastMCP server with Claude Code.
diff --git a/docs/integrations/claude-desktop.mdx b/docs/integrations/claude-desktop.mdx
index 2d37d523e..a69bebb0b 100644
--- a/docs/integrations/claude-desktop.mdx
+++ b/docs/integrations/claude-desktop.mdx
@@ -78,12 +78,26 @@ After installation, restart Claude Desktop completely. You should see a hammer i
#### Dependencies
-If your server has dependencies, include them with the `--with` flag:
+FastMCP provides several ways to manage your server's dependencies when installing in Claude Desktop:
+
+**Individual packages**: Use the `--with` flag to specify packages your server needs. You can use this flag multiple times:
```bash
fastmcp install claude-desktop server.py --with pandas --with requests
```
+**Requirements file**: If you have a `requirements.txt` file listing all your dependencies, use `--with-requirements` to install them all at once:
+
+```bash
+fastmcp install claude-desktop server.py --with-requirements requirements.txt
+```
+
+**Editable packages**: For local packages in development, use `--with-editable` to install them in editable mode:
+
+```bash
+fastmcp install claude-desktop server.py --with-editable ./my-local-package
+```
+
Alternatively, you can specify dependencies directly in your server code:
```python server.py
@@ -95,6 +109,24 @@ mcp = FastMCP(
)
```
+#### Python Version and Project Directory
+
+FastMCP allows you to control the Python environment for your server:
+
+**Python version**: Use `--python` to specify which Python version your server should run with. This is particularly useful when your server requires a specific Python version:
+
+```bash
+fastmcp install claude-desktop server.py --python 3.11
+```
+
+**Project directory**: Use `--project` to run your server within a specific project directory. This ensures that `uv` will discover all `pyproject.toml`, `uv.toml`, and `.python-version` files from that project:
+
+```bash
+fastmcp install claude-desktop server.py --project /path/to/my-project
+```
+
+When you specify a project directory, all relative paths in your server will be resolved from that directory, and the project's virtual environment will be used.
+
#### Environment Variables
@@ -105,8 +137,8 @@ If your server needs environment variables (like API keys), you must include the
```bash
fastmcp install claude-desktop server.py --name "Weather Server" \
- --env-var API_KEY=your-api-key \
- --env-var DEBUG=true
+ --env API_KEY=your-api-key \
+ --env DEBUG=true
```
Or load them from a `.env` file:
@@ -146,6 +178,8 @@ After updating the configuration file, restart Claude Desktop completely. Look f
If your server has dependencies, you can use `uv` or another package manager to set up the environment.
+When manually configuring dependencies, the recommended approach is to use `uv` with FastMCP. The configuration uses `uv run` to create an isolated environment with your specified packages:
+
```json
{
"mcpServers": {
@@ -153,9 +187,11 @@ If your server has dependencies, you can use `uv` or another package manager to
"command": "uv",
"args": [
"run",
+ "--with", "fastmcp",
"--with", "pandas",
"--with", "requests",
- "python",
+ "fastmcp",
+ "run",
"path/to/your/server.py"
]
}
@@ -163,6 +199,29 @@ If your server has dependencies, you can use `uv` or another package manager to
}
```
+You can also manually specify Python versions and project directories in your configuration. Add `--python` to use a specific Python version, or `--project` to run within a project directory:
+
+```json
+{
+ "mcpServers": {
+ "dice-roller": {
+ "command": "uv",
+ "args": [
+ "run",
+ "--python", "3.11",
+ "--project", "/path/to/project",
+ "--with", "fastmcp",
+ "fastmcp",
+ "run",
+ "path/to/your/server.py"
+ ]
+ }
+ }
+}
+```
+
+The order of arguments matters: Python version and project settings come before package specifications, which come before the actual command to run.
+
- **`uv` must be installed and available in your system PATH**. Claude Desktop runs in its own isolated environment and needs `uv` to manage dependencies.
- **On macOS, it is recommended to install `uv` globally with Homebrew** so that Claude Desktop will detect it: `brew install uv`. Installing `uv` with other methods may not make it accessible to Claude Desktop.
diff --git a/docs/integrations/cursor.mdx b/docs/integrations/cursor.mdx
index e44e770ba..0f0dec816 100644
--- a/docs/integrations/cursor.mdx
+++ b/docs/integrations/cursor.mdx
@@ -64,12 +64,26 @@ After running the command, Cursor will open automatically and prompt you to inst
#### Dependencies
-If your server has dependencies, include them with the `--with` flag:
+FastMCP offers multiple ways to manage dependencies for your Cursor servers:
+
+**Individual packages**: Use the `--with` flag to specify packages your server needs. You can use this flag multiple times:
```bash
fastmcp install cursor server.py --with pandas --with requests
```
+**Requirements file**: For projects with a `requirements.txt` file, use `--with-requirements` to install all dependencies at once:
+
+```bash
+fastmcp install cursor server.py --with-requirements requirements.txt
+```
+
+**Editable packages**: When developing local packages, use `--with-editable` to install them in editable mode:
+
+```bash
+fastmcp install cursor server.py --with-editable ./my-local-package
+```
+
Alternatively, you can specify dependencies directly in your server code:
```python server.py
@@ -81,6 +95,22 @@ mcp = FastMCP(
)
```
+#### Python Version and Project Configuration
+
+Control your server's Python environment with these options:
+
+**Python version**: Use `--python` to specify which Python version your server should use. This is essential when your server requires specific Python features:
+
+```bash
+fastmcp install cursor server.py --python 3.11
+```
+
+**Project directory**: Use `--project` to run your server within a specific project context. This ensures `uv` discovers all project configuration files and uses the correct virtual environment:
+
+```bash
+fastmcp install cursor server.py --project /path/to/my-project
+```
+
#### Environment Variables
@@ -91,8 +121,8 @@ If your server needs environment variables (like API keys), you must include the
```bash
fastmcp install cursor server.py --name "Weather Server" \
- --env-var API_KEY=your-api-key \
- --env-var DEBUG=true
+ --env API_KEY=your-api-key \
+ --env DEBUG=true
```
Or load them from a `.env` file:
@@ -147,6 +177,8 @@ After updating the configuration file, your server should be available in Cursor
If your server has dependencies, you can use `uv` or another package manager to set up the environment.
+When manually configuring dependencies, the recommended approach is to use `uv` with FastMCP. The configuration should use `uv run` to create an isolated environment with your specified packages:
+
```json
{
"mcpServers": {
@@ -154,9 +186,11 @@ If your server has dependencies, you can use `uv` or another package manager to
"command": "uv",
"args": [
"run",
+ "--with", "fastmcp",
"--with", "pandas",
"--with", "requests",
- "python",
+ "fastmcp",
+ "run",
"path/to/your/server.py"
]
}
@@ -164,6 +198,29 @@ If your server has dependencies, you can use `uv` or another package manager to
}
```
+You can also manually specify Python versions and project directories in your configuration:
+
+```json
+{
+ "mcpServers": {
+ "dice-roller": {
+ "command": "uv",
+ "args": [
+ "run",
+ "--python", "3.11",
+ "--project", "/path/to/project",
+ "--with", "fastmcp",
+ "fastmcp",
+ "run",
+ "path/to/your/server.py"
+ ]
+ }
+ }
+}
+```
+
+Note that the order of arguments is important: Python version and project settings should come before package specifications.
+
**`uv` must be installed and available in your system PATH**. Cursor runs in its own isolated environment and needs `uv` to manage dependencies.
diff --git a/docs/integrations/mcp-json-configuration.mdx b/docs/integrations/mcp-json-configuration.mdx
index 9a185043c..f3be1db06 100644
--- a/docs/integrations/mcp-json-configuration.mdx
+++ b/docs/integrations/mcp-json-configuration.mdx
@@ -136,6 +136,10 @@ To use this in a client configuration file, add it to the `mcpServers` object in
}
```
+
+When using `--python`, `--project`, or `--with-requirements`, the generated configuration will include these options in the `uv run` command, ensuring your server runs with the correct Python version and dependencies.
+
+
Different MCP clients may have specific configuration requirements or formatting needs. Always consult your client's documentation to ensure proper integration.
@@ -165,6 +169,9 @@ fastmcp install mcp-json server.py --with pandas --with requests --with httpx
# Editable local package
fastmcp install mcp-json server.py --with-editable ./my-package
+
+# From requirements file
+fastmcp install mcp-json server.py --with-requirements requirements.txt
```
You can also specify dependencies directly in your server code:
@@ -190,6 +197,18 @@ fastmcp install mcp-json server.py \
fastmcp install mcp-json server.py --env-file .env
```
+### Python Version and Project Directory
+
+Specify Python version or run within a specific project:
+
+```bash
+# Use specific Python version
+fastmcp install mcp-json server.py --python 3.11
+
+# Run within a project directory
+fastmcp install mcp-json server.py --project /path/to/project
+```
+
### Server Object Selection
Use the same `file.py:object` notation as other FastMCP commands:
@@ -250,6 +269,17 @@ fastmcp install mcp-json api_server.py \
--env TIMEOUT=30
```
+### Advanced Configuration
+
+```bash
+fastmcp install mcp-json ml_server.py \
+ --name "ML Analysis Server" \
+ --python 3.11 \
+ --with-requirements requirements.txt \
+ --project /home/user/ml-project \
+ --env GPU_DEVICE=0
+```
+
Output:
```json
{
@@ -275,6 +305,32 @@ Output:
}
```
+The advanced configuration example generates:
+```json
+{
+ "ML Analysis Server": {
+ "command": "uv",
+ "args": [
+ "run",
+ "--python",
+ "3.11",
+ "--project",
+ "/home/user/ml-project",
+ "--with",
+ "fastmcp",
+ "--with-requirements",
+ "requirements.txt",
+ "fastmcp",
+ "run",
+ "/home/user/ml_server.py"
+ ],
+ "env": {
+ "GPU_DEVICE": "0"
+ }
+ }
+}
+```
+
### Pipeline Usage
Save configuration to file:
diff --git a/docs/patterns/cli.mdx b/docs/patterns/cli.mdx
index dce1b5b32..e394b99f5 100644
--- a/docs/patterns/cli.mdx
+++ b/docs/patterns/cli.mdx
@@ -18,8 +18,8 @@ fastmcp --help
| Command | Purpose | Dependency Management |
| ------- | ------- | --------------------- |
-| `run` | Run a FastMCP server directly | Uses your current environment; you are responsible for ensuring all dependencies are available |
-| `dev` | Run a server with the MCP Inspector for testing | Creates an isolated environment; dependencies must be explicitly specified with `--with` and/or `--with-editable` |
+| `run` | Run a FastMCP server directly | Default: Uses your local environment directly. With `--python`, `--with`, `--project`, or `--with-requirements`: Runs via `uv run` subprocess |
+| `dev` | Run a server with the MCP Inspector for testing | Always runs via `uv run` subprocess (never uses your local environment); dependencies must be specified or available in a uv-managed project |
| `install` | Install a server in MCP client applications | Creates an isolated environment; dependencies must be explicitly specified with `--with` and/or `--with-editable` |
| `inspect` | Generate a JSON report about a FastMCP server | Uses your current environment; you are responsible for ensuring all dependencies are available |
| `version` | Display version information | N/A |
@@ -35,7 +35,7 @@ fastmcp run server.py
```
-This command runs the server directly in your current Python environment. You are responsible for ensuring all dependencies are available.
+By default, this command runs the server directly in your current Python environment. You are responsible for ensuring all dependencies are available. When using `--python`, `--with`, `--project`, or `--with-requirements` options, it runs the server via `uv run` subprocess instead.
#### Options
@@ -48,6 +48,10 @@ This command runs the server directly in your current Python environment. You ar
| Path | `--path` | Path to bind to when using http transport (default: `/mcp/` or `/sse/` for SSE) |
| Log Level | `--log-level`, `-l` | Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) |
| No Banner | `--no-banner` | Disable the startup banner display |
+| Python Version | `--python` | Python version to use (e.g., 3.10, 3.11) |
+| Additional Packages | `--with` | Additional packages to install (can be used multiple times) |
+| Project Directory | `--project` | Run the command within the given project directory |
+| Requirements File | `--with-requirements` | Requirements file to install dependencies from |
#### Server Specification
@@ -96,6 +100,18 @@ fastmcp run https://example.com/mcp-server
# Connect to a remote server with specified log level
fastmcp run https://example.com/mcp-server --log-level DEBUG
+
+# Run with a specific Python version
+fastmcp run server.py --python 3.11
+
+# Run with additional packages
+fastmcp run server.py --with pandas --with numpy
+
+# Run within a specific project directory
+fastmcp run server.py --project /path/to/project
+
+# Run with dependencies from a requirements file
+fastmcp run server.py --with-requirements requirements.txt
```
### `dev`
@@ -107,7 +123,7 @@ fastmcp dev server.py
```
-This command runs your server in an isolated environment. All dependencies must be explicitly specified using the `--with` and/or `--with-editable` options.
+This command always runs your server via `uv run` subprocess (never your local environment) to work with the MCP Inspector. All dependencies must be explicitly specified using the `--with` and/or `--with-editable` options, or be available in a uv-managed project.
@@ -136,12 +152,24 @@ This command does not support HTTP testing. To test a server over Streamable HTT
| Inspector Version | `--inspector-version` | Version of the MCP Inspector to use |
| UI Port | `--ui-port` | Port for the MCP Inspector UI |
| Server Port | `--server-port` | Port for the MCP Inspector Proxy server |
+| Python Version | `--python` | Python version to use (e.g., 3.10, 3.11) |
+| Project Directory | `--project` | Run the command within the given project directory |
+| Requirements File | `--with-requirements` | Requirements file to install dependencies from |
-**Example**
+**Examples**
```bash
# Run dev server with editable mode and additional packages
fastmcp dev server.py -e . --with pandas --with matplotlib
+
+# Run dev server with specific Python version
+fastmcp dev server.py --python 3.11
+
+# Run dev server with requirements file
+fastmcp dev server.py --with-requirements requirements.txt
+
+# Run dev server within a specific project directory
+fastmcp dev server.py --project /path/to/project
```
### `install`
@@ -167,6 +195,10 @@ Note that for security reasons, MCP clients usually run every server in a comple
**`uv` must be installed and available in your system PATH**. Both Claude Desktop and Cursor run in isolated environments and need `uv` to manage dependencies. On macOS, install `uv` globally with Homebrew for Claude Desktop compatibility: `brew install uv`.
+
+**Python Version Considerations**: The install commands now support the `--python` option to specify a Python version directly. You can also use `--project` to run within a specific project directory or `--with-requirements` to install dependencies from a requirements file.
+
+
**FastMCP `install` commands focus on local server files with STDIO transport.** For remote servers running with HTTP or SSE transport, use your client's native configuration - FastMCP's value is simplifying the complex local setup with dependencies and `uv` commands.
@@ -187,6 +219,9 @@ The `install` command supports the same `file.py:object` notation as the `run` c
| Additional Packages | `--with` | Additional packages to install (can be used multiple times) |
| Environment Variables | `--env` | Environment variables in KEY=VALUE format (can be used multiple times) |
| Environment File | `--env-file`, `-f` | Load environment variables from a .env file |
+| Python Version | `--python` | Python version to use (e.g., 3.10, 3.11) |
+| Project Directory | `--project` | Run the command within the given project directory |
+| Requirements File | `--with-requirements` | Requirements file to install dependencies from |
**Examples**
@@ -209,6 +244,15 @@ fastmcp install cursor server.py --env API_KEY=secret --env DEBUG=true
# Install with environment file
fastmcp install cursor server.py --env-file .env
+# Install with specific Python version
+fastmcp install claude-desktop server.py --python 3.11
+
+# Install with requirements file
+fastmcp install claude-code server.py --with-requirements requirements.txt
+
+# Install within a project directory
+fastmcp install cursor server.py --project /path/to/project
+
# Generate MCP JSON configuration
fastmcp install mcp-json server.py --name "My Server" --with pandas
diff --git a/src/fastmcp/cli/cli.py b/src/fastmcp/cli/cli.py
index a7ecf771f..1d0a45973 100644
--- a/src/fastmcp/cli/cli.py
+++ b/src/fastmcp/cli/cli.py
@@ -62,11 +62,22 @@ def _build_uv_command(
with_editable: Path | None = None,
with_packages: list[str] | None = None,
no_banner: bool = False,
+ python_version: str | None = None,
+ with_requirements: Path | None = None,
+ project: Path | None = None,
) -> list[str]:
"""Build the uv run command that runs a MCP server through mcp run."""
- cmd = ["uv"]
+ cmd = ["uv", "run"]
- cmd.extend(["run", "--with", "fastmcp"])
+ # Add Python version if specified
+ if python_version:
+ cmd.extend(["--python", python_version])
+
+ # Add project if specified
+ if project:
+ cmd.extend(["--project", str(project)])
+
+ cmd.extend(["--with", "fastmcp"])
if with_editable:
cmd.extend(["--with-editable", str(with_editable)])
@@ -76,6 +87,9 @@ def _build_uv_command(
if pkg:
cmd.extend(["--with", pkg])
+ if with_requirements:
+ cmd.extend(["--with-requirements", str(with_requirements)])
+
# Add mcp run command
cmd.extend(["fastmcp", "run", server_spec])
@@ -163,6 +177,27 @@ def dev(
help="Port for the MCP Inspector Proxy server",
),
] = None,
+ python: Annotated[
+ str | None,
+ cyclopts.Parameter(
+ "--python",
+ help="Python version to use (e.g., 3.10, 3.11)",
+ ),
+ ] = None,
+ with_requirements: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--with-requirements",
+ help="Requirements file to install dependencies from",
+ ),
+ ] = None,
+ project: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--project",
+ help="Run the command within the given project directory",
+ ),
+ ] = None,
) -> None:
"""Run an MCP server with the MCP Inspector for development.
@@ -209,7 +244,13 @@ def dev(
inspector_cmd += f"@{inspector_version}"
uv_cmd = _build_uv_command(
- server_spec, with_editable, with_packages, no_banner=True
+ server_spec,
+ with_editable,
+ with_packages,
+ no_banner=True,
+ python_version=python,
+ with_requirements=with_requirements,
+ project=project,
)
# Run the MCP Inspector command with shell=True on Windows
@@ -288,6 +329,35 @@ def run(
negative=False,
),
] = False,
+ python: Annotated[
+ str | None,
+ cyclopts.Parameter(
+ "--python",
+ help="Python version to use (e.g., 3.10, 3.11)",
+ ),
+ ] = None,
+ with_packages: Annotated[
+ list[str],
+ cyclopts.Parameter(
+ "--with",
+ help="Additional packages to install (can be used multiple times)",
+ negative=False,
+ ),
+ ] = [],
+ project: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--project",
+ help="Run the command within the given project directory",
+ ),
+ ] = None,
+ with_requirements: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--with-requirements",
+ help="Requirements file to install dependencies from",
+ ),
+ ] = None,
) -> None:
"""Run an MCP server or connect to a remote one.
@@ -318,26 +388,53 @@ def run(
},
)
- try:
- run_module.run_command(
- server_spec=server_spec,
- transport=transport,
- host=host,
- port=port,
- path=path,
- log_level=log_level,
- server_args=server_args,
- show_banner=not no_banner,
- )
- except Exception as e:
- logger.error(
- f"Failed to run: {e}",
- extra={
- "server_spec": server_spec,
- "error": str(e),
- },
- )
- sys.exit(1)
+ # If any uv-specific options are provided, use uv run
+ if python or with_packages or with_requirements or project:
+ try:
+ run_module.run_with_uv(
+ server_spec=server_spec,
+ python_version=python,
+ with_packages=with_packages,
+ with_requirements=with_requirements,
+ project=project,
+ transport=transport,
+ host=host,
+ port=port,
+ path=path,
+ log_level=log_level,
+ show_banner=not no_banner,
+ )
+ except Exception as e:
+ logger.error(
+ f"Failed to run: {e}",
+ extra={
+ "server_spec": server_spec,
+ "error": str(e),
+ },
+ )
+ sys.exit(1)
+ else:
+ # Use direct import for backwards compatibility
+ try:
+ run_module.run_command(
+ server_spec=server_spec,
+ transport=transport,
+ host=host,
+ port=port,
+ path=path,
+ log_level=log_level,
+ server_args=server_args,
+ show_banner=not no_banner,
+ )
+ except Exception as e:
+ logger.error(
+ f"Failed to run: {e}",
+ extra={
+ "server_spec": server_spec,
+ "error": str(e),
+ },
+ )
+ sys.exit(1)
@app.command
diff --git a/src/fastmcp/cli/install/claude_code.py b/src/fastmcp/cli/install/claude_code.py
index 59923e9ad..7f8e89d72 100644
--- a/src/fastmcp/cli/install/claude_code.py
+++ b/src/fastmcp/cli/install/claude_code.py
@@ -77,6 +77,9 @@ def install_claude_code(
with_editable: Path | None = None,
with_packages: list[str] | None = None,
env_vars: dict[str, str] | None = None,
+ python_version: str | None = None,
+ with_requirements: Path | None = None,
+ project: Path | None = None,
) -> bool:
"""Install FastMCP server in Claude Code.
@@ -87,6 +90,9 @@ def install_claude_code(
with_editable: Optional directory to install in editable mode
with_packages: Optional list of additional packages to install
env_vars: Optional dictionary of environment variables
+ python_version: Optional Python version to use
+ with_requirements: Optional requirements file to install from
+ project: Optional project directory to run within
Returns:
True if installation was successful, False otherwise
@@ -103,6 +109,14 @@ def install_claude_code(
# Build uv run command
args = ["run"]
+ # Add Python version if specified
+ if python_version:
+ args.extend(["--python", python_version])
+
+ # Add project if specified
+ if project:
+ args.extend(["--project", str(project)])
+
# Collect all packages in a set to deduplicate
packages = {"fastmcp"}
if with_packages:
@@ -115,6 +129,9 @@ def install_claude_code(
if with_editable:
args.extend(["--with-editable", str(with_editable)])
+ if with_requirements:
+ args.extend(["--with-requirements", str(with_requirements)])
+
# Build server spec from parsed components
if server_object:
server_spec = f"{file.resolve()}:{server_object}"
@@ -190,6 +207,27 @@ def claude_code_command(
help="Load environment variables from .env file",
),
] = None,
+ python: Annotated[
+ str | None,
+ cyclopts.Parameter(
+ "--python",
+ help="Python version to use (e.g., 3.10, 3.11)",
+ ),
+ ] = None,
+ with_requirements: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--with-requirements",
+ help="Requirements file to install dependencies from",
+ ),
+ ] = None,
+ project: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--project",
+ help="Run the command within the given project directory",
+ ),
+ ] = None,
) -> None:
"""Install an MCP server in Claude Code.
@@ -207,6 +245,9 @@ def claude_code_command(
with_editable=with_editable,
with_packages=packages,
env_vars=env_dict,
+ python_version=python,
+ with_requirements=with_requirements,
+ project=project,
)
if success:
diff --git a/src/fastmcp/cli/install/claude_desktop.py b/src/fastmcp/cli/install/claude_desktop.py
index f23ecbd11..c47426de9 100644
--- a/src/fastmcp/cli/install/claude_desktop.py
+++ b/src/fastmcp/cli/install/claude_desktop.py
@@ -42,6 +42,9 @@ def install_claude_desktop(
with_editable: Path | None = None,
with_packages: list[str] | None = None,
env_vars: dict[str, str] | None = None,
+ python_version: str | None = None,
+ with_requirements: Path | None = None,
+ project: Path | None = None,
) -> bool:
"""Install FastMCP server in Claude Desktop.
@@ -52,6 +55,9 @@ def install_claude_desktop(
with_editable: Optional directory to install in editable mode
with_packages: Optional list of additional packages to install
env_vars: Optional dictionary of environment variables
+ python_version: Optional Python version to use
+ with_requirements: Optional requirements file to install from
+ project: Optional project directory to run within
Returns:
True if installation was successful, False otherwise
@@ -69,6 +75,14 @@ def install_claude_desktop(
# Build uv run command
args = ["run"]
+ # Add Python version if specified
+ if python_version:
+ args.extend(["--python", python_version])
+
+ # Add project if specified
+ if project:
+ args.extend(["--project", str(project)])
+
# Collect all packages in a set to deduplicate
packages = {"fastmcp"}
if with_packages:
@@ -81,6 +95,9 @@ def install_claude_desktop(
if with_editable:
args.extend(["--with-editable", str(with_editable)])
+ if with_requirements:
+ args.extend(["--with-requirements", str(with_requirements)])
+
# Build server spec from parsed components
if server_object:
server_spec = f"{file.resolve()}:{server_object}"
@@ -163,6 +180,27 @@ def claude_desktop_command(
help="Load environment variables from .env file",
),
] = None,
+ python: Annotated[
+ str | None,
+ cyclopts.Parameter(
+ "--python",
+ help="Python version to use (e.g., 3.10, 3.11)",
+ ),
+ ] = None,
+ with_requirements: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--with-requirements",
+ help="Requirements file to install dependencies from",
+ ),
+ ] = None,
+ project: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--project",
+ help="Run the command within the given project directory",
+ ),
+ ] = None,
) -> None:
"""Install an MCP server in Claude Desktop.
@@ -180,6 +218,9 @@ def claude_desktop_command(
with_editable=with_editable,
with_packages=with_packages,
env_vars=env_dict,
+ python_version=python,
+ with_requirements=with_requirements,
+ project=project,
)
if not success:
diff --git a/src/fastmcp/cli/install/cursor.py b/src/fastmcp/cli/install/cursor.py
index 6bdd49c65..23e611687 100644
--- a/src/fastmcp/cli/install/cursor.py
+++ b/src/fastmcp/cli/install/cursor.py
@@ -72,6 +72,9 @@ def install_cursor(
with_editable: Path | None = None,
with_packages: list[str] | None = None,
env_vars: dict[str, str] | None = None,
+ python_version: str | None = None,
+ with_requirements: Path | None = None,
+ project: Path | None = None,
) -> bool:
"""Install FastMCP server in Cursor.
@@ -82,6 +85,9 @@ def install_cursor(
with_editable: Optional directory to install in editable mode
with_packages: Optional list of additional packages to install
env_vars: Optional dictionary of environment variables
+ python_version: Optional Python version to use
+ with_requirements: Optional requirements file to install from
+ project: Optional project directory to run within
Returns:
True if installation was successful, False otherwise
@@ -89,6 +95,14 @@ def install_cursor(
# Build uv run command
args = ["run"]
+ # Add Python version if specified
+ if python_version:
+ args.extend(["--python", python_version])
+
+ # Add project if specified
+ if project:
+ args.extend(["--project", str(project)])
+
# Collect all packages in a set to deduplicate
packages = {"fastmcp"}
if with_packages:
@@ -101,6 +115,9 @@ def install_cursor(
if with_editable:
args.extend(["--with-editable", str(with_editable)])
+ if with_requirements:
+ args.extend(["--with-requirements", str(with_requirements)])
+
# Build server spec from parsed components
if server_object:
server_spec = f"{file.resolve()}:{server_object}"
@@ -173,6 +190,27 @@ def cursor_command(
help="Load environment variables from .env file",
),
] = None,
+ python: Annotated[
+ str | None,
+ cyclopts.Parameter(
+ "--python",
+ help="Python version to use (e.g., 3.10, 3.11)",
+ ),
+ ] = None,
+ with_requirements: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--with-requirements",
+ help="Requirements file to install dependencies from",
+ ),
+ ] = None,
+ project: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--project",
+ help="Run the command within the given project directory",
+ ),
+ ] = None,
) -> None:
"""Install an MCP server in Cursor.
@@ -190,6 +228,9 @@ def cursor_command(
with_editable=with_editable,
with_packages=with_packages,
env_vars=env_dict,
+ python_version=python,
+ with_requirements=with_requirements,
+ project=project,
)
if not success:
diff --git a/src/fastmcp/cli/install/mcp_json.py b/src/fastmcp/cli/install/mcp_json.py
index 91bd40ece..1afaa7a3b 100644
--- a/src/fastmcp/cli/install/mcp_json.py
+++ b/src/fastmcp/cli/install/mcp_json.py
@@ -25,6 +25,9 @@ def install_mcp_json(
with_packages: list[str] | None = None,
env_vars: dict[str, str] | None = None,
copy: bool = False,
+ python_version: str | None = None,
+ with_requirements: Path | None = None,
+ project: Path | None = None,
) -> bool:
"""Generate MCP configuration JSON for manual installation.
@@ -36,6 +39,9 @@ def install_mcp_json(
with_packages: Optional list of additional packages to install
env_vars: Optional dictionary of environment variables
copy: If True, copy to clipboard instead of printing to stdout
+ python_version: Optional Python version to use
+ with_requirements: Optional requirements file to install from
+ project: Optional project directory to run within
Returns:
True if generation was successful, False otherwise
@@ -44,6 +50,14 @@ def install_mcp_json(
# Build uv run command
args = ["run"]
+ # Add Python version if specified
+ if python_version:
+ args.extend(["--python", python_version])
+
+ # Add project if specified
+ if project:
+ args.extend(["--project", str(project)])
+
# Collect all packages in a set to deduplicate
packages = {"fastmcp"}
if with_packages:
@@ -56,6 +70,9 @@ def install_mcp_json(
if with_editable:
args.extend(["--with-editable", str(with_editable)])
+ if with_requirements:
+ args.extend(["--with-requirements", str(with_requirements)])
+
# Build server spec from parsed components
if server_object:
server_spec = f"{file.resolve()}:{server_object}"
@@ -144,6 +161,27 @@ def mcp_json_command(
negative=False,
),
] = False,
+ python: Annotated[
+ str | None,
+ cyclopts.Parameter(
+ "--python",
+ help="Python version to use (e.g., 3.10, 3.11)",
+ ),
+ ] = None,
+ with_requirements: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--with-requirements",
+ help="Requirements file to install dependencies from",
+ ),
+ ] = None,
+ project: Annotated[
+ Path | None,
+ cyclopts.Parameter(
+ "--project",
+ help="Run the command within the given project directory",
+ ),
+ ] = None,
) -> None:
"""Generate MCP configuration JSON for manual installation.
@@ -162,6 +200,9 @@ def mcp_json_command(
with_packages=packages,
env_vars=env_dict,
copy=copy,
+ python_version=python,
+ with_requirements=with_requirements,
+ project=project,
)
if not success:
diff --git a/src/fastmcp/cli/run.py b/src/fastmcp/cli/run.py
index 373d9cfbb..f0464d2e4 100644
--- a/src/fastmcp/cli/run.py
+++ b/src/fastmcp/cli/run.py
@@ -2,6 +2,7 @@
import importlib.util
import re
+import subprocess
import sys
from pathlib import Path
from typing import Any, Literal
@@ -122,6 +123,84 @@ def import_server(file: Path, server_object: str | None = None) -> Any:
return server
+def run_with_uv(
+ server_spec: str,
+ python_version: str | None = None,
+ with_packages: list[str] | None = None,
+ with_requirements: Path | None = None,
+ project: Path | None = None,
+ transport: TransportType | None = None,
+ host: str | None = None,
+ port: int | None = None,
+ path: str | None = None,
+ log_level: LogLevelType | None = None,
+ show_banner: bool = True,
+) -> None:
+ """Run a MCP server using uv run subprocess.
+
+ Args:
+ server_spec: Python file, object specification (file:obj), or URL
+ python_version: Python version to use (e.g. "3.10")
+ with_packages: Additional packages to install
+ with_requirements: Requirements file to use
+ project: Run the command within the given project directory
+ 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
+ show_banner: Whether to show the server banner
+ """
+ cmd = ["uv", "run"]
+
+ # Add Python version if specified
+ if python_version:
+ cmd.extend(["--python", python_version])
+
+ # Add project if specified
+ if project:
+ cmd.extend(["--project", str(project)])
+
+ # Add fastmcp package
+ cmd.extend(["--with", "fastmcp"])
+
+ # Add additional packages
+ if with_packages:
+ for pkg in with_packages:
+ if pkg:
+ cmd.extend(["--with", pkg])
+
+ # Add requirements file
+ if with_requirements:
+ cmd.extend(["--with-requirements", str(with_requirements)])
+
+ # Add fastmcp run command
+ cmd.extend(["fastmcp", "run", server_spec])
+
+ # Add transport options
+ if transport:
+ cmd.extend(["--transport", transport])
+ if host:
+ cmd.extend(["--host", host])
+ if port:
+ cmd.extend(["--port", str(port)])
+ if path:
+ cmd.extend(["--path", path])
+ if log_level:
+ cmd.extend(["--log-level", log_level])
+ if not show_banner:
+ cmd.append("--no-banner")
+
+ # Run the command
+ logger.debug(f"Running command: {' '.join(cmd)}")
+ try:
+ process = subprocess.run(cmd, check=True)
+ sys.exit(process.returncode)
+ except subprocess.CalledProcessError as e:
+ logger.error(f"Failed to run server: {e}")
+ sys.exit(e.returncode)
+
+
def create_client_server(url: str) -> Any:
"""Create a FastMCP server from a client URL.
@@ -175,6 +254,7 @@ def run_command(
log_level: LogLevelType | None = None,
server_args: list[str] | None = None,
show_banner: bool = True,
+ use_direct_import: bool = False,
) -> None:
"""Run a MCP server or connect to a remote one.
@@ -187,6 +267,7 @@ def run_command(
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
"""
if is_url(server_spec):
# Handle URL case
diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py
index 097c7b17e..13b304446 100644
--- a/tests/cli/test_cli.py
+++ b/tests/cli/test_cli.py
@@ -90,6 +90,94 @@ class TestMainCLI:
]
assert cmd == expected
+ def test_build_uv_command_with_python_version(self):
+ """Test building uv command with Python version."""
+ cmd = _build_uv_command("server.py", python_version="3.11")
+ expected = [
+ "uv",
+ "run",
+ "--python",
+ "3.11",
+ "--with",
+ "fastmcp",
+ "fastmcp",
+ "run",
+ "server.py",
+ ]
+ assert cmd == expected
+
+ def test_build_uv_command_with_project(self):
+ """Test building uv command with project directory."""
+ project_path = Path("/path/to/project")
+ cmd = _build_uv_command("server.py", project=project_path)
+ expected = [
+ "uv",
+ "run",
+ "--project",
+ str(project_path),
+ "--with",
+ "fastmcp",
+ "fastmcp",
+ "run",
+ "server.py",
+ ]
+ assert cmd == expected
+
+ def test_build_uv_command_with_requirements(self):
+ """Test building uv command with requirements file."""
+ req_path = Path("requirements.txt")
+ cmd = _build_uv_command("server.py", with_requirements=req_path)
+ expected = [
+ "uv",
+ "run",
+ "--with",
+ "fastmcp",
+ "--with-requirements",
+ "requirements.txt",
+ "fastmcp",
+ "run",
+ "server.py",
+ ]
+ assert cmd == expected
+
+ def test_build_uv_command_with_all_options(self):
+ """Test building uv command with all options."""
+ project_path = Path("/my/project")
+ editable_path = Path("/local/pkg")
+ requirements_path = Path("reqs.txt")
+ cmd = _build_uv_command(
+ "server.py",
+ python_version="3.10",
+ project=project_path,
+ with_packages=["pandas", "numpy"],
+ with_requirements=requirements_path,
+ with_editable=editable_path,
+ no_banner=True,
+ )
+ expected = [
+ "uv",
+ "run",
+ "--python",
+ "3.10",
+ "--project",
+ str(project_path),
+ "--with",
+ "fastmcp",
+ "--with-editable",
+ str(editable_path),
+ "--with",
+ "pandas",
+ "--with",
+ "numpy",
+ "--with-requirements",
+ str(requirements_path),
+ "fastmcp",
+ "run",
+ "server.py",
+ "--no-banner",
+ ]
+ assert cmd == expected
+
class TestVersionCommand:
"""Test the version command."""
@@ -167,6 +255,29 @@ class TestDevCommand:
assert bound.arguments["inspector_version"] == "1.0.0"
assert bound.arguments["ui_port"] == 3000
+ def test_dev_command_parsing_with_new_options(self):
+ """Test dev command parsing with new uv options."""
+ command, bound, _ = app.parse_args(
+ [
+ "dev",
+ "server.py",
+ "--python",
+ "3.10",
+ "--project",
+ "/workspace",
+ "--with-requirements",
+ "dev-requirements.txt",
+ "--with",
+ "pytest",
+ ]
+ )
+ assert command is not None
+ assert bound.arguments["server_spec"] == "server.py"
+ assert bound.arguments["python"] == "3.10"
+ assert bound.arguments["project"] == Path("/workspace")
+ assert bound.arguments["with_requirements"] == Path("dev-requirements.txt")
+ assert bound.arguments["with_packages"] == ["pytest"]
+
class TestRunCommand:
"""Test the run command."""
@@ -236,6 +347,32 @@ class TestRunCommand:
assert "log_level" not in bound.arguments
assert "path" not in bound.arguments
+ def test_run_command_parsing_with_new_options(self):
+ """Test run command parsing with new uv options."""
+ command, bound, _ = app.parse_args(
+ [
+ "run",
+ "server.py",
+ "--python",
+ "3.11",
+ "--with",
+ "pandas",
+ "--with",
+ "numpy",
+ "--project",
+ "/path/to/project",
+ "--with-requirements",
+ "requirements.txt",
+ ]
+ )
+
+ assert command is not None
+ assert bound.arguments["server_spec"] == "server.py"
+ assert bound.arguments["python"] == "3.11"
+ assert bound.arguments["with_packages"] == ["pandas", "numpy"]
+ assert bound.arguments["project"] == Path("/path/to/project")
+ assert bound.arguments["with_requirements"] == Path("requirements.txt")
+
def test_run_command_transport_aliases(self):
"""Test that both 'http' and 'streamable-http' are accepted as valid transport options."""
# Test with 'http' transport
diff --git a/tests/cli/test_cursor.py b/tests/cli/test_cursor.py
index 5f3820b9d..2383f211a 100644
--- a/tests/cli/test_cursor.py
+++ b/tests/cli/test_cursor.py
@@ -327,6 +327,9 @@ class TestCursorCommand:
with_editable=None,
with_packages=[],
env_vars={},
+ python_version=None,
+ with_requirements=None,
+ project=None,
)
mock_exit.assert_not_called()
diff --git a/tests/cli/test_install.py b/tests/cli/test_install.py
index a16a1e61d..cefa541dc 100644
--- a/tests/cli/test_install.py
+++ b/tests/cli/test_install.py
@@ -1,3 +1,5 @@
+from pathlib import Path
+
from fastmcp.cli.install import install_app
@@ -63,6 +65,27 @@ class TestClaudeCodeInstall:
assert bound.arguments["with_packages"] == ["package1", "package2"]
assert bound.arguments["env_vars"] == ["VAR1=value1"]
+ def test_claude_code_with_new_options(self):
+ """Test claude-code install with new uv options."""
+ from pathlib import Path
+
+ command, bound, _ = install_app.parse_args(
+ [
+ "claude-code",
+ "server.py",
+ "--python",
+ "3.11",
+ "--project",
+ "/workspace",
+ "--with-requirements",
+ "requirements.txt",
+ ]
+ )
+
+ assert bound.arguments["python"] == "3.11"
+ assert bound.arguments["project"] == Path("/workspace")
+ assert bound.arguments["with_requirements"] == Path("requirements.txt")
+
class TestClaudeDesktopInstall:
"""Test claude-desktop install command."""
@@ -94,6 +117,27 @@ class TestClaudeDesktopInstall:
assert bound.arguments["env_vars"] == ["VAR1=value1", "VAR2=value2"]
+ def test_claude_desktop_with_new_options(self):
+ """Test claude-desktop install with new uv options."""
+ from pathlib import Path
+
+ command, bound, _ = install_app.parse_args(
+ [
+ "claude-desktop",
+ "server.py",
+ "--python",
+ "3.10",
+ "--project",
+ "/my/project",
+ "--with-requirements",
+ "reqs.txt",
+ ]
+ )
+
+ assert bound.arguments["python"] == "3.10"
+ assert bound.arguments["project"] == Path("/my/project")
+ assert bound.arguments["with_requirements"] == Path("reqs.txt")
+
class TestCursorInstall:
"""Test cursor install command."""
@@ -163,3 +207,45 @@ class TestInstallCommandParsing:
command, bound, _ = install_app.parse_args(["mcp-json", "server.py"])
assert command is not None
assert bound.arguments["server_spec"] == "server.py"
+
+ def test_python_option(self):
+ """Test --python option for all install commands."""
+ commands_to_test = [
+ ["claude-code", "server.py", "--python", "3.11"],
+ ["claude-desktop", "server.py", "--python", "3.11"],
+ ["cursor", "server.py", "--python", "3.11"],
+ ["mcp-json", "server.py", "--python", "3.11"],
+ ]
+
+ for cmd_args in commands_to_test:
+ command, bound, _ = install_app.parse_args(cmd_args)
+ assert command is not None
+ assert bound.arguments["python"] == "3.11"
+
+ def test_with_requirements_option(self):
+ """Test --with-requirements option for all install commands."""
+ commands_to_test = [
+ ["claude-code", "server.py", "--with-requirements", "requirements.txt"],
+ ["claude-desktop", "server.py", "--with-requirements", "requirements.txt"],
+ ["cursor", "server.py", "--with-requirements", "requirements.txt"],
+ ["mcp-json", "server.py", "--with-requirements", "requirements.txt"],
+ ]
+
+ for cmd_args in commands_to_test:
+ command, bound, _ = install_app.parse_args(cmd_args)
+ assert command is not None
+ assert str(bound.arguments["with_requirements"]) == "requirements.txt"
+
+ def test_project_option(self):
+ """Test --project option for all install commands."""
+ commands_to_test = [
+ ["claude-code", "server.py", "--project", "/path/to/project"],
+ ["claude-desktop", "server.py", "--project", "/path/to/project"],
+ ["cursor", "server.py", "--project", "/path/to/project"],
+ ["mcp-json", "server.py", "--project", "/path/to/project"],
+ ]
+
+ for cmd_args in commands_to_test:
+ command, bound, _ = install_app.parse_args(cmd_args)
+ assert command is not None
+ assert str(bound.arguments["project"]) == str(Path("/path/to/project"))
diff --git a/tests/cli/test_run_with_uv.py b/tests/cli/test_run_with_uv.py
new file mode 100644
index 000000000..3aac81fa7
--- /dev/null
+++ b/tests/cli/test_run_with_uv.py
@@ -0,0 +1,240 @@
+"""Tests for the run_with_uv function and related functionality."""
+
+import subprocess
+from pathlib import Path
+from unittest.mock import Mock, patch
+
+import pytest
+
+from fastmcp.cli.run import run_with_uv
+
+
+class TestRunWithUv:
+ """Test the run_with_uv function."""
+
+ @patch("subprocess.run")
+ def test_run_with_uv_basic(self, mock_run):
+ """Test basic run_with_uv execution."""
+ mock_run.return_value = Mock(returncode=0)
+
+ with pytest.raises(SystemExit) as exc_info:
+ run_with_uv("server.py")
+
+ assert exc_info.value.code == 0
+
+ # Check the command that was called
+ mock_run.assert_called_once()
+ cmd = mock_run.call_args[0][0]
+
+ expected = ["uv", "run", "--with", "fastmcp", "fastmcp", "run", "server.py"]
+ assert cmd == expected
+
+ @patch("subprocess.run")
+ def test_run_with_uv_python_version(self, mock_run):
+ """Test run_with_uv with Python version."""
+ mock_run.return_value = Mock(returncode=0)
+
+ with pytest.raises(SystemExit) as exc_info:
+ run_with_uv("server.py", python_version="3.11")
+
+ assert exc_info.value.code == 0
+
+ cmd = mock_run.call_args[0][0]
+ expected = [
+ "uv",
+ "run",
+ "--python",
+ "3.11",
+ "--with",
+ "fastmcp",
+ "fastmcp",
+ "run",
+ "server.py",
+ ]
+ assert cmd == expected
+
+ @patch("subprocess.run")
+ def test_run_with_uv_project(self, mock_run):
+ """Test run_with_uv with project directory."""
+ mock_run.return_value = Mock(returncode=0)
+ project_path = Path("/my/project")
+
+ with pytest.raises(SystemExit) as exc_info:
+ run_with_uv("server.py", project=project_path)
+
+ assert exc_info.value.code == 0
+
+ cmd = mock_run.call_args[0][0]
+ expected = [
+ "uv",
+ "run",
+ "--project",
+ str(Path("/my/project")),
+ "--with",
+ "fastmcp",
+ "fastmcp",
+ "run",
+ "server.py",
+ ]
+ assert cmd == expected
+
+ @patch("subprocess.run")
+ def test_run_with_uv_with_packages(self, mock_run):
+ """Test run_with_uv with additional packages."""
+ mock_run.return_value = Mock(returncode=0)
+
+ with pytest.raises(SystemExit) as exc_info:
+ run_with_uv("server.py", with_packages=["pandas", "numpy"])
+
+ assert exc_info.value.code == 0
+
+ cmd = mock_run.call_args[0][0]
+ expected = [
+ "uv",
+ "run",
+ "--with",
+ "fastmcp",
+ "--with",
+ "pandas",
+ "--with",
+ "numpy",
+ "fastmcp",
+ "run",
+ "server.py",
+ ]
+ assert cmd == expected
+
+ @patch("subprocess.run")
+ def test_run_with_uv_with_requirements(self, mock_run):
+ """Test run_with_uv with requirements file."""
+ mock_run.return_value = Mock(returncode=0)
+ req_path = Path("requirements.txt")
+
+ with pytest.raises(SystemExit) as exc_info:
+ run_with_uv("server.py", with_requirements=req_path)
+
+ assert exc_info.value.code == 0
+
+ cmd = mock_run.call_args[0][0]
+ expected = [
+ "uv",
+ "run",
+ "--with",
+ "fastmcp",
+ "--with-requirements",
+ "requirements.txt",
+ "fastmcp",
+ "run",
+ "server.py",
+ ]
+ assert cmd == expected
+
+ @patch("subprocess.run")
+ def test_run_with_uv_transport_options(self, mock_run):
+ """Test run_with_uv with transport-related options."""
+ mock_run.return_value = Mock(returncode=0)
+
+ with pytest.raises(SystemExit) as exc_info:
+ run_with_uv(
+ "server.py",
+ transport="http",
+ host="localhost",
+ port=8080,
+ path="/api",
+ log_level="DEBUG",
+ show_banner=False,
+ )
+
+ assert exc_info.value.code == 0
+
+ cmd = mock_run.call_args[0][0]
+ expected = [
+ "uv",
+ "run",
+ "--with",
+ "fastmcp",
+ "fastmcp",
+ "run",
+ "server.py",
+ "--transport",
+ "http",
+ "--host",
+ "localhost",
+ "--port",
+ "8080",
+ "--path",
+ "/api",
+ "--log-level",
+ "DEBUG",
+ "--no-banner",
+ ]
+ assert cmd == expected
+
+ @patch("subprocess.run")
+ def test_run_with_uv_all_options(self, mock_run):
+ """Test run_with_uv with all options combined."""
+ mock_run.return_value = Mock(returncode=0)
+
+ with pytest.raises(SystemExit) as exc_info:
+ run_with_uv(
+ "server.py",
+ python_version="3.10",
+ project=Path("/workspace"),
+ with_packages=["pandas"],
+ with_requirements=Path("reqs.txt"),
+ transport="http",
+ port=9000,
+ show_banner=False,
+ )
+
+ assert exc_info.value.code == 0
+
+ cmd = mock_run.call_args[0][0]
+ expected = [
+ "uv",
+ "run",
+ "--python",
+ "3.10",
+ "--project",
+ str(Path("/workspace")),
+ "--with",
+ "fastmcp",
+ "--with",
+ "pandas",
+ "--with-requirements",
+ "reqs.txt",
+ "fastmcp",
+ "run",
+ "server.py",
+ "--transport",
+ "http",
+ "--port",
+ "9000",
+ "--no-banner",
+ ]
+ assert cmd == expected
+
+ @patch("subprocess.run")
+ def test_run_with_uv_error_handling(self, mock_run):
+ """Test run_with_uv error handling."""
+ mock_run.side_effect = subprocess.CalledProcessError(1, ["uv", "run"])
+
+ with pytest.raises(SystemExit) as exc_info:
+ run_with_uv("server.py")
+
+ assert exc_info.value.code == 1
+
+ @patch("fastmcp.cli.run.logger")
+ @patch("subprocess.run")
+ def test_run_with_uv_logging(self, mock_run, mock_logger):
+ """Test that run_with_uv logs the command."""
+ mock_run.return_value = Mock(returncode=0)
+
+ with pytest.raises(SystemExit):
+ run_with_uv("server.py", python_version="3.11")
+
+ # Check that debug logging was called with the command
+ mock_logger.debug.assert_called()
+ call_args = mock_logger.debug.call_args[0][0]
+ assert "Running command:" in call_args
+ assert "uv run --python 3.11" in call_args