mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-21 13:04:18 +02:00
Merge pull request #161 from jlowin/transport-kwargs
Add transport kwargs for mcp.run() and fastmcp run
This commit is contained in:
commit
059c0889fd
5 changed files with 163 additions and 13 deletions
|
|
@ -111,7 +111,12 @@ def greet(name: str) -> str:
|
|||
|
||||
if __name__ == "__main__":
|
||||
# This code only runs when the file is executed directly
|
||||
|
||||
# Basic run with default settings (stdio transport)
|
||||
mcp.run()
|
||||
|
||||
# Or with specific transport and parameters
|
||||
# mcp.run(transport="sse", host="127.0.0.1", port=9000)
|
||||
```
|
||||
|
||||
This pattern is important because:
|
||||
|
|
@ -156,18 +161,47 @@ With SSE:
|
|||
- The server stays running until explicitly terminated
|
||||
- This is ideal for remote access to services
|
||||
|
||||
You can configure the host, port, and log level when running the server:
|
||||
You can configure transport parameters directly when running the server:
|
||||
|
||||
```python
|
||||
# Configure with parameters
|
||||
mcp.run(transport="sse", host="127.0.0.1", port=8888)
|
||||
# Configure with specific parameters
|
||||
mcp.run(
|
||||
transport="sse",
|
||||
host="127.0.0.1", # Override default host
|
||||
port=8888, # Override default port
|
||||
log_level="debug" # Set logging level
|
||||
)
|
||||
|
||||
# Or run asynchronously with the same parameters
|
||||
# You can also run asynchronously with the same parameters
|
||||
import asyncio
|
||||
asyncio.run(mcp.run_sse_async(host="127.0.0.1", port=8888, log_level="debug"))
|
||||
asyncio.run(
|
||||
mcp.run_sse_async(
|
||||
host="127.0.0.1",
|
||||
port=8888,
|
||||
log_level="debug"
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
These parameters override any settings defined when creating the FastMCP instance.
|
||||
Transport parameters passed to `run()` or `run_sse_async()` override any settings defined when creating the FastMCP instance. The most common parameters for SSE transport are:
|
||||
|
||||
- `host`: Host to bind to (default: "0.0.0.0")
|
||||
- `port`: Port to bind to (default: 8000)
|
||||
- `log_level`: Logging level (default: "INFO")
|
||||
|
||||
#### Advanced Transport Configuration
|
||||
|
||||
Under the hood, FastMCP's `run()` method accepts arbitrary keyword arguments (`**transport_kwargs`) that are passed to the transport-specific run methods:
|
||||
|
||||
```python
|
||||
# For SSE transport, kwargs are passed to run_sse_async()
|
||||
mcp.run(transport="sse", **transport_kwargs)
|
||||
|
||||
# For stdio transport, kwargs are passed to run_stdio_async()
|
||||
mcp.run(transport="stdio", **transport_kwargs)
|
||||
```
|
||||
|
||||
This means that any future transport-specific options will be automatically available through the same interface without requiring changes to your code.
|
||||
|
||||
### Using the FastMCP CLI
|
||||
|
||||
|
|
@ -180,8 +214,11 @@ fastmcp run my_server.py:mcp
|
|||
# Explicitly specify a transport
|
||||
fastmcp run my_server.py:mcp --transport sse
|
||||
|
||||
# Configure SSE transport
|
||||
# Configure SSE transport with host and port
|
||||
fastmcp run my_server.py:mcp --transport sse --host 127.0.0.1 --port 8888
|
||||
|
||||
# With log level
|
||||
fastmcp run my_server.py:mcp --transport sse --log-level DEBUG
|
||||
```
|
||||
|
||||
The CLI can dynamically find and run FastMCP server objects in your files, but including the `if __name__ == "__main__":` block ensures compatibility with all clients.
|
||||
|
|
|
|||
|
|
@ -297,6 +297,29 @@ def run(
|
|||
help="Transport protocol to use (stdio or sse)",
|
||||
),
|
||||
] = None,
|
||||
host: Annotated[
|
||||
str | None,
|
||||
typer.Option(
|
||||
"--host",
|
||||
help="Host to bind to when using sse transport (default: 0.0.0.0)",
|
||||
),
|
||||
] = None,
|
||||
port: Annotated[
|
||||
int | None,
|
||||
typer.Option(
|
||||
"--port",
|
||||
"-p",
|
||||
help="Port to bind to when using sse transport (default: 8000)",
|
||||
),
|
||||
] = None,
|
||||
log_level: Annotated[
|
||||
str | None,
|
||||
typer.Option(
|
||||
"--log-level",
|
||||
"-l",
|
||||
help="Log level for sse transport (DEBUG, INFO, WARNING, ERROR, CRITICAL)",
|
||||
),
|
||||
] = None,
|
||||
) -> None:
|
||||
"""Run a MCP server.
|
||||
|
||||
|
|
@ -316,6 +339,9 @@ def run(
|
|||
"file": str(file),
|
||||
"server_object": server_object,
|
||||
"transport": transport,
|
||||
"host": host,
|
||||
"port": port,
|
||||
"log_level": log_level,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -329,6 +355,12 @@ def run(
|
|||
kwargs = {}
|
||||
if transport:
|
||||
kwargs["transport"] = transport
|
||||
if host:
|
||||
kwargs["host"] = host
|
||||
if port:
|
||||
kwargs["port"] = port
|
||||
if log_level:
|
||||
kwargs["log_level"] = log_level
|
||||
|
||||
server.run(**kwargs)
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,9 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
def instructions(self) -> str | None:
|
||||
return self._mcp_server.instructions
|
||||
|
||||
async def run_async(self, transport: Literal["stdio", "sse"] | None = None) -> None:
|
||||
async def run_async(
|
||||
self, transport: Literal["stdio", "sse"] | None = None, **transport_kwargs: Any
|
||||
) -> None:
|
||||
"""Run the FastMCP server asynchronously.
|
||||
|
||||
Args:
|
||||
|
|
@ -158,18 +160,20 @@ class FastMCP(Generic[LifespanResultT]):
|
|||
raise ValueError(f"Unknown transport: {transport}")
|
||||
|
||||
if transport == "stdio":
|
||||
await self.run_stdio_async()
|
||||
await self.run_stdio_async(**transport_kwargs)
|
||||
else: # transport == "sse"
|
||||
await self.run_sse_async()
|
||||
await self.run_sse_async(**transport_kwargs)
|
||||
|
||||
def run(self, transport: Literal["stdio", "sse"] | None = None) -> None:
|
||||
def run(
|
||||
self, transport: Literal["stdio", "sse"] | None = None, **transport_kwargs: Any
|
||||
) -> None:
|
||||
"""Run the FastMCP server. Note this is a synchronous function.
|
||||
|
||||
Args:
|
||||
transport: Transport protocol to use ("stdio" or "sse")
|
||||
"""
|
||||
logger.info(f'Starting server "{self.name}"...')
|
||||
anyio.run(self.run_async, transport)
|
||||
anyio.run(self.run_async, transport, **transport_kwargs)
|
||||
|
||||
def _setup_handlers(self) -> None:
|
||||
"""Set up core MCP protocol handlers."""
|
||||
|
|
|
|||
77
tests/cli/test_run.py
Normal file
77
tests/cli/test_run.py
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.cli.cli import app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def server_file(tmp_path):
|
||||
"""Create a simple server file for testing"""
|
||||
server_path = tmp_path / "test_server.py"
|
||||
server_path.write_text(
|
||||
"""
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP(name="TestServer")
|
||||
|
||||
@mcp.tool()
|
||||
def hello(name: str) -> str:
|
||||
return f"Hello, {name}!"
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
"""
|
||||
)
|
||||
return server_path
|
||||
|
||||
|
||||
def test_cli_run_transport_kwargs():
|
||||
"""Test that transport_kwargs are correctly passed from CLI to server.run()"""
|
||||
runner = CliRunner()
|
||||
|
||||
# Need to mock both the file parsing and the server import
|
||||
with (
|
||||
patch("fastmcp.cli.cli._parse_file_path") as mock_parse_file_path,
|
||||
patch("fastmcp.cli.cli._import_server") as mock_import_server,
|
||||
):
|
||||
# Make _parse_file_path return a fake path and server object
|
||||
mock_parse_file_path.return_value = (Path("fake_server.py"), "mcp")
|
||||
|
||||
# Create a mock server with a mock run method
|
||||
mock_server = FastMCP(name="MockServer")
|
||||
mock_server.run = Mock()
|
||||
|
||||
# Make _import_server return our mock server
|
||||
mock_import_server.return_value = mock_server
|
||||
|
||||
# Run the CLI command with transport_kwargs
|
||||
result = runner.invoke(
|
||||
app,
|
||||
[
|
||||
"run",
|
||||
"fake_server.py",
|
||||
"--transport",
|
||||
"sse",
|
||||
"--host",
|
||||
"127.0.0.1",
|
||||
"--port",
|
||||
"9000",
|
||||
"--log-level",
|
||||
"DEBUG",
|
||||
],
|
||||
)
|
||||
|
||||
# Check that the run method was called with the correct kwargs
|
||||
mock_server.run.assert_called_once_with(
|
||||
transport="sse",
|
||||
host="127.0.0.1",
|
||||
port=9000,
|
||||
log_level="DEBUG",
|
||||
)
|
||||
|
||||
# Check CLI command succeeded
|
||||
assert result.exit_code == 0
|
||||
2
uv.lock
generated
2
uv.lock
generated
|
|
@ -254,7 +254,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "fastmcp"
|
||||
version = "2.1.2.dev2+c0de75e"
|
||||
version = "2.1.2.dev9+55f3666"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "dotenv" },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue