Jeremiah Lowin commited on
Commit
346a696
·
1 Parent(s): 9f94e73

Add CLI args

Browse files
docs/servers/fastmcp.mdx CHANGED
@@ -111,7 +111,12 @@ def greet(name: str) -> str:
111
 
112
  if __name__ == "__main__":
113
  # This code only runs when the file is executed directly
 
 
114
  mcp.run()
 
 
 
115
  ```
116
 
117
  This pattern is important because:
@@ -156,18 +161,47 @@ With SSE:
156
  - The server stays running until explicitly terminated
157
  - This is ideal for remote access to services
158
 
159
- You can configure the host, port, and log level when running the server:
160
 
161
  ```python
162
- # Configure with parameters
163
- mcp.run(transport="sse", host="127.0.0.1", port=8888)
 
 
 
 
 
164
 
165
- # Or run asynchronously with the same parameters
166
  import asyncio
167
- asyncio.run(mcp.run_sse_async(host="127.0.0.1", port=8888, log_level="debug"))
 
 
 
 
 
 
168
  ```
169
 
170
- These parameters override any settings defined when creating the FastMCP instance.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
  ### Using the FastMCP CLI
173
 
@@ -180,8 +214,11 @@ fastmcp run my_server.py:mcp
180
  # Explicitly specify a transport
181
  fastmcp run my_server.py:mcp --transport sse
182
 
183
- # Configure SSE transport
184
  fastmcp run my_server.py:mcp --transport sse --host 127.0.0.1 --port 8888
 
 
 
185
  ```
186
 
187
  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.
 
111
 
112
  if __name__ == "__main__":
113
  # This code only runs when the file is executed directly
114
+
115
+ # Basic run with default settings (stdio transport)
116
  mcp.run()
117
+
118
+ # Or with specific transport and parameters
119
+ # mcp.run(transport="sse", host="127.0.0.1", port=9000)
120
  ```
121
 
122
  This pattern is important because:
 
161
  - The server stays running until explicitly terminated
162
  - This is ideal for remote access to services
163
 
164
+ You can configure transport parameters directly when running the server:
165
 
166
  ```python
167
+ # Configure with specific parameters
168
+ mcp.run(
169
+ transport="sse",
170
+ host="127.0.0.1", # Override default host
171
+ port=8888, # Override default port
172
+ log_level="debug" # Set logging level
173
+ )
174
 
175
+ # You can also run asynchronously with the same parameters
176
  import asyncio
177
+ asyncio.run(
178
+ mcp.run_sse_async(
179
+ host="127.0.0.1",
180
+ port=8888,
181
+ log_level="debug"
182
+ )
183
+ )
184
  ```
185
 
186
+ 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:
187
+
188
+ - `host`: Host to bind to (default: "0.0.0.0")
189
+ - `port`: Port to bind to (default: 8000)
190
+ - `log_level`: Logging level (default: "INFO")
191
+
192
+ #### Advanced Transport Configuration
193
+
194
+ Under the hood, FastMCP's `run()` method accepts arbitrary keyword arguments (`**transport_kwargs`) that are passed to the transport-specific run methods:
195
+
196
+ ```python
197
+ # For SSE transport, kwargs are passed to run_sse_async()
198
+ mcp.run(transport="sse", **transport_kwargs)
199
+
200
+ # For stdio transport, kwargs are passed to run_stdio_async()
201
+ mcp.run(transport="stdio", **transport_kwargs)
202
+ ```
203
+
204
+ This means that any future transport-specific options will be automatically available through the same interface without requiring changes to your code.
205
 
206
  ### Using the FastMCP CLI
207
 
 
214
  # Explicitly specify a transport
215
  fastmcp run my_server.py:mcp --transport sse
216
 
217
+ # Configure SSE transport with host and port
218
  fastmcp run my_server.py:mcp --transport sse --host 127.0.0.1 --port 8888
219
+
220
+ # With log level
221
+ fastmcp run my_server.py:mcp --transport sse --log-level DEBUG
222
  ```
223
 
224
  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.
src/fastmcp/cli/cli.py CHANGED
@@ -297,6 +297,29 @@ def run(
297
  help="Transport protocol to use (stdio or sse)",
298
  ),
299
  ] = None,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  ) -> None:
