import os import warnings from unittest.mock import patch import pytest from fastmcp import FastMCP from fastmcp.settings import Settings from fastmcp.utilities.tests import caplog_for_fastmcp # reset deprecation warnings for this module pytestmark = pytest.mark.filterwarnings("default::DeprecationWarning") class TestDeprecatedServerInitKwargs: """Test deprecated server initialization keyword arguments.""" def test_log_level_deprecation_warning(self): """Test that log_level raises a deprecation warning.""" with pytest.warns( DeprecationWarning, match=r"Providing `log_level` when creating a server is deprecated\. Provide it when calling `run` or as a global setting instead\.", ): server = FastMCP("TestServer", log_level="DEBUG") # Verify the setting is still applied assert server._deprecated_settings.log_level == "DEBUG" def test_debug_deprecation_warning(self): """Test that debug raises a deprecation warning.""" with pytest.warns( DeprecationWarning, match=r"Providing `debug` when creating a server is deprecated\. Provide it when calling `run` or as a global setting instead\.", ): server = FastMCP("TestServer", debug=True) # Verify the setting is still applied assert server._deprecated_settings.debug is True def test_host_deprecation_warning(self): """Test that host raises a deprecation warning.""" with pytest.warns( DeprecationWarning, match=r"Providing `host` when creating a server is deprecated\. Provide it when calling `run` or as a global setting instead\.", ): server = FastMCP("TestServer", host="0.0.0.0") # Verify the setting is still applied assert server._deprecated_settings.host == "0.0.0.0" def test_port_deprecation_warning(self): """Test that port raises a deprecation warning.""" with pytest.warns( DeprecationWarning, match=r"Providing `port` when creating a server is deprecated\. Provide it when calling `run` or as a global setting instead\.", ): server = FastMCP("TestServer", port=8080) # Verify the setting is still applied assert server._deprecated_settings.port == 8080 def test_sse_path_deprecation_warning(self): """Test that sse_path raises a deprecation warning.""" with pytest.warns( DeprecationWarning, match=r"Providing `sse_path` when creating a server is deprecated\. Provide it when calling `run` or as a global setting instead\.", ): server = FastMCP("TestServer", sse_path="/custom-sse") # Verify the setting is still applied assert server._deprecated_settings.sse_path == "/custom-sse" def test_message_path_deprecation_warning(self): """Test that message_path raises a deprecation warning.""" with pytest.warns( DeprecationWarning, match=r"Providing `message_path` when creating a server is deprecated\. Provide it when calling `run` or as a global setting instead\.", ): server = FastMCP("TestServer", message_path="/custom-message") # Verify the setting is still applied assert server._deprecated_settings.message_path == "/custom-message" def test_streamable_http_path_deprecation_warning(self): """Test that streamable_http_path raises a deprecation warning.""" with pytest.warns( DeprecationWarning, match=r"Providing `streamable_http_path` when creating a server is deprecated\. Provide it when calling `run` or as a global setting instead\.", ): server = FastMCP("TestServer", streamable_http_path="/custom-http") # Verify the setting is still applied assert server._deprecated_settings.streamable_http_path == "/custom-http" def test_json_response_deprecation_warning(self): """Test that json_response raises a deprecation warning.""" with pytest.warns( DeprecationWarning, match=r"Providing `json_response` when creating a server is deprecated\. Provide it when calling `run` or as a global setting instead\.", ): server = FastMCP("TestServer", json_response=True) # Verify the setting is still applied assert server._deprecated_settings.json_response is True def test_stateless_http_deprecation_warning(self): """Test that stateless_http raises a deprecation warning.""" with pytest.warns( DeprecationWarning, match=r"Providing `stateless_http` when creating a server is deprecated\. Provide it when calling `run` or as a global setting instead\.", ): server = FastMCP("TestServer", stateless_http=True) # Verify the setting is still applied assert server._deprecated_settings.stateless_http is True def test_multiple_deprecated_kwargs_warnings(self): """Test that multiple deprecated kwargs each raise their own warning.""" with warnings.catch_warnings(record=True) as recorded_warnings: warnings.simplefilter("always") server = FastMCP( "TestServer", log_level="INFO", debug=False, host="127.0.0.1", port=9999, sse_path="/sse/", message_path="/msg", streamable_http_path="/http", json_response=False, stateless_http=False, ) # Should have 9 deprecation warnings (one for each deprecated parameter) deprecation_warnings = [ w for w in recorded_warnings if issubclass(w.category, DeprecationWarning) ] assert len(deprecation_warnings) == 9 # Verify all expected parameters are mentioned in warnings expected_params = { "log_level", "debug", "host", "port", "sse_path", "message_path", "streamable_http_path", "json_response", "stateless_http", } mentioned_params = set() for warning in deprecation_warnings: message = str(warning.message) for param in expected_params: if f"Providing `{param}`" in message: mentioned_params.add(param) assert mentioned_params == expected_params # Verify all settings are still applied assert server._deprecated_settings.log_level == "INFO" assert server._deprecated_settings.debug is False assert server._deprecated_settings.host == "127.0.0.1" assert server._deprecated_settings.port == 9999 assert server._deprecated_settings.sse_path == "/sse/" assert server._deprecated_settings.message_path == "/msg" assert server._deprecated_settings.streamable_http_path == "/http" assert server._deprecated_settings.json_response is False assert server._deprecated_settings.stateless_http is False def test_non_deprecated_kwargs_no_warnings(self): """Test that non-deprecated kwargs don't raise warnings.""" with warnings.catch_warnings(record=True) as recorded_warnings: warnings.simplefilter("always") server = FastMCP( name="TestServer", instructions="Test instructions", on_duplicate_tools="warn", on_duplicate_resources="error", on_duplicate_prompts="replace", resource_prefix_format="path", mask_error_details=True, ) # Should have no deprecation warnings deprecation_warnings = [ w for w in recorded_warnings if issubclass(w.category, DeprecationWarning) ] assert len(deprecation_warnings) == 0 # Verify server was created successfully assert server.name == "TestServer" assert server.instructions == "Test instructions" def test_none_values_no_warnings(self): """Test that None values for deprecated kwargs don't raise warnings.""" with warnings.catch_warnings(record=True) as recorded_warnings: warnings.simplefilter("always") FastMCP( "TestServer", log_level=None, debug=None, host=None, port=None, sse_path=None, message_path=None, streamable_http_path=None, json_response=None, stateless_http=None, ) # Should have no deprecation warnings for None values deprecation_warnings = [ w for w in recorded_warnings if issubclass(w.category, DeprecationWarning) ] assert len(deprecation_warnings) == 0 def test_deprecated_settings_inheritance_from_global(self): """Test that deprecated settings inherit from global settings when not provided.""" # Mock fastmcp.settings to test inheritance with patch("fastmcp.settings") as mock_settings: mock_settings.model_dump.return_value = { "log_level": "WARNING", "debug": True, "host": "0.0.0.0", "port": 3000, "sse_path": "/events", "message_path": "/messages", "streamable_http_path": "/stream", "json_response": True, "stateless_http": True, } mock_settings.server_auth = None # Add server_auth attribute server = FastMCP("TestServer") # Verify settings are inherited from global settings assert server._deprecated_settings.log_level == "WARNING" assert server._deprecated_settings.debug is True assert server._deprecated_settings.host == "0.0.0.0" assert server._deprecated_settings.port == 3000 assert server._deprecated_settings.sse_path == "/events" assert server._deprecated_settings.message_path == "/messages" assert server._deprecated_settings.streamable_http_path == "/stream" assert server._deprecated_settings.json_response is True assert server._deprecated_settings.stateless_http is True def test_deprecated_settings_override_global(self): """Test that deprecated settings override global settings when provided.""" # Mock fastmcp.settings to test override behavior with patch("fastmcp.settings") as mock_settings: mock_settings.model_dump.return_value = { "log_level": "WARNING", "debug": True, "host": "0.0.0.0", "port": 3000, "sse_path": "/events", "message_path": "/messages", "streamable_http_path": "/stream", "json_response": True, "stateless_http": True, } mock_settings.server_auth = None # Add server_auth attribute with warnings.catch_warnings(): warnings.simplefilter("ignore") # Ignore warnings for this test server = FastMCP( "TestServer", log_level="ERROR", debug=False, host="127.0.0.1", port=8080, ) # Verify provided settings override global settings assert server._deprecated_settings.log_level == "ERROR" assert server._deprecated_settings.debug is False assert server._deprecated_settings.host == "127.0.0.1" assert server._deprecated_settings.port == 8080 # Non-overridden settings should still come from global assert server._deprecated_settings.sse_path == "/events" assert server._deprecated_settings.message_path == "/messages" assert server._deprecated_settings.streamable_http_path == "/stream" assert server._deprecated_settings.json_response is True assert server._deprecated_settings.stateless_http is True def test_stacklevel_points_to_constructor_call(self): """Test that deprecation warnings point to the FastMCP constructor call.""" with warnings.catch_warnings(record=True) as recorded_warnings: warnings.simplefilter("always") FastMCP("TestServer", log_level="DEBUG") # Should have exactly one deprecation warning deprecation_warnings = [ w for w in recorded_warnings if issubclass(w.category, DeprecationWarning) ] assert len(deprecation_warnings) == 1 # The warning should point to the server.py file where FastMCP.__init__ is called # This verifies the stacklevel is working as intended (pointing to constructor) warning = deprecation_warnings[0] assert "server.py" in warning.filename class TestDeprecatedEnvironmentVariables: """Test deprecated environment variable prefixes.""" def test_fastmcp_server_env_var_deprecation_warning(self, caplog): """Test that FASTMCP_SERVER_ environment variables emit deprecation warnings.""" env_var_name = "FASTMCP_SERVER_HOST" original_value = os.environ.get(env_var_name) try: os.environ[env_var_name] = "192.168.1.1" with caplog_for_fastmcp(caplog): settings = Settings() # Check that a warning was logged assert any( "Using `FASTMCP_SERVER_` environment variables is deprecated. Use `FASTMCP_` instead." in record.message for record in caplog.records if record.levelname == "WARNING" ) # Verify the setting is still applied assert settings.host == "192.168.1.1" finally: # Clean up environment variable if original_value is not None: os.environ[env_var_name] = original_value else: os.environ.pop(env_var_name, None) class TestDeprecatedSettingsProperty: """Test deprecated settings property access.""" def test_settings_property_deprecation_warning(self, caplog): """Test that accessing fastmcp.settings.settings logs a deprecation warning.""" from fastmcp import settings with caplog_for_fastmcp(caplog): # Access the deprecated property deprecated_settings = settings.settings # Check that a warning was logged assert any( "Using fastmcp.settings.settings is deprecated. Use fastmcp.settings instead." in record.message for record in caplog.records if record.levelname == "WARNING" ) # Verify it still returns the same settings object assert deprecated_settings is settings assert isinstance(deprecated_settings, Settings)