Skip to content

Conversation

@jlowin
Copy link
Owner

@jlowin jlowin commented Sep 27, 2025

@strawgate curious if you 👍 . We've had considerably more requests to disable this than enable it :)

@marvin-context-protocol marvin-context-protocol bot added enhancement Improvement to existing functionality. For issues and smaller PR improvements. server Related to FastMCP server implementation or server-side functionality. labels Sep 27, 2025
@strawgate
Copy link
Collaborator

There's a configuration for rich tracebacks that I use in another project that I've found is nice, basically limiting to 3 levels for the traceback and excluding FastMCP and mcp modules from the tracebacks.

I'm fine with just disabling outright but we could try just making them less bad first?

@strawgate
Copy link
Collaborator

Something like

handler.tracebacks_max_frames = 3
handler.tracebacks_suppress = [pydantic, fastmcp,mcp]

@jlowin
Copy link
Owner Author

jlowin commented Sep 28, 2025

Oh interesting, I wasn't aware -- happy to do that. I think the main reason for the dislike is that when they are narrow they are totally unusable (which I think you're wrestling with elsewhere for console width)

@strawgate
Copy link
Collaborator

I was planning on looking at console width tomorrow and will let you know if I find anything

@strawgate
Copy link
Collaborator

I made a quick project and deployed it to FastMCP cloud https://github.com/strawgate/py-mcp-terminal-size

And didn't discover anything revealing

Will play with it more today

@strawgate
Copy link
Collaborator

Screenshot 2025-09-29 at 8 19 05 AM

