diff --git a/examples/mcpserver/logging_and_progress.py b/examples/mcpserver/logging_and_progress.py index b157f9dd0..5c8535542 100644 --- a/examples/mcpserver/logging_and_progress.py +++ b/examples/mcpserver/logging_and_progress.py @@ -12,7 +12,9 @@ async def echo(text: str, ctx: Context) -> str: """Echo the input text sending log messages and progress updates during processing.""" await ctx.report_progress(progress=0, total=100) - await ctx.info("Starting to process echo for input: " + text) + + # Test logging with objects (not just strings) - now valid per MCP spec + await ctx.info({"status": "starting", "input_length": len(text), "text": text}) await asyncio.sleep(2) @@ -21,7 +23,8 @@ async def echo(text: str, ctx: Context) -> str: await asyncio.sleep(2) - await ctx.info("Finished processing echo for input: " + text) + # Test logging with a list + await ctx.info(["processing", "complete", "returning"]) await ctx.report_progress(progress=100, total=100) # Progress notifications are process asynchronously by the client. diff --git a/src/mcp/server/mcpserver/context.py b/src/mcp/server/mcpserver/context.py index 1538adc7c..b9133569c 100644 --- a/src/mcp/server/mcpserver/context.py +++ b/src/mcp/server/mcpserver/context.py @@ -187,7 +187,7 @@ async def elicit_url( async def log( self, level: Literal["debug", "info", "warning", "error"], - message: str, + data: Any, *, logger_name: str | None = None, extra: dict[str, Any] | None = None, @@ -196,15 +196,15 @@ async def log( Args: level: Log level (debug, info, warning, error) - message: Log message + data: The data to be logged, such as a string message or an object. Any JSON serializable type is allowed. logger_name: Optional logger name extra: Optional dictionary with additional structured data to include """ if extra: - log_data = {"message": message, **extra} + log_data = {"message": data, **extra} else: - log_data = message + log_data = data await self.request_context.session.send_log_message( level=level, @@ -261,20 +261,20 @@ async def close_standalone_sse_stream(self) -> None: await self._request_context.close_standalone_sse_stream() # Convenience methods for common log levels - async def debug(self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None: + async def debug(self, data: Any, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None: """Send a debug log message.""" - await self.log("debug", message, logger_name=logger_name, extra=extra) + await self.log("debug", data, logger_name=logger_name, extra=extra) - async def info(self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None: + async def info(self, data: Any, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None: """Send an info log message.""" - await self.log("info", message, logger_name=logger_name, extra=extra) + await self.log("info", data, logger_name=logger_name, extra=extra) async def warning( - self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None + self, data: Any, *, logger_name: str | None = None, extra: dict[str, Any] | None = None ) -> None: """Send a warning log message.""" - await self.log("warning", message, logger_name=logger_name, extra=extra) + await self.log("warning", data, logger_name=logger_name, extra=extra) - async def error(self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None: + async def error(self, data: Any, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None: """Send an error log message.""" - await self.log("error", message, logger_name=logger_name, extra=extra) + await self.log("error", data, logger_name=logger_name, extra=extra)