Skip to content

Comments

[Draft] Enhance debug settings with dual-purpose flag and granular control#2908

Open
strawgate wants to merge 2 commits intomainfrom
claude/issue-2860-20260118-1548
Open

[Draft] Enhance debug settings with dual-purpose flag and granular control#2908
strawgate wants to merge 2 commits intomainfrom
claude/issue-2860-20260118-1548

Conversation

@strawgate
Copy link
Collaborator

The existing debug setting was confusing because it only affected Starlette's debug tracebacks for HTTP/SSE transports, not FastMCP's logging. Users naturally expected FASTMCP_DEBUG to enable FastMCP debugging, leading to confusion.

This PR implements a dual approach:

The debug flag now does BOTH:

  • Sets log_level to DEBUG
  • Enables Starlette debug tracebacks for HTTP/SSE transports

New starlette_debug flag for granular control:

  • Only enables Starlette debug tracebacks
  • No effect on log level
  • Useful when you want HTTP error details without DEBUG logging

This provides both convenience (debug) and precision (starlette_debug).

Example usage:

# Enable everything for debugging
export FASTMCP_DEBUG=true

# Or granular control
export FASTMCP_LOG_LEVEL=INFO
export FASTMCP_STARLETTE_DEBUG=true

Closes #2860


Generated with Claude Code

Add starlette_debug setting for Starlette-specific debug control and enhance
the debug flag to set both log level and Starlette debug mode.

- Add starlette_debug setting for granular control over Starlette debug mode
- Update debug flag to set log_level=DEBUG and enable Starlette debug
- Update HTTP app creators to accept both debug and starlette_debug parameters
- Document both settings in server.mdx with clear usage guidelines
- Add comprehensive tests for new settings behavior
- Update existing test to reflect new debug behavior

Co-authored-by: Bill Easton <strawgate@users.noreply.github.com>
@strawgate strawgate changed the title Enhance debug settings with dual-purpose flag and granular control [Draft] Enhance debug settings with dual-purpose flag and granular control Jan 18, 2026
@marvin-context-protocol marvin-context-protocol bot added enhancement Improvement to existing functionality. For issues and smaller PR improvements. breaking change Breaks backward compatibility. Requires minor version bump. Critical for maintainer attention. server Related to FastMCP server implementation or server-side functionality. DON'T MERGE PR is not ready for merging. Used by authors to prevent premature merging. labels Jan 18, 2026
@marvin-context-protocol
Copy link
Contributor

Test Failure Analysis

Summary: The static analysis workflow failed due to an unused variable on line 47 in the new test file .

Root Cause: In the test , the variable is assigned on line 47 but never used afterward. The test creates a instance with and , which triggers logging reconfiguration as a side effect during initialization, but ruff's linter (F841) correctly identifies that the variable itself is never referenced.

Suggested Solution:

Add an underscore prefix to indicate the variable is intentionally unused:

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

This follows the Python convention of prefixing unused variables with underscore to signal they're intentionally unused (in this case, the initialization side effect is what matters).

Detailed Analysis

The ruff linter reported:

F841 Local variable `settings` is assigned to but never used
    --> tests/test_debug_settings.py:47:9
   |
45 |         """Test that enabling debug reconfigures logging."""
46 |         # Create a settings instance with debug enabled
47 |         settings = Settings(debug=True, log_enabled=True)
   |         ^^^^^^^^
48 |
49 |         # Verify logging was reconfigured

The hook auto-fixed 3 other issues (removing unused imports) but this one requires manual intervention or the flag.

Related Files
  • tests/test_debug_settings.py:47 - The test that needs the fix
  • src/fastmcp/settings.py - The Settings class with the model_post_init hook that performs the side effect

@marvin-context-protocol
Copy link
Contributor

Test Failure Analysis

Summary: The static analysis workflow failed due to an unused variable settings on line 47 in the new test file tests/test_debug_settings.py.

