|
| 1 | +import logging |
| 2 | + |
| 3 | +import pytest |
| 4 | + |
| 5 | +from mcp.server.mcpserver import MCPServer |
| 6 | + |
| 7 | + |
| 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) |
| 25 | + |
| 26 | + |
| 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. |
| 31 | +
|
| 32 | + This matches the standard library's own `logging.basicConfig()` |
| 33 | + contract: it is a no-op if the root logger already has handlers. |
| 34 | + """ |
| 35 | + baseline = list(clean_root_logger.handlers) |
| 36 | + app_handler = logging.StreamHandler() |
| 37 | + clean_root_logger.addHandler(app_handler) |
| 38 | + |
| 39 | + MCPServer("test-server") |
| 40 | + |
| 41 | + assert clean_root_logger.handlers == [*baseline, app_handler] |
| 42 | + |
| 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. |
| 47 | +
|
| 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" |
0 commit comments