From 4fa9f38e09adfefd77af474e86b90ead0702eeac Mon Sep 17 00:00:00 2001 From: Vincent <0426vincent@gmail.com> Date: Wed, 29 Oct 2025 22:08:33 -0700 Subject: [PATCH 1/2] fix: Include extra field for context log --- src/mcp/server/fastmcp/server.py | 12 +++++++- tests/client/test_logging_callback.py | 42 +++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py index 719595916a..e5c7a35df7 100644 --- a/src/mcp/server/fastmcp/server.py +++ b/src/mcp/server/fastmcp/server.py @@ -1206,6 +1206,7 @@ async def log( message: str, *, logger_name: str | None = None, + **extra: Any, ) -> None: """Send a log message to the client. @@ -1215,9 +1216,18 @@ async def log( logger_name: Optional logger name **extra: Additional structured data to include """ + + if extra: + log_data = { + "message": message, + **extra, + } + else: + log_data = message + await self.request_context.session.send_log_message( level=level, - data=message, + data=log_data, logger=logger_name, related_request_id=self.request_id, ) diff --git a/tests/client/test_logging_callback.py b/tests/client/test_logging_callback.py index f298ee2871..c55a92fba2 100644 --- a/tests/client/test_logging_callback.py +++ b/tests/client/test_logging_callback.py @@ -1,4 +1,4 @@ -from typing import Literal +from typing import Any, Literal import pytest @@ -47,6 +47,24 @@ async def test_tool_with_log( ) return True + @server.tool("test_tool_with_log_extra") + async def test_tool_with_log_extra( + message: str, + level: Literal["debug", "info", "warning", "error"], + logger: str, + extra_string: str, + extra_dict: dict[str, Any], + ) -> bool: + """Send a log notification to the client with extra fields.""" + await server.get_context().log( + level=level, + message=message, + logger_name=logger, + extra_string=extra_string, + extra_dict=extra_dict, + ) + return True + # Create a message handler to catch exceptions async def message_handler( message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception, @@ -74,10 +92,30 @@ async def message_handler( "logger": "test_logger", }, ) + log_result_with_extra = await client_session.call_tool( + "test_tool_with_log_extra", + { + "message": "Test log message", + "level": "info", + "logger": "test_logger", + "extra_string": "example", + "extra_dict": {"a": 1, "b": 2, "c": 3}, + }, + ) assert log_result.isError is False - assert len(logging_collector.log_messages) == 1 + assert log_result_with_extra.isError is False + assert len(logging_collector.log_messages) == 2 # Create meta object with related_request_id added dynamically log = logging_collector.log_messages[0] assert log.level == "info" assert log.logger == "test_logger" assert log.data == "Test log message" + + log_with_extra = logging_collector.log_messages[1] + assert log_with_extra.level == "info" + assert log_with_extra.logger == "test_logger" + assert log_with_extra.data == { + "message": "Test log message", + "extra_string": "example", + "extra_dict": {"a": 1, "b": 2, "c": 3}, + } From e0006bbead1401040988f4275caa658d1489c240 Mon Sep 17 00:00:00 2001 From: Vincent <0426vincent@gmail.com> Date: Fri, 31 Oct 2025 07:51:24 -0700 Subject: [PATCH 2/2] fix: Make context log extra field dict --- src/mcp/server/fastmcp/server.py | 22 ++++++++++++---------- tests/client/test_logging_callback.py | 3 +-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py index e5c7a35df7..e5a9eab7e0 100644 --- a/src/mcp/server/fastmcp/server.py +++ b/src/mcp/server/fastmcp/server.py @@ -1206,7 +1206,7 @@ async def log( message: str, *, logger_name: str | None = None, - **extra: Any, + extra: dict[str, Any] | None = None, ) -> None: """Send a log message to the client. @@ -1214,7 +1214,7 @@ async def log( level: Log level (debug, info, warning, error) message: Log message logger_name: Optional logger name - **extra: Additional structured data to include + extra: Optional dictionary with additional structured data to include """ if extra: @@ -1248,18 +1248,20 @@ def session(self): return self.request_context.session # Convenience methods for common log levels - async def debug(self, message: str, **extra: Any) -> None: + async def debug(self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None: """Send a debug log message.""" - await self.log("debug", message, **extra) + await self.log("debug", message, logger_name=logger_name, extra=extra) - async def info(self, message: str, **extra: Any) -> None: + async def info(self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None: """Send an info log message.""" - await self.log("info", message, **extra) + await self.log("info", message, logger_name=logger_name, extra=extra) - async def warning(self, message: str, **extra: Any) -> None: + async def warning( + self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None + ) -> None: """Send a warning log message.""" - await self.log("warning", message, **extra) + await self.log("warning", message, logger_name=logger_name, extra=extra) - async def error(self, message: str, **extra: Any) -> None: + async def error(self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None: """Send an error log message.""" - await self.log("error", message, **extra) + await self.log("error", message, logger_name=logger_name, extra=extra) diff --git a/tests/client/test_logging_callback.py b/tests/client/test_logging_callback.py index c55a92fba2..3b6143d4a0 100644 --- a/tests/client/test_logging_callback.py +++ b/tests/client/test_logging_callback.py @@ -60,8 +60,7 @@ async def test_tool_with_log_extra( level=level, message=message, logger_name=logger, - extra_string=extra_string, - extra_dict=extra_dict, + extra={"extra_string": extra_string, "extra_dict": extra_dict}, ) return True