Root Cause: In the test test_debug_reconfigures_logging(), the variable settings is assigned on line 47 but never used afterward. The test creates a Settings instance with debug=True and log_enabled=True, which triggers logging reconfiguration as a side effect during initialization, but ruff's linter (F841) correctly identifies that the variable itself is never referenced.

Suggested Solution:

Add an underscore prefix to indicate the variable is intentionally unused:

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

This follows the Python convention of prefixing unused variables with underscore to signal they're intentionally unused (in this case, the initialization side effect is what matters).

Detailed Analysis

The ruff linter reported:

F841 Local variable `settings` is assigned to but never used
    --> tests/test_debug_settings.py:47:9
   |
45 |         """Test that enabling debug reconfigures logging."""
46 |         # Create a settings instance with debug enabled
47 |         settings = Settings(debug=True, log_enabled=True)
   |         ^^^^^^^^
48 |
49 |         # Verify logging was reconfigured

The hook auto-fixed 3 other issues (removing unused imports) but this one requires manual intervention or the --unsafe-fixes flag.

Related Files
  • tests/test_debug_settings.py:47 - The test that needs the fix
  • src/fastmcp/settings.py - The Settings class with the model_post_init hook that performs the side effect

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b2fbe1a1bb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 307 to 311
if self.log_enabled:
from fastmcp.utilities.logging import configure_logging

