diff --git a/README.md b/README.md index 034b8d4b5..b281b5513 100644 --- a/README.md +++ b/README.md @@ -387,6 +387,8 @@ python server.py mcp run server.py ``` +Note that `mcp run` or `mcp dev` only supports server using FastMCP and not the low-level server variant. + ### Streamable HTTP Transport > **Note**: Streamable HTTP transport is superseding SSE transport for production deployments. @@ -694,6 +696,8 @@ if __name__ == "__main__": asyncio.run(run()) ``` +Caution: The `mcp run` and `mcp dev` tool doesn't support low-level server. + ### Writing MCP Clients The SDK provides a high-level client interface for connecting to MCP servers using various [transports](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports): diff --git a/src/mcp/cli/cli.py b/src/mcp/cli/cli.py index cb0830600..b2632f1d9 100644 --- a/src/mcp/cli/cli.py +++ b/src/mcp/cli/cli.py @@ -6,7 +6,10 @@ import subprocess import sys from pathlib import Path -from typing import Annotated +from typing import Annotated, Any + +from mcp.server import FastMCP +from mcp.server import Server as LowLevelServer try: import typer @@ -141,17 +144,48 @@ def _import_server(file: Path, server_object: str | None = None): module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) + def _check_server_object(server_object: Any, object_name: str): + """Helper function to check that the server object is supported + + Args: + server_object: The server object to check. + + Returns: + True if it's supported. + """ + if not isinstance(server_object, FastMCP): + logger.error( + f"The server object {object_name} is of type " + f"{type(server_object)} (expecting {FastMCP})." + ) + if isinstance(server_object, LowLevelServer): + logger.warning( + "Note that only FastMCP server is supported. Low level " + "Server class is not yet supported." + ) + return False + return True + # If no object specified, try common server names if not server_object: # Look for the most common server object names for name in ["mcp", "server", "app"]: if hasattr(module, name): + if not _check_server_object(getattr(module, name), f"{file}:{name}"): + logger.error( + f"Ignoring object '{file}:{name}' as it's not a valid " + "server object" + ) + continue return getattr(module, name) logger.error( f"No server object found in {file}. Please either:\n" "1. Use a standard variable name (mcp, server, or app)\n" - "2. Specify the object name with file:object syntax", + "2. Specify the object name with file:object syntax" + "3. If the server creates the FastMCP object within main() " + " or another function, refactor the FastMCP object to be a " + " global variable named mcp, server, or app.", extra={"file": str(file)}, ) sys.exit(1) @@ -179,6 +213,9 @@ def _import_server(file: Path, server_object: str | None = None): ) sys.exit(1) + if not _check_server_object(server, server_object): + sys.exit(1) + return server