-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[Draft] Enhance debug settings with dual-purpose flag and granular control #2908
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -260,7 +260,57 @@ def normalize_log_level(cls, v): | |
| sse_path: str = "/sse" | ||
| message_path: str = "/messages/" | ||
| streamable_http_path: str = "/mcp" | ||
| debug: bool = False | ||
|
|
||
| debug: Annotated[ | ||
| bool, | ||
| Field( | ||
| description=inspect.cleandoc( | ||
| """ | ||
| Global debug mode. When enabled, sets log level to DEBUG and enables | ||
| Starlette debug tracebacks for HTTP/SSE transports. This provides a | ||
| convenient way to enable comprehensive debugging. For granular control, | ||
| use log_level and starlette_debug separately. | ||
| """ | ||
| ) | ||
| ), | ||
| ] = False | ||
|
|
||
| starlette_debug: Annotated[ | ||
| bool, | ||
| Field( | ||
| description=inspect.cleandoc( | ||
| """ | ||
| Enable Starlette debug mode for HTTP/SSE transports. When enabled, | ||
| detailed error tracebacks will be returned in HTTP responses. Only | ||
| affects HTTP/SSE transports; has no effect on stdio transport. | ||
| """ | ||
| ) | ||
| ), | ||
| ] = False | ||
|
|
||
| @field_validator("debug") | ||
| @classmethod | ||
| def _update_log_level_for_debug(cls, v: bool, info) -> bool: | ||
| """When debug is enabled, set log_level to DEBUG.""" | ||
| if v: | ||
| # When debug is True, we need to ensure log_level is set to DEBUG | ||
| # This is checked in model_post_init | ||
| pass | ||
| return v | ||
|
|
||
| def model_post_init(self, __context) -> None: | ||
| """Post-initialization hook to handle debug mode.""" | ||
| if self.debug and self.log_level != "DEBUG": | ||
| # When debug is enabled, force log_level to DEBUG | ||
| self.log_level = "DEBUG" | ||
| # Reconfigure logging if it's enabled | ||
| if self.log_enabled: | ||
| from fastmcp.utilities.logging import configure_logging | ||
|
|
||
| configure_logging( | ||
| level=self.log_level, | ||
|
||
| enable_rich_tracebacks=self.enable_rich_tracebacks, | ||
| ) | ||
|
||
|
|
||
| # error handling | ||
| mask_error_details: Annotated[ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| """Tests for debug and starlette_debug settings.""" | ||
|
|
||
| import logging | ||
|
|
||
| import pytest | ||
|
|
||
| from fastmcp import settings as global_settings | ||
| from fastmcp.settings import Settings | ||
| from fastmcp.utilities.logging import get_logger | ||
|
|
||
|
|
||
| class TestDebugSettings: | ||
| """Test debug and starlette_debug settings behavior.""" | ||
|
|
||
| def test_debug_sets_log_level(self): | ||
| """Test that enabling debug sets log_level to DEBUG.""" | ||
| settings = Settings(debug=True, log_enabled=False) | ||
| assert settings.log_level == "DEBUG" | ||
|
|
||
| def test_debug_false_preserves_log_level(self): | ||
| """Test that debug=False doesn't change log_level.""" | ||
| settings = Settings(debug=False, log_level="INFO", log_enabled=False) | ||
| assert settings.log_level == "INFO" | ||
|
|
||
| def test_debug_with_explicit_log_level(self): | ||
| """Test that debug=True overrides explicit log_level.""" | ||
| # When debug is enabled, it forces log_level to DEBUG | ||
| settings = Settings(debug=True, log_level="WARNING", log_enabled=False) | ||
| assert settings.log_level == "DEBUG" | ||
|
|
||
| def test_starlette_debug_independent(self, monkeypatch): | ||
| """Test that starlette_debug works independently.""" | ||
| # Clear any env vars that might affect the test | ||
| monkeypatch.delenv("FASTMCP_LOG_LEVEL", raising=False) | ||
| settings = Settings(starlette_debug=True, log_enabled=False) | ||
| assert settings.starlette_debug is True | ||
| # log_level should not be affected (defaults to INFO) | ||
| assert settings.log_level == "INFO" | ||
|
|
||
| def test_both_debug_and_starlette_debug(self): | ||
| """Test that both settings can be enabled together.""" | ||
| settings = Settings(debug=True, starlette_debug=True, log_enabled=False) | ||
| assert settings.debug is True | ||
| assert settings.starlette_debug is True | ||
| assert settings.log_level == "DEBUG" | ||
|
|
||
| def test_debug_reconfigures_logging(self): | ||
| """Test that enabling debug reconfigures logging.""" | ||
| # Create a settings instance with debug enabled | ||
| settings = Settings(debug=True, log_enabled=True) | ||
|
|
||
| # Verify logging was reconfigured | ||
| logger = get_logger("test") | ||
| assert logger.getEffectiveLevel() == logging.DEBUG | ||
|
|
||
| def test_debug_respects_log_enabled(self): | ||
| """Test that debug respects log_enabled setting.""" | ||
| # When log_enabled is False, logging should not be reconfigured | ||
| settings = Settings(debug=True, log_enabled=False) | ||
| assert settings.log_level == "DEBUG" | ||
| # Logger should not be reconfigured, but we can't easily test this | ||
| # without side effects | ||
|
|
||
| def test_starlette_debug_default_false(self): | ||
| """Test that starlette_debug defaults to False.""" | ||
| settings = Settings(log_enabled=False) | ||
| assert settings.starlette_debug is False | ||
|
|
||
| def test_debug_default_false(self): | ||
| """Test that debug defaults to False.""" | ||
| settings = Settings(log_enabled=False) | ||
| assert settings.debug is False | ||
|
|
||
| def test_env_var_debug(self, monkeypatch): | ||
| """Test that FASTMCP_DEBUG environment variable works.""" | ||
| monkeypatch.setenv("FASTMCP_DEBUG", "true") | ||
| settings = Settings(log_enabled=False) | ||
| assert settings.debug is True | ||
| assert settings.log_level == "DEBUG" | ||
|
|
||
| def test_env_var_starlette_debug(self, monkeypatch): | ||
| """Test that FASTMCP_STARLETTE_DEBUG environment variable works.""" | ||
| # Clear any env vars that might affect the test | ||
| monkeypatch.delenv("FASTMCP_LOG_LEVEL", raising=False) | ||
| monkeypatch.setenv("FASTMCP_STARLETTE_DEBUG", "true") | ||
| settings = Settings(log_enabled=False) | ||
| assert settings.starlette_debug is True | ||
| # log_level should not be affected (defaults to INFO) | ||
| assert settings.log_level == "INFO" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a production-safety warning for debug tracebacks.
These options expose detailed tracebacks in HTTP responses; please add a clear warning that you should only enable them in development to avoid leaking internals. As per coding guidelines.