1- import logging
2-
3- import pytest
1+ from unittest .mock import patch
42
53from mcp .server .mcpserver import MCPServer
64
75
8- @pytest .fixture
9- def clean_root_logger ():
10- """Save and restore the root logger's handlers/level around each test.
11-
12- `MCPServer` can mutate the root logger via `logging.basicConfig()`, so
13- tests that exercise this behavior must not leak handlers into other
14- tests (or into pytest's own log capturing, which itself attaches a
15- handler to the root logger -- hence comparing against a baseline
16- snapshot below, rather than assuming an empty handler list).
17- """
18- root = logging .getLogger ()
19- original_handlers = list (root .handlers )
20- original_level = root .level
21- yield root
22- root .handlers .clear ()
23- root .handlers .extend (original_handlers )
24- root .setLevel (original_level )
6+ class TestConfigureLogging :
7+ def test_default_calls_configure_logging (self ):
8+ """By default, `MCPServer` configures logging on construction (the
9+ pre-existing behavior, preserved for backwards compatibility)."""
10+ with patch ("mcp.server.mcpserver.server._configure_logging" ) as spy :
11+ MCPServer ("test-server" )
2512
13+ spy .assert_called_once_with ("INFO" )
2614
27- class TestConfigureLogging :
28- def test_default_does_not_override_existing_handlers (self , clean_root_logger ):
29- """If an application has already configured the root logger before
30- creating an `MCPServer`, the default behavior must not replace it.
15+ def test_configure_logging_false_skips_configuration (self ):
16+ """`configure_logging=False` opts a server out of calling
17+ `_configure_logging` (and therefore `logging.basicConfig()`)
18+ entirely, so an application can safely manage its own logging
19+ setup without the server racing to configure the root logger
20+ first.
3121
32- This matches the standard library's own `logging.basicConfig()`
33- contract: it is a no-op if the root logger already has handlers.
22+ See: https://github.com/modelcontextprotocol/python-sdk/issues/1656
3423 """
35- baseline = list (clean_root_logger .handlers )
36- app_handler = logging .StreamHandler ()
37- clean_root_logger .addHandler (app_handler )
38-
39- MCPServer ("test-server" )
24+ with patch ("mcp.server.mcpserver.server._configure_logging" ) as spy :
25+ MCPServer ("test-server" , configure_logging = False )
4026
41- assert clean_root_logger . handlers == [ * baseline , app_handler ]
27+ spy . assert_not_called ()
4228
43- def test_configure_logging_false_leaves_root_logger_untouched (self , clean_root_logger ):
44- """`configure_logging=False` opts a server out of touching the root
45- logger entirely, so an application can safely configure its own
46- logging before or after constructing the server.
29+ def test_configure_logging_true_still_calls_configure_logging (self ):
30+ """Explicitly passing `configure_logging=True` behaves the same as
31+ the default (sanity check for the new parameter's positive case)."""
32+ with patch ("mcp.server.mcpserver.server._configure_logging" ) as spy :
33+ MCPServer ("test-server" , configure_logging = True , log_level = "DEBUG" )
4734
48- See: https://github.com/modelcontextprotocol/python-sdk/issues/1656
49- """
50- baseline = list (clean_root_logger .handlers )
51-
52- MCPServer ("test-server" , configure_logging = False )
53- assert clean_root_logger .handlers == baseline
54-
55- # The application's own logging setup, performed afterwards, must
56- # take effect (i.e. must not have been pre-empted by the server).
57- # Clear first so `basicConfig` (a no-op if handlers already exist)
58- # is guaranteed to actually apply the new format.
59- clean_root_logger .handlers .clear ()
60- logging .basicConfig (format = "MYAPP: %(message)s" )
61- assert len (clean_root_logger .handlers ) == 1
62- assert clean_root_logger .handlers [0 ].formatter ._fmt == "MYAPP: %(message)s"
35+ spy .assert_called_once_with ("DEBUG" )
0 commit comments