301
  """Run a MCP server.
302
 
@@ -316,6 +339,9 @@ def run(
316
  "file": str(file),
317
  "server_object": server_object,
318
  "transport": transport,
 
 
 
319
  },
320
  )
321
 
@@ -329,6 +355,12 @@ def run(
329
  kwargs = {}
330
  if transport:
331
  kwargs["transport"] = transport
 
 
 
 
 
 
332
 
333
  server.run(**kwargs)
334
 
 
297
  help="Transport protocol to use (stdio or sse)",
298
  ),
299
  ] = None,
300
+ host: Annotated[
301
+ str | None,
302
+ typer.Option(
303
+ "--host",
304
+ help="Host to bind to when using sse transport (default: 0.0.0.0)",
305
+ ),
306
+ ] = None,
307
+ port: Annotated[
308
+ int | None,
309
+ typer.Option(
310
+ "--port",
311
+ "-p",
312
+ help="Port to bind to when using sse transport (default: 8000)",
313
+ ),
314
+ ] = None,
315
+ log_level: Annotated[
316
+ str | None,
317
+ typer.Option(
318
+ "--log-level",
319
+ "-l",
320
+ help="Log level for sse transport (DEBUG, INFO, WARNING, ERROR, CRITICAL)",
321
+ ),
322
+ ] = None,
323
  ) -> None:
324
  """Run a MCP server.
325
 
 
339
  "file": str(file),
340
  "server_object": server_object,
341
  "transport": transport,
342
+ "host": host,
343
+ "port": port,
344
+ "log_level": log_level,
345
  },
346
  )
347
 
 
355
  kwargs = {}
356
  if transport:
357
  kwargs["transport"] = transport
358
+ if host:
359
+ kwargs["host"] = host
360
+ if port:
361
+ kwargs["port"] = port
362
+ if log_level:
363
+ kwargs["log_level"] = log_level
364
 
365
  server.run(**kwargs)
366
 
tests/cli/test_run.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ from unittest.mock import Mock, patch
3
+
4
+ import pytest
5
+ from typer.testing import CliRunner
6
+
7
+ from fastmcp import FastMCP
8
+ from fastmcp.cli.cli import app
9
+
10
+
11
+ @pytest.fixture
12
+ def server_file(tmp_path):
13
+ """Create a simple server file for testing"""
14
+ server_path = tmp_path / "test_server.py"
15
+ server_path.write_text(
16
+ """
17
+ from fastmcp import FastMCP
18
+
19
+ mcp = FastMCP(name="TestServer")
20
+
21
+ @mcp.tool()
22
+ def hello(name: str) -> str:
23
+ return f"Hello, {name}!"
24
+
25
+ if __name__ == "__main__":
26
+ mcp.run()
27
+ """
28
+ )
29
+ return server_path
30
+
31
+
32
+ def test_cli_run_transport_kwargs():
33
+ """Test that transport_kwargs are correctly passed from CLI to server.run()"""
34
+ runner = CliRunner()
35
+
36
+ # Need to mock both the file parsing and the server import
37
+ with (
38
+ patch("fastmcp.cli.cli._parse_file_path") as mock_parse_file_path,
39
+ patch("fastmcp.cli.cli._import_server") as mock_import_server,
40
+ ):
41
+ # Make _parse_file_path return a fake path and server object
42
+ mock_parse_file_path.return_value = (Path("fake_server.py"), "mcp")
43
+
44
+ # Create a mock server with a mock run method
45
+ mock_server = FastMCP(name="MockServer")
46
+ mock_server.run = Mock()
47
+
48
+ # Make _import_server return our mock server
49
+ mock_import_server.return_value = mock_server
50
+
51
+ # Run the CLI command with transport_kwargs
52
+ result = runner.invoke(
53
+ app,
54
+ [
55
+ "run",
56
+ "fake_server.py",
57
+ "--transport",
58
+ "sse",
59
+ "--host",
60
+ "127.0.0.1",
61
+ "--port",
62
+ "9000",
63
+ "--log-level",
64
+ "DEBUG",
65
+ ],
66
+ )
67
+
68
+ # Check that the run method was called with the correct kwargs
69
+ mock_server.run.assert_called_once_with(
70
+ transport="sse",
71
+ host="127.0.0.1",
72
+ port=9000,
73
+ log_level="DEBUG",
74
+ )
75
+
76
+ # Check CLI command succeeded
77
+ assert result.exit_code == 0