[09/29/25 13:17:27] INFO Causing traceback main.py:36 <-- this line is 80 characters
INFO <console width=80 None> main.py:39 <-- This line is 60 characters
╭─ Traceback (most recent cal─╮ <-- The traceback is 30 characters

@strawgate
Copy link
Collaborator

strawgate commented Sep 29, 2025

Basically I think what's happening is Rich sees the console as 80 characters wide
ERROR ................................tool_manager.py:233

It's a little misleading because fastmcp.cloud is stripping whitespace from the log line -- but here's what the actual layout really looks like (with a much wider width):

[09/29/25 07:57:33] INFO     Causing traceback                                                                                     main.py:36
                    INFO     <RichHandler (NOTSET)>                                                                                main.py:38
                    INFO     <console width=141 ColorSystem.TRUECOLOR>                                                             main.py:39
                    ERROR    Error calling tool 'cause_traceback'                                                         tool_manager.py:233
                             ╭─────────────────────────── Traceback (most recent call last) ────────────────────────────╮                    
                             │ /Users/bill.easton/repos/py-mcp-terminal-size/.venv/lib/python3.13/site-packages/fastmcp │                    
                             │ /tools/tool_manager.py:224 in call_tool                                                  │                    
                             │                                                                                          │                    
                             │   221 │   │   │   │   raise NotFoundError(f"Tool {key!r} not found")                     │                    
                             │   222 │   │   │                                                                          │                    
                             │   223 │   │   │   try:                                                                   │                    
                             │ ❱ 224 │   │   │   │   return await tool.run(arguments)                                   │                    
                             │   225 │   │   │                                                                          │                    
                             │   226 │   │   │   # raise ToolErrors as-is                                               │                    

The timestamp (which is blank if it's the same as the last one) plus LEVEL plus File plus Line Number leaves ~30 characters for the traceback if the console is limited to 80 characters wide.

@strawgate
Copy link
Collaborator

strawgate commented Sep 29, 2025

Using a specific handler for tracebacks where we don't include the error level or file path gives a lot more room for the traceback.

...
import fastmcp
import mcp
import pydantic

def configure_logging(
    level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | int = "INFO",
    logger: logging.Logger | None = None,
    enable_rich_tracebacks: bool | None = None,
    **rich_kwargs: Any,
) -> None:
    """
    Configure logging for FastMCP.

    Args:
        logger: the logger to configure
        level: the log level to use
        rich_kwargs: the parameters to use for creating RichHandler
    """
    # Check if logging is disabled in settings
    if not fastmcp.settings.log_enabled:
        return

    # Use settings default if not specified
    if enable_rich_tracebacks is None:
        enable_rich_tracebacks = fastmcp.settings.enable_rich_tracebacks

    if logger is None:
        logger = logging.getLogger("fastmcp")

    formatter = logging.Formatter("%(message)s")

    # Only configure the FastMCP logger namespace
    handler = RichHandler(
        console=Console(stderr=True),
        rich_tracebacks=enable_rich_tracebacks,
        **rich_kwargs,
    )
    handler.setFormatter(formatter)

    # filter to exclude tracebacks
    handler.addFilter(lambda record: record.exc_info is None)

    traceback_handler = RichHandler(
        console=Console(stderr=True),
        show_path=False,
        show_level=False,
        rich_tracebacks=enable_rich_tracebacks,
        tracebacks_max_frames=3,
        tracebacks_suppress=[fastmcp,mcp,pydantic],
        **rich_kwargs,
    )
    traceback_handler.setFormatter(formatter)

    # filter to include tracebacks
    traceback_handler.addFilter(lambda record: record.exc_info is not None)

    logger.setLevel(level)

    # Remove any existing handlers to avoid duplicates on reconfiguration
    for hdlr in logger.handlers[:]:
        logger.removeHandler(hdlr)

    logger.addHandler(handler)
    logger.addHandler(traceback_handler)

    # Don't propagate to the root logger
    logger.propagate = False
[09/29/25 08:38:11] INFO     Causing traceback                                                                                     main.py:36
[09/29/25 08:38:11] Error calling tool 'cause_traceback'                                                                                     
                    ╭────────────────────────────────────────── Traceback (most recent call last) ──────────────────────────────────────────╮
                    │ /Users/bill.easton/repos/py-mcp-terminal-size/.venv/lib/python3.13/site-packages/fastmcp/tools/tool_manager.py:224 in │
                    │ call_tool                                                                                                             │
                    │                                                                                                                       │
                    │ /Users/bill.easton/repos/py-mcp-terminal-size/.venv/lib/python3.13/site-packages/fastmcp/tools/tool.py:314 in run     │
                    │                                                                                                                       │
                    │ /Users/bill.easton/repos/py-mcp-terminal-size/.venv/lib/python3.13/site-packages/pydantic/type_adapter.py:421 in      │
                    │ validate_python                                                                                                       │
                    │                                                                                                                       │
                    │ /Users/bill.easton/repos/py-mcp-terminal-size/main.py:40 in cause_traceback                                           │
                    │                                                                                                                       │
                    │   37 │   rich_handler: RichHandler = get_handlers_from_logger()[0]                                                    │
                    │   38 │   logger.info(str(rich_handler))                                                                               │
                    │   39 │   logger.info(str(rich_handler.console))                                                                       │
                    │ ❱ 40 │   raise Exception("Test exception")                                                                            │
                    │   41                                                                                                                  │
                    │   42 @mcp.tool()                                                                                                      │

@strawgate
Copy link
Collaborator

strawgate commented Sep 29, 2025

Here's what that looks like in fastmcp cloud

Screenshot 2025-09-29 at 8 43 34 AM

Double handlers is a bit messy but that does look a lot better to me, especially with the limited traceback depth and suppression of framework frames

@strawgate
Copy link
Collaborator

@jlowin i can implement this if you want otherwise we can proceed with whatever you think is best

@jlowin
Copy link
Owner Author

jlowin commented Sep 30, 2025

That does look cleaner but line 38 is still getting cut off in your most recent screenshot:

image

Is this a rich traceback thing where it censors the line? I guess I never looked closely at it

@strawgate
Copy link
Collaborator

strawgate commented Sep 30, 2025

Rich has an option to wrap the code lines, but it's off by default. We could enable it, but truncated has been fine for me.

This is the worst-case scenario, though, where the terminal is not dynamic and is limited to exactly 80 chars. As long as it shows enough code to point to the section that's causing the problem that's a big improvement.

With the limit of 3 for the frames perhaps enabling wrapping wouldn't even be that bad.

@strawgate
Copy link
Collaborator

strawgate commented Oct 7, 2025

@jlowin are you okay with me taking this over per the discussion above? or would you prefer to just keep it off?

@jlowin
Copy link
Owner Author

jlowin commented Oct 8, 2025

Totally fine if you want to take over, and also fine with default off as a sane rapid fallback if it doesn't work! @strawgate

@strawgate strawgate changed the title Default rich tracebacks to False Improve Tracebacks for narrow terminals Oct 10, 2025
@strawgate strawgate self-assigned this Oct 10, 2025
@strawgate strawgate requested a review from Copilot October 10, 2025 20:39
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR improves traceback display for narrow terminals by implementing a compressed traceback format. The change creates a dual-handler logging system: one for regular logs and another specifically for tracebacks with reduced visual clutter.

Key Changes:

  • Splits logging into two handlers: one for normal logs (excluding tracebacks) and one dedicated to tracebacks
  • Configures the traceback handler with compressed formatting (no path/level display) and limits frames to 3 with suppressed framework frames

@strawgate
Copy link
Collaborator

@jlowin updated and should be good to go
Before:
Screenshot 2025-10-10 at 4 41 09 PM

After:
Screenshot 2025-10-10 at 4 40 44 PM

@strawgate
Copy link
Collaborator

strawgate commented Oct 10, 2025

Interestingly -- the current state of the PR also seems to improve the performance of printing tracebacks by about 5x (reduce time spent in the richhandler by 80%).

In my test setup, printing 20 tracebacks takes 1s on main, 0.2s in this PR and 0.1s with rich tracebacks off

See #2053

@jlowin
Copy link
Owner Author

jlowin commented Oct 10, 2025

I can't submit a review bc it's my own PR but LGTM!

@jlowin jlowin merged commit 8b1099d into main Oct 10, 2025
8 checks passed
@jlowin jlowin deleted the tracebacks branch October 10, 2025 21:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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.

3 participants