configure_logging(
level=self.log_level,

Choose a reason for hiding this comment

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

P1 Badge Avoid configure_logging before fastmcp.settings exists

When FASTMCP_DEBUG=true, Settings() is constructed during fastmcp/__init__.py before the module-level settings attribute is assigned. model_post_init calls configure_logging(...) here, but configure_logging dereferences fastmcp.settings (see fastmcp/utilities/logging.py:44), which is still unset at this point. That can raise AttributeError and prevent import fastmcp whenever debug is enabled via env. Consider deferring logging configuration until after fastmcp.settings is bound (e.g., keep it in __init__.py) or guard inside configure_logging against missing fastmcp.settings.

Useful? React with 👍 / 👎.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 18, 2026

Walkthrough

This PR introduces a new starlette_debug global setting separate from the existing debug setting. The starlette_debug parameter is added to HTTP transport creation functions (create_sse_app and create_streamable_http_app), and forwarded through the server's HTTP app initialization. The Settings class is refactored to include both fields as annotated attributes with validation logic that enforces log level to be DEBUG when debug is enabled, and includes a post-initialization hook for logging reconfiguration. Documentation is updated to describe both settings.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main changes: enhancing debug settings with a dual-purpose flag and introducing granular control via the new starlette_debug option.
Description check ✅ Passed The description clearly explains the problem, solution, and provides example usage. It follows the template with a concise explanation and closes the related issue.
Linked Issues check ✅ Passed Changes comprehensively address issue #2860: clarify debug options, document the distinction between FASTMCP_DEBUG and FASTMCP_LOG_LEVEL, and add the starlette_debug flag for granular control.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the dual-purpose debug flag and starlette_debug configuration. Documentation, settings, and HTTP app creation updates align with the stated objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Comment on lines 365 to 366
- **`debug`**: Global debug mode that sets log level to DEBUG and enables Starlette debug tracebacks for HTTP/SSE transports, set with `FASTMCP_DEBUG`. Provides a convenient way to enable comprehensive debugging. For granular control, use `log_level` and `starlette_debug` separately
- **`starlette_debug`**: Enable Starlette debug mode for HTTP/SSE transports only, set with `FASTMCP_STARLETTE_DEBUG`. When enabled, detailed error tracebacks are returned in HTTP responses. Only affects HTTP/SSE transports; has no effect on stdio transport
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines 291 to 313
@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,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, verify the file exists and check the relevant lines
cat -n src/fastmcp/settings.py | sed -n '285,320p'

Repository: jlowin/fastmcp

Length of output: 1531


🏁 Script executed:

# Check imports at the top of the file to see what's available
head -50 src/fastmcp/settings.py

Repository: jlowin/fastmcp

Length of output: 1237


🏁 Script executed:

# Search for the _update_log_level_for_debug method to see its exact current state
rg -A 15 "_update_log_level_for_debug" src/fastmcp/settings.py

Repository: jlowin/fastmcp

Length of output: 777


🏁 Script executed:

# Check if Any is imported and the overall type annotation coverage
rg "from typing import|import typing|from __future__ import annotations" src/fastmcp/settings.py

Repository: jlowin/fastmcp

Length of output: 151


Remove the unused info parameter and add missing type annotation for __context.

The _update_log_level_for_debug validator is a no-op and triggers Ruff ARG003 due to the unused info parameter. Additionally, model_post_init lacks a type annotation for __context, violating the full-annotations guideline. Simplify the validator by removing the unused parameter and add Any as the type for __context.

🛠️ Proposed fix
-    `@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
+    `@field_validator`("debug")
+    `@classmethod`
+    def _update_log_level_for_debug(cls, v: bool) -> bool:
+        """When debug is enabled, set log_level to DEBUG."""
+        return v
@@
-    def model_post_init(self, __context) -> None:
+    def model_post_init(self, __context: Any) -> None:
🧰 Tools
🪛 Ruff (0.14.13)

293-293: Unused class method argument: info

(ARG003)

@strawgate
Copy link
Collaborator Author

/marvin can you address feedback and fix the failing static analysis checks

@marvin-context-protocol
Copy link
Contributor

marvin-context-protocol bot commented Jan 18, 2026

Claude finished @strawgate's task in 4m 31s —— View job


Addressed Feedback and Fixed Static Analysis ✅

All issues identified in the reviews have been fixed:

  • Fix unused variable settings_settings in test file (tests/test_debug_settings.py:50)
  • Add type annotation for __context parameter in model_post_init
  • Remove unused info parameter from _update_log_level_for_debug validator
  • Fix circular import issue with configure_logging in model_post_init by checking if fastmcp.settings exists before attempting reconfiguration
  • Add production safety warnings to documentation for both debug and starlette_debug settings
  • Run static checks - all passed ✅
  • Run full test suite - 3572 passed ✅

Changes Made

src/fastmcp/settings.py:

  • Removed unused info parameter from _update_log_level_for_debug validator
  • Added Any type annotation for __context parameter in model_post_init
  • Fixed circular import by checking hasattr(fastmcp, 'settings') before calling configure_logging() to avoid AttributeError during module initialization

tests/test_debug_settings.py:

  • Renamed settings_settings in test_debug_reconfigures_logging() to indicate intentionally unused variable

docs/servers/server.mdx:

  • Added production safety warnings to both debug and starlette_debug settings explaining they should only be enabled in development

All static analysis checks now pass and the full test suite runs successfully.

--- |

- Fix unused variable in test (settings → _settings)
- Add type annotation for __context parameter
- Remove unused info parameter from validator
- Fix circular import with configure_logging
- Add production safety warnings to documentation

Co-authored-by: Bill Easton <strawgate@users.noreply.github.com>
@jlowin
Copy link
Member

jlowin commented Jan 18, 2026

I think the starlette debug thing is an accident (or LLM enthusiasm) -- I personally mainly use this feature just to have a way of detecting if I'm in a debugging session, not for any FastMCP functionality.

@strawgate
Copy link
Collaborator Author

@jlowin should we just remove the debug flag then? Or what do you think we should do here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Breaks backward compatibility. Requires minor version bump. Critical for maintainer attention. DON'T MERGE PR is not ready for merging. Used by authors to prevent premature merging. enhancement Improvement to existing functionality. For issues and smaller PR improvements. server Related to FastMCP server implementation or server-side functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Clarify debug options

2 participants