diff --git a/docs/patterns/cli.mdx b/docs/patterns/cli.mdx
index b39aec2bb..12f6cbb11 100644
--- a/docs/patterns/cli.mdx
+++ b/docs/patterns/cli.mdx
@@ -18,15 +18,13 @@ fastmcp --help
| Command | Purpose | Dependency Management |
| ------- | ------- | --------------------- |
-| `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 |
+| `run` | Run a FastMCP server directly | **Supports:** Local files, factory functions, URLs, MCP configs. **Deps:** 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 | **Supports:** Local files only. **Deps:** 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 | **Supports:** Local files only. **Deps:** Creates an isolated environment; dependencies must be explicitly specified with `--with` and/or `--with-editable` |
+| `inspect` | Generate a JSON report about a FastMCP server | **Supports:** Local files only. **Deps:** Uses your current environment; you are responsible for ensuring all dependencies are available |
| `version` | Display version information | N/A |
-## Command Details
-
-### `run`
+## `fastmcp run`
Run a FastMCP server directly or proxy a remote server.
@@ -38,7 +36,7 @@ fastmcp run server.py
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
+### Options
| Option | Flag | Description |
| ------ | ---- | ----------- |
@@ -54,106 +52,125 @@ By default, this command runs the server directly in your current Python environ
| Requirements File | `--with-requirements` | Requirements file to install dependencies from |
-#### Server Specification
+### Entrypoints
-The server can be specified in four ways:
-1. `server.py` - imports the module and looks for a FastMCP object named `mcp`, `server`, or `app`. Errors if no such object is found.
-2. `server.py:custom_name` - imports and uses the specified server object
-3. `http://server-url/path` or `https://server-url/path` - connects to a remote server and creates a proxy
-4. `mcp.json` - runs servers defined in a standard MCP configuration file
+The `fastmcp run` command supports the following entrypoints:
-
-When using `fastmcp run` with a local file, it **ignores** the `if __name__ == "__main__"` block entirely. Instead, it finds your server object and calls its `run()` method directly with the transport options you specify. This means you can use `fastmcp run` to override the transport specified in your code.
-
+1. **[Inferred server instance](#inferred-server-instance)**: `server.py` - imports the module and looks for a FastMCP object named `mcp`, `server`, or `app`. Errors if no such object is found.
+2. **[Explicit server object](#explicit-server-object)**: `server.py:custom_name` - imports and uses the specified server object
+3. **[Factory function](#factory-function)**: `server.py:create_server` - calls the specified function (sync or async) to create a server instance
+4. **[Remote server proxy](#remote-server-proxy)**: `https://example.com/mcp-server` - connects to a remote server and creates a **local proxy server**
+5. **MCP configuration file**: `mcp.json` - runs servers defined in a standard MCP configuration file
-For example, if your code contains:
+
+Note: When using `fastmcp run` with a local file, it **completely ignores** the `if __name__ == "__main__"` block. This means:
+- Any setup code in `__main__` will NOT run
+- Server configuration in `__main__` is bypassed
+- `fastmcp run` finds your server object/factory and runs it with its own transport settings
-```python
-# server.py
+If you need setup code to run, use the **factory pattern** instead.
+
+
+#### Inferred Server Instance
+
+If you provide a path to a file, `fastmcp run` will load the file and look for a FastMCP server instance stored as a variable named `mcp`, `server`, or `app`. If no such object is found, it will raise an error.
+
+For example, if you have a file called `server.py` with the following content:
+
+```python server.py
from fastmcp import FastMCP
mcp = FastMCP("MyServer")
-
-@mcp.tool
-def hello(name: str) -> str:
- return f"Hello, {name}!"
-
-if __name__ == "__main__":
- # This is ignored when using `fastmcp run`!
- mcp.run(transport="stdio")
```
-You can run it with Streamable HTTP transport regardless of what's in the `__main__` block:
+You can run it with:
```bash
-fastmcp run server.py --transport http --port 8000
+fastmcp run server.py
```
-**Examples**
+#### Explicit Server Object
+
+If your server is stored as a variable with a custom name, or you want to be explicit about which server to run, you can use the following syntax to load a specific server object:
```bash
-# Run a local server with Streamable HTTP transport on a custom port
-fastmcp run server.py --transport http --port 8000
-
-# Connect to a remote server and proxy as a stdio server
-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
+fastmcp run server.py:custom_name
```
-#### Running MCP Configuration Files
+For example, if you have a file called `server.py` with the following content:
-FastMCP can run servers defined in standard MCP configuration files (typically named `mcp.json`). When you run an mcp.json file, FastMCP creates a proxy server that runs all the servers referenced in the configuration.
+```python
+from fastmcp import FastMCP
-**Example mcp.json:**
-```json
-{
- "mcpServers": {
- "fetch": {
- "command": "uvx",
- "args": [
- "mcp-server-fetch"
- ]
- },
- "filesystem": {
- "command": "npx",
- "args": [
- "-y",
- "@modelcontextprotocol/server-filesystem",
- "/Users/username/Documents"
- ]
- }
- }
-}
+my_server = FastMCP("CustomServer")
+
+@my_server.tool
+def hello() -> str:
+ return "Hello from custom server!"
```
-**Run the configuration:**
+You can run it with:
+
+```bash
+fastmcp run server.py:custom_name
+```
+
+#### Factory Function
+
+
+Since `fastmcp run` ignores the `if __name__ == "__main__"` block, you can use a factory function to run setup code before your server starts. Factory functions are called without any arguments and must return a FastMCP server instance. Both sync and async factory functions are supported.
+
+The syntax for using a factory function is the same as for an explicit server object: `fastmcp run server.py:factory_fn`. FastMCP will automatically detect that you have identified a function rather than a server Instance
+
+For example, if you have a file called `server.py` with the following content:
+
+```python
+from fastmcp import FastMCP
+
+async def create_server() -> FastMCP:
+ mcp = FastMCP("MyServer")
+
+ @mcp.tool
+ def add(x: int, y: int) -> int:
+ return x + y
+
+ # Setup that runs with fastmcp run
+ tool = await mcp.get_tool("add")
+ tool.disable()
+
+ return mcp
+```
+
+You can run it with:
+
+```bash
+fastmcp run server.py:create_server
+```
+
+#### Remote Server Proxy
+
+FastMCP run can also start a local proxy server that connects to a remote server. This is useful when you want to run a remote server locally for testing or development purposes, or to use with a client that doesn't support direct connections to remote servers.
+
+To start a local proxy, you can use the following syntax:
+
+```bash
+fastmcp run https://example.com/mcp
+```
+
+#### MCP Configuration
+
+FastMCP can also run servers defined in a standard MCP configuration file. This is useful when you want to run multiple servers from a single file, or when you want to use a client that doesn't support direct connections to remote servers.
+
+To run a MCP configuration file, you can use the following syntax:
+
```bash
-# Run with default stdio transport
fastmcp run mcp.json
-
-# Run with HTTP transport on custom port
-fastmcp run mcp.json --transport http --port 8080
-
-# Run with SSE transport
-fastmcp run mcp.json --transport sse
```
-### `dev`
+This will run all the servers defined in the file.
+
+## `fastmcp dev`
Run a MCP server with the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) for testing.
@@ -182,7 +199,7 @@ This command does not support HTTP testing. To test a server over Streamable HTT
2. Open the MCP Inspector separately and connect to your running server
-#### Options
+### Options
| Option | Flag | Description |
| ------ | ---- | ----------- |
@@ -195,6 +212,18 @@ This command does not support HTTP testing. To test a server over Streamable HTT
| Project Directory | `--project` | Run the command within the given project directory |
| Requirements File | `--with-requirements` | Requirements file to install dependencies from |
+### Entrypoints
+
+The `dev` command supports local FastMCP server files only:
+
+1. **Inferred server instance**: `server.py` - imports the module and looks for a FastMCP object named `mcp`, `server`, or `app`. Errors if no such object is found.
+2. **Explicit server object**: `server.py:custom_name` - imports and uses the specified server object
+3. **Factory function**: `server.py:create_server` - calls the specified function (sync or async) to create a server instance
+
+
+The `dev` command **only supports local files** - no URLs, remote servers, or MCP configuration files.
+
+
**Examples**
```bash
@@ -211,7 +240,7 @@ fastmcp dev server.py --with-requirements requirements.txt
fastmcp dev server.py --project /path/to/project
```
-### `install`
+## `fastmcp install`
Install a MCP server in MCP client applications. FastMCP currently supports the following clients:
@@ -242,14 +271,7 @@ Note that for security reasons, MCP clients usually run every server in a comple
**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.
-#### Server Specification
-
-The `install` command supports the same `file.py:object` notation as the `run` command:
-
-1. `server.py` - imports the module and looks for a FastMCP object named `mcp`, `server`, or `app`. Errors if no such object is found.
-2. `server.py:custom_name` - imports and uses the specified server object
-
-#### Options
+### Options
| Option | Flag | Description |
| ------ | ---- | ----------- |
@@ -262,6 +284,22 @@ The `install` command supports the same `file.py:object` notation as the `run` c
| Project Directory | `--project` | Run the command within the given project directory |
| Requirements File | `--with-requirements` | Requirements file to install dependencies from |
+### Entrypoints
+
+The `install` command supports local FastMCP server files only:
+
+1. **Inferred server instance**: `server.py` - imports the module and looks for a FastMCP object named `mcp`, `server`, or `app`. Errors if no such object is found.
+2. **Explicit server object**: `server.py:custom_name` - imports and uses the specified server object
+3. **Factory function**: `server.py:create_server` - calls the specified function (sync or async) to create a server instance
+
+
+Factory functions are particularly useful for install commands since they allow setup code to run that would otherwise be ignored when the MCP client runs your server.
+
+
+
+The `install` command **only supports local files** - no URLs, remote servers, or MCP configuration files. For remote servers, use your MCP client's native configuration.
+
+
**Examples**
```bash
@@ -299,7 +337,7 @@ fastmcp install mcp-json server.py --name "My Server" --with pandas
fastmcp install mcp-json server.py --copy
```
-#### MCP JSON Generation
+### MCP JSON Generation
The `mcp-json` subcommand generates standard MCP JSON configuration that can be used with any MCP-compatible client. This is useful when:
@@ -339,7 +377,7 @@ To use this configuration with your MCP client, you'll typically need to add it
| ------ | ---- | ----------- |
| Copy to Clipboard | `--copy` | Copy configuration to clipboard instead of printing to stdout |
-### `inspect`
+## `fastmcp inspect`
@@ -349,7 +387,25 @@ Generate a detailed JSON report about a FastMCP server, including information ab
fastmcp inspect server.py
```
-The command supports the same server specification format as `run` and `install`:
+### Options
+
+| Option | Flag | Description |
+| ------ | ---- | ----------- |
+| Output File | `--output`, `-o` | Output file path for the JSON report (default: server-info.json) |
+
+### Entrypoints
+
+The `inspect` command supports local FastMCP server files only:
+
+1. **Inferred server instance**: `server.py` - imports the module and looks for a FastMCP object named `mcp`, `server`, or `app`. Errors if no such object is found.
+2. **Explicit server object**: `server.py:custom_name` - imports and uses the specified server object
+3. **Factory function**: `server.py:create_server` - calls the specified function (sync or async) to create a server instance
+
+
+The `inspect` command **only supports local files** - no URLs, remote servers, or MCP configuration files.
+
+
+**Examples**
```bash
# Auto-detect server object
@@ -362,7 +418,7 @@ fastmcp inspect server.py:my_server
fastmcp inspect server.py --output analysis.json
```
-### `version`
+## `fastmcp version`
Display version information about FastMCP and related components.
@@ -370,7 +426,7 @@ Display version information about FastMCP and related components.
fastmcp version
```
-#### Options
+### Options
| Option | Flag | Description |
| ------ | ---- | ----------- |
diff --git a/src/fastmcp/cli/cli.py b/src/fastmcp/cli/cli.py
index 99e37107a..a5390d2b0 100644
--- a/src/fastmcp/cli/cli.py
+++ b/src/fastmcp/cli/cli.py
@@ -138,7 +138,7 @@ def version(
@app.command
-def dev(
+async def dev(
server_spec: str,
*,
with_editable: Annotated[
@@ -220,7 +220,7 @@ def dev(
try:
# Import server to get dependencies
- server: FastMCP = run_module.import_server(file, server_object)
+ server: FastMCP = await run_module.import_server(file, server_object)
if server.dependencies is not None:
with_packages = list(set(with_packages + server.dependencies))
@@ -283,7 +283,7 @@ def dev(
@app.command
-def run(
+async def run(
server_spec: str,
*server_args: str,
transport: Annotated[
@@ -414,7 +414,7 @@ def run(
else:
# Use direct import for backwards compatibility
try:
- run_module.run_command(
+ await run_module.run_command(
server_spec=server_spec,
transport=transport,
host=host,
@@ -476,7 +476,7 @@ async def inspect(
try:
# Import the server
- server = run_module.import_server(file, server_object)
+ server = await run_module.import_server(file, server_object)
# Get server information - using native async support
info = await inspect_fastmcp(server)
diff --git a/src/fastmcp/cli/install/claude_code.py b/src/fastmcp/cli/install/claude_code.py
index 3b728af26..490de9545 100644
--- a/src/fastmcp/cli/install/claude_code.py
+++ b/src/fastmcp/cli/install/claude_code.py
@@ -167,7 +167,7 @@ def install_claude_code(
return False
-def claude_code_command(
+async def claude_code_command(
server_spec: str,
*,
server_name: Annotated[
@@ -234,7 +234,7 @@ def claude_code_command(
Args:
server_spec: Python file to install, optionally with :object suffix
"""
- file, server_object, name, packages, env_dict = process_common_args(
+ file, server_object, name, packages, env_dict = await process_common_args(
server_spec, server_name, with_packages, env_vars, env_file
)
diff --git a/src/fastmcp/cli/install/claude_desktop.py b/src/fastmcp/cli/install/claude_desktop.py
index 134560a32..56f5d2bc8 100644
--- a/src/fastmcp/cli/install/claude_desktop.py
+++ b/src/fastmcp/cli/install/claude_desktop.py
@@ -140,7 +140,7 @@ def install_claude_desktop(
return False
-def claude_desktop_command(
+async def claude_desktop_command(
server_spec: str,
*,
server_name: Annotated[
@@ -207,7 +207,7 @@ def claude_desktop_command(
Args:
server_spec: Python file to install, optionally with :object suffix
"""
- file, server_object, name, with_packages, env_dict = process_common_args(
+ file, server_object, name, with_packages, env_dict = await process_common_args(
server_spec, server_name, with_packages, env_vars, env_file
)
diff --git a/src/fastmcp/cli/install/cursor.py b/src/fastmcp/cli/install/cursor.py
index 654e5e0e8..87527ce73 100644
--- a/src/fastmcp/cli/install/cursor.py
+++ b/src/fastmcp/cli/install/cursor.py
@@ -150,7 +150,7 @@ def install_cursor(
return False
-def cursor_command(
+async def cursor_command(
server_spec: str,
*,
server_name: Annotated[
@@ -217,7 +217,7 @@ def cursor_command(
Args:
server_spec: Python file to install, optionally with :object suffix
"""
- file, server_object, name, with_packages, env_dict = process_common_args(
+ file, server_object, name, with_packages, env_dict = await process_common_args(
server_spec, server_name, with_packages, env_vars, env_file
)
diff --git a/src/fastmcp/cli/install/mcp_json.py b/src/fastmcp/cli/install/mcp_json.py
index 1afaa7a3b..b5145c18d 100644
--- a/src/fastmcp/cli/install/mcp_json.py
+++ b/src/fastmcp/cli/install/mcp_json.py
@@ -113,7 +113,7 @@ def install_mcp_json(
return False
-def mcp_json_command(
+async def mcp_json_command(
server_spec: str,
*,
server_name: Annotated[
@@ -188,7 +188,7 @@ def mcp_json_command(
Args:
server_spec: Python file to install, optionally with :object suffix
"""
- file, server_object, name, packages, env_dict = process_common_args(
+ file, server_object, name, packages, env_dict = await process_common_args(
server_spec, server_name, with_packages, env_vars, env_file
)
diff --git a/src/fastmcp/cli/install/shared.py b/src/fastmcp/cli/install/shared.py
index a56bc595b..e611e3d7e 100644
--- a/src/fastmcp/cli/install/shared.py
+++ b/src/fastmcp/cli/install/shared.py
@@ -23,7 +23,7 @@ def parse_env_var(env_var: str) -> tuple[str, str]:
return key.strip(), value.strip()
-def process_common_args(
+async def process_common_args(
server_spec: str,
server_name: str | None,
with_packages: list[str],
@@ -49,7 +49,7 @@ def process_common_args(
server = None
if not name:
try:
- server = import_server(file, server_object)
+ server = await import_server(file, server_object)
name = server.name
except (ImportError, ModuleNotFoundError) as e:
logger.debug(
diff --git a/src/fastmcp/cli/run.py b/src/fastmcp/cli/run.py
index 5df022529..9a0a47971 100644
--- a/src/fastmcp/cli/run.py
+++ b/src/fastmcp/cli/run.py
@@ -1,6 +1,7 @@
"""FastMCP run command implementation with enhanced type hints."""
import importlib.util
+import inspect
import json
import re
import subprocess
@@ -58,15 +59,15 @@ def parse_file_path(server_spec: str) -> tuple[Path, str | None]:
return file_path, server_object
-def import_server(file: Path, server_object: str | None = None) -> Any:
+async def import_server(file: Path, server_or_factory: str | None = None) -> Any:
"""Import a MCP server from a file.
Args:
file: Path to the file
- server_object: Optional object name in format "module:object" or just "object"
+ server_or_factory: Optional object name in format "module:object" or just "object"
Returns:
- The server object
+ The server object (or result of calling a factory function)
"""
# Add parent directory to Python path so imports can be resolved
file_dir = str(file.parent)
@@ -86,11 +87,12 @@ def import_server(file: Path, server_object: str | None = None) -> Any:
spec.loader.exec_module(module)
# If no object specified, try common server names
- if not server_object:
- # Look for the most common server object names
+ if not server_or_factory:
+ # Look for common server instance names
for name in ["mcp", "server", "app"]:
if hasattr(module, name):
- return getattr(module, name)
+ obj = getattr(module, name)
+ return await _resolve_server_or_factory(obj, file, name)
logger.error(
f"No server object found in {file}. Please either:\n"
@@ -100,14 +102,14 @@ def import_server(file: Path, server_object: str | None = None) -> Any:
)
sys.exit(1)
- assert server_object is not None
+ assert server_or_factory is not None
# Handle module:object syntax
- if ":" in server_object:
- module_name, object_name = server_object.split(":", 1)
+ if ":" in server_or_factory:
+ module_name, object_name = server_or_factory.split(":", 1)
try:
server_module = importlib.import_module(module_name)
- server = getattr(server_module, object_name, None)
+ obj = getattr(server_module, object_name, None)
except ImportError:
logger.error(
f"Could not import module '{module_name}'",
@@ -116,16 +118,62 @@ def import_server(file: Path, server_object: str | None = None) -> Any:
sys.exit(1)
else:
# Just object name
- server = getattr(module, server_object, None)
+ obj = getattr(module, server_or_factory, None)
- if server is None:
+ if obj is None:
logger.error(
- f"Server object '{server_object}' not found",
+ f"Server object '{server_or_factory}' not found",
extra={"file": str(file)},
)
sys.exit(1)
- return server
+ return await _resolve_server_or_factory(obj, file, server_or_factory)
+
+
+async def _resolve_server_or_factory(obj: Any, file: Path, name: str) -> Any:
+ """Resolve a server object or factory function to a server instance.
+
+ Args:
+ obj: The object that might be a server or factory function
+ file: Path to the file for error messages
+ name: Name of the object for error messages
+
+ Returns:
+ A server instance
+ """
+ # Check if it's a function or coroutine function
+ if inspect.isfunction(obj) or inspect.iscoroutinefunction(obj):
+ logger.debug(f"Found factory function '{name}' in {file}")
+
+ try:
+ if inspect.iscoroutinefunction(obj):
+ # Async factory function
+ server = await obj()
+ else:
+ # Sync factory function
+ server = obj()
+
+ # Validate the result is a FastMCP server
+ if not isinstance(server, FastMCP | FastMCP1x):
+ logger.error(
+ f"Factory function '{name}' must return a FastMCP server instance, "
+ f"got {type(server).__name__}",
+ extra={"file": str(file)},
+ )
+ sys.exit(1)
+
+ logger.debug(f"Factory function '{name}' created server: {server.name}")
+ return server
+
+ except Exception as e:
+ logger.error(
+ f"Failed to call factory function '{name}': {e}",
+ extra={"file": str(file)},
+ )
+ sys.exit(1)
+
+ # Not a function, return as-is (should be a server instance)
+ return obj
def run_with_uv(
@@ -219,7 +267,7 @@ def create_client_server(url: str) -> Any:
import fastmcp
client = fastmcp.Client(url)
- server = fastmcp.FastMCP.from_client(client)
+ server = fastmcp.FastMCP.as_proxy(client)
return server
except Exception as e:
logger.error(f"Failed to create client for URL {url}: {e}")
@@ -237,14 +285,16 @@ def create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None]:
return server
-def import_server_with_args(
- file: Path, server_object: str | None = None, server_args: list[str] | None = None
+async def import_server_with_args(
+ file: Path,
+ server_or_factory: str | None = None,
+ server_args: list[str] | None = None,
) -> Any:
"""Import a server with optional command line arguments.
Args:
file: Path to the server file
- server_object: Optional server object name
+ server_or_factory: Optional server object or factory function name
server_args: Optional command line arguments to inject
Returns:
@@ -254,14 +304,14 @@ def import_server_with_args(
original_argv = sys.argv[:]
try:
sys.argv = [str(file)] + server_args
- return import_server(file, server_object)
+ return await import_server(file, server_or_factory)
finally:
sys.argv = original_argv
else:
- return import_server(file, server_object)
+ return await import_server(file, server_or_factory)
-def run_command(
+async def run_command(
server_spec: str,
transport: TransportType | None = None,
host: str | None = None,
@@ -293,8 +343,8 @@ def run_command(
server = create_mcp_config_server(Path(server_spec))
else:
# Handle file case
- file, server_object = parse_file_path(server_spec)
- server = import_server_with_args(file, server_object, server_args)
+ file, server_or_factory = parse_file_path(server_spec)
+ server = await import_server_with_args(file, server_or_factory, server_args)
logger.debug(f'Found server "{server.name}" in {file}')
# Run the server
@@ -320,7 +370,7 @@ def run_command(
kwargs["show_banner"] = False
try:
- server.run(**kwargs)
+ await server.run_async(**kwargs)
except Exception as e:
logger.error(f"Failed to run server: {e}")
sys.exit(1)
diff --git a/tests/cli/test_cursor.py b/tests/cli/test_cursor.py
index 2383f211a..de52f17e1 100644
--- a/tests/cli/test_cursor.py
+++ b/tests/cli/test_cursor.py
@@ -306,7 +306,7 @@ class TestCursorCommand:
@patch("fastmcp.cli.install.cursor.install_cursor")
@patch("fastmcp.cli.install.cursor.process_common_args")
- def test_cursor_command_basic(self, mock_process_args, mock_install):
+ async def test_cursor_command_basic(self, mock_process_args, mock_install):
"""Test basic cursor command execution."""
mock_process_args.return_value = (
Path("server.py"),
@@ -318,7 +318,7 @@ class TestCursorCommand:
mock_install.return_value = True
with patch("sys.exit") as mock_exit:
- cursor_command("server.py")
+ await cursor_command("server.py")
mock_install.assert_called_once_with(
file=Path("server.py"),
@@ -335,7 +335,7 @@ class TestCursorCommand:
@patch("fastmcp.cli.install.cursor.install_cursor")
@patch("fastmcp.cli.install.cursor.process_common_args")
- def test_cursor_command_failure(self, mock_process_args, mock_install):
+ async def test_cursor_command_failure(self, mock_process_args, mock_install):
"""Test cursor command when installation fails."""
mock_process_args.return_value = (
Path("server.py"),
@@ -347,6 +347,6 @@ class TestCursorCommand:
mock_install.return_value = False
with pytest.raises(SystemExit) as exc_info:
- cursor_command("server.py")
+ await cursor_command("server.py")
assert exc_info.value.code == 1
diff --git a/tests/cli/test_run.py b/tests/cli/test_run.py
index 6ae06f1eb..d6d2578ce 100644
--- a/tests/cli/test_run.py
+++ b/tests/cli/test_run.py
@@ -157,7 +157,7 @@ def greet(name: str) -> str:
return f"Hello, {name}!"
""")
- server = import_server(test_file)
+ server = await import_server(test_file)
assert server.name == "TestServer"
tools = await server.get_tools()
assert "greet" in tools
@@ -178,12 +178,12 @@ if __name__ == "__main__":
app.run()
""")
- server = import_server(test_file)
+ server = await import_server(test_file)
assert server.name == "MainServer"
tools = await server.get_tools()
assert "calculate" in tools
- def test_import_server_standard_names(self, tmp_path):
+ async def test_import_server_standard_names(self, tmp_path):
"""Test automatic detection of standard names (mcp, server, app)."""
# Test with 'mcp' name
mcp_file = tmp_path / "mcp_server.py"
@@ -192,7 +192,7 @@ import fastmcp
mcp = fastmcp.FastMCP("MCPServer")
""")
- server = import_server(mcp_file)
+ server = await import_server(mcp_file)
assert server.name == "MCPServer"
# Test with 'server' name
@@ -202,7 +202,7 @@ import fastmcp
server = fastmcp.FastMCP("ServerServer")
""")
- server = import_server(server_file)
+ server = await import_server(server_file)
assert server.name == "ServerServer"
# Test with 'app' name
@@ -212,7 +212,7 @@ import fastmcp
app = fastmcp.FastMCP("AppServer")
""")
- server = import_server(app_file)
+ server = await import_server(app_file)
assert server.name == "AppServer"
async def test_import_server_nonstandard_name(self, tmp_path):
@@ -228,12 +228,12 @@ def custom_tool() -> str:
return "custom"
""")
- server = import_server(test_file, "my_custom_server")
+ server = await import_server(test_file, "my_custom_server")
assert server.name == "CustomServer"
tools = await server.get_tools()
assert "custom_tool" in tools
- def test_import_server_no_standard_names_fails(self, tmp_path):
+ async def test_import_server_no_standard_names_fails(self, tmp_path):
"""Test importing server when no standard names exist fails."""
test_file = tmp_path / "server.py"
test_file.write_text("""
@@ -243,10 +243,10 @@ other_name = fastmcp.FastMCP("OtherServer")
""")
with pytest.raises(SystemExit) as exc_info:
- import_server(test_file)
+ await import_server(test_file)
assert exc_info.value.code == 1
- def test_import_server_nonexistent_object_fails(self, tmp_path):
+ async def test_import_server_nonexistent_object_fails(self, tmp_path):
"""Test importing nonexistent server object fails."""
test_file = tmp_path / "server.py"
test_file.write_text("""
@@ -256,5 +256,5 @@ mcp = fastmcp.FastMCP("TestServer")
""")
with pytest.raises(SystemExit) as exc_info:
- import_server(test_file, "nonexistent")
+ await import_server(test_file, "nonexistent")
assert exc_info.value.code == 1