Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/development/upgrade-guide.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,15 @@ This applies to all auth providers: `GitHubProvider`, `GoogleProvider`, `AzurePr

The `FastMCPSettings` class has also been simplified - it no longer includes auth-related settings that were previously loaded from environment variables.

### Server Banner Environment Variable Renamed

The environment variable for controlling the server banner has been renamed:

- **Before:** `FASTMCP_SHOW_CLI_BANNER`
- **After:** `FASTMCP_SHOW_SERVER_BANNER`

This change reflects that the setting now applies to all server startup methods, not just the CLI. The banner is now suppressed when running `python server.py` directly, not just when using `fastmcp run`.
Comment on lines +182 to +189
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify the banner behavior description.

Line 189 states "The banner is now suppressed when running python server.py directly," which is imprecise. The setting controls whether the banner is shown or hidden—it's not necessarily suppressed. When FASTMCP_SHOW_SERVER_BANNER=true (the default), the banner displays for all startup methods including direct Python runs.

Consider revising to something like: "This change reflects that the setting now controls banner display for all server startup methods (both CLI and direct Python execution), not just CLI invocations."

🔎 Suggested revision
-The environment variable for controlling the server banner has been renamed:
-
-- **Before:** `FASTMCP_SHOW_CLI_BANNER`
-- **After:** `FASTMCP_SHOW_SERVER_BANNER`
-
-This change reflects that the setting now applies to all server startup methods, not just the CLI. The banner is now suppressed when running `python server.py` directly, not just when using `fastmcp run`.
+The environment variable for controlling the server banner has been renamed:
+
+- **Before:** `FASTMCP_SHOW_CLI_BANNER`
+- **After:** `FASTMCP_SHOW_SERVER_BANNER`
+
+This change reflects that the setting now controls banner display for all server startup methods (both CLI and direct Python execution), not just CLI invocations. Set `FASTMCP_SHOW_SERVER_BANNER=false` to suppress the banner across all startup methods.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Server Banner Environment Variable Renamed
The environment variable for controlling the server banner has been renamed:
- **Before:** `FASTMCP_SHOW_CLI_BANNER`
- **After:** `FASTMCP_SHOW_SERVER_BANNER`
This change reflects that the setting now applies to all server startup methods, not just the CLI. The banner is now suppressed when running `python server.py` directly, not just when using `fastmcp run`.
### Server Banner Environment Variable Renamed
The environment variable for controlling the server banner has been renamed:
- **Before:** `FASTMCP_SHOW_CLI_BANNER`
- **After:** `FASTMCP_SHOW_SERVER_BANNER`
This change reflects that the setting now controls banner display for all server startup methods (both CLI and direct Python execution), not just CLI invocations. Set `FASTMCP_SHOW_SERVER_BANNER=false` to suppress the banner across all startup methods.


## v2.14.0

### OpenAPI Parser Promotion
Expand Down
6 changes: 4 additions & 2 deletions src/fastmcp/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,8 +423,10 @@ async def run(
final_log_level = log_level or config.deployment.log_level
final_server_args = server_args or config.deployment.args
# Use CLI override if provided, otherwise use settings
# no_banner CLI flag overrides the show_cli_banner setting
final_no_banner = no_banner if no_banner else not fastmcp.settings.show_cli_banner
# no_banner CLI flag overrides the show_server_banner setting
final_no_banner = (
no_banner if no_banner else not fastmcp.settings.show_server_banner
)

logger.debug(
"Running server or client",
Expand Down
10 changes: 8 additions & 2 deletions src/fastmcp/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,14 +556,18 @@ async def _lifespan_manager(self) -> AsyncIterator[None]:
async def run_async(
self,
transport: Transport | None = None,
show_banner: bool = True,
show_banner: bool | None = None,
**transport_kwargs: Any,
) -> None:
"""Run the FastMCP server asynchronously.

Args:
transport: Transport protocol to use ("stdio", "sse", or "streamable-http")
show_banner: Whether to display the server banner. If None, uses the
FASTMCP_SHOW_SERVER_BANNER setting (default: True).
"""
if show_banner is None:
show_banner = fastmcp.settings.show_server_banner
if transport is None:
transport = "stdio"
if transport not in {"stdio", "http", "sse", "streamable-http"}:
Expand All @@ -586,13 +590,15 @@ async def run_async(
def run(
self,
transport: Transport | None = None,
show_banner: bool = True,
show_banner: bool | None = None,
**transport_kwargs: Any,
) -> None:
"""Run the FastMCP server. Note this is a synchronous function.

Args:
transport: Transport protocol to use ("http", "stdio", "sse", or "streamable-http")
show_banner: Whether to display the server banner. If None, uses the
FASTMCP_SHOW_SERVER_BANNER setting (default: True).
"""

anyio.run(
Expand Down
9 changes: 5 additions & 4 deletions src/fastmcp/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,14 +320,15 @@ def normalize_log_level(cls, v):
),
] = False

show_cli_banner: Annotated[
show_server_banner: Annotated[
bool,
Field(
description=inspect.cleandoc(
"""
If True, the server banner will be displayed when running the server via CLI.
This setting can be overridden by the --no-banner CLI flag.
Set to False via FASTMCP_SHOW_CLI_BANNER=false to suppress the banner.
If True, the server banner will be displayed when running the server.
This setting can be overridden by the --no-banner CLI flag or by
passing show_banner=False to server.run().
Set to False via FASTMCP_SHOW_SERVER_BANNER=false to suppress the banner.
"""
),
),
Expand Down
14 changes: 7 additions & 7 deletions tests/cli/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,28 +374,28 @@ def test_run_command_parsing_project_and_skip_source(self):
assert bound.arguments["project"] == Path("./test-env")
assert bound.arguments["skip_source"] is True

def test_show_cli_banner_setting(self):
"""Test that show_cli_banner setting works with environment variable."""
def test_show_server_banner_setting(self):
"""Test that show_server_banner setting works with environment variable."""
import os
from unittest import mock

from fastmcp.settings import Settings

# Test default (banner shown)
settings = Settings()
assert settings.show_cli_banner is True
assert settings.show_server_banner is True

# Test with env var set to false (banner hidden)
with mock.patch.dict(os.environ, {"FASTMCP_SHOW_CLI_BANNER": "false"}):
with mock.patch.dict(os.environ, {"FASTMCP_SHOW_SERVER_BANNER": "false"}):
settings = Settings()
assert settings.show_cli_banner is False
assert settings.show_server_banner is False

# Test CLI precedence logic (simulated)
with mock.patch.dict(os.environ, {"FASTMCP_SHOW_CLI_BANNER": "true"}):
with mock.patch.dict(os.environ, {"FASTMCP_SHOW_SERVER_BANNER": "true"}):
settings = Settings()
# CLI --no-banner flag would override
cli_no_banner = True
final = cli_no_banner if cli_no_banner else not settings.show_cli_banner
final = cli_no_banner if cli_no_banner else not settings.show_server_banner
assert final is True # Banner suppressed by CLI flag


Expand Down
50 changes: 50 additions & 0 deletions tests/server/test_server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os
from pathlib import Path
from tempfile import TemporaryDirectory
from textwrap import dedent
from unittest import mock

from mcp.types import TextContent, TextResourceContents

Expand Down Expand Up @@ -417,3 +419,51 @@ def sample_tool(x: int) -> int:
tool = next(t for t in tools if t.name == "sample_tool")
assert tool.meta is not None
assert set(tool.meta["_fastmcp"]["tags"]) == {"test-tag"}


class TestShowServerBannerSetting:
"""Test that show_server_banner setting controls banner display."""

async def test_show_banner_defaults_to_setting_true(self):
"""Test that show_banner=None uses the setting (default True)."""
mcp = FastMCP()

with mock.patch.object(mcp, "run_stdio_async") as mock_run:
mock_run.return_value = None
await mcp.run_async(transport="stdio")
mock_run.assert_called_once()
assert mock_run.call_args.kwargs["show_banner"] is True

async def test_show_banner_respects_setting_false(self):
"""Test that show_banner=None uses the setting when False."""
mcp = FastMCP()

with mock.patch.dict(os.environ, {"FASTMCP_SHOW_SERVER_BANNER": "false"}):
with temporary_settings(show_server_banner=False):
with mock.patch.object(mcp, "run_stdio_async") as mock_run:
mock_run.return_value = None
await mcp.run_async(transport="stdio")
mock_run.assert_called_once()
assert mock_run.call_args.kwargs["show_banner"] is False

async def test_show_banner_explicit_true_overrides_setting(self):
"""Test that explicit show_banner=True overrides False setting."""
mcp = FastMCP()

with temporary_settings(show_server_banner=False):
with mock.patch.object(mcp, "run_stdio_async") as mock_run:
mock_run.return_value = None
await mcp.run_async(transport="stdio", show_banner=True)
mock_run.assert_called_once()
assert mock_run.call_args.kwargs["show_banner"] is True

async def test_show_banner_explicit_false_overrides_setting(self):
"""Test that explicit show_banner=False overrides True setting."""
mcp = FastMCP()

with temporary_settings(show_server_banner=True):
with mock.patch.object(mcp, "run_stdio_async") as mock_run:
mock_run.return_value = None
await mcp.run_async(transport="stdio", show_banner=False)
mock_run.assert_called_once()
assert mock_run.call_args.kwargs["show_banner"] is False
Loading