Skip to content

Commit 06748eb

Browse files
authored
fix: Include extra field for context log (#1535)
1 parent 2aa1ad2 commit 06748eb

File tree

2 files changed

+61
-12
lines changed

2 files changed

+61
-12
lines changed

src/mcp/server/fastmcp/server.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,18 +1260,28 @@ async def log(
12601260
message: str,
12611261
*,
12621262
logger_name: str | None = None,
1263+
extra: dict[str, Any] | None = None,
12631264
) -> None:
12641265
"""Send a log message to the client.
12651266
12661267
Args:
12671268
level: Log level (debug, info, warning, error)
12681269
message: Log message
12691270
logger_name: Optional logger name
1270-
**extra: Additional structured data to include
1271+
extra: Optional dictionary with additional structured data to include
12711272
"""
1273+
1274+
if extra:
1275+
log_data = {
1276+
"message": message,
1277+
**extra,
1278+
}
1279+
else:
1280+
log_data = message
1281+
12721282
await self.request_context.session.send_log_message(
12731283
level=level,
1274-
data=message,
1284+
data=log_data,
12751285
logger=logger_name,
12761286
related_request_id=self.request_id,
12771287
)
@@ -1326,18 +1336,20 @@ async def close_standalone_sse_stream(self) -> None:
13261336
await self._request_context.close_standalone_sse_stream()
13271337

13281338
# Convenience methods for common log levels
1329-
async def debug(self, message: str, **extra: Any) -> None:
1339+
async def debug(self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None:
13301340
"""Send a debug log message."""
1331-
await self.log("debug", message, **extra)
1341+
await self.log("debug", message, logger_name=logger_name, extra=extra)
13321342

1333-
async def info(self, message: str, **extra: Any) -> None:
1343+
async def info(self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None:
13341344
"""Send an info log message."""
1335-
await self.log("info", message, **extra)
1345+
await self.log("info", message, logger_name=logger_name, extra=extra)
13361346

1337-
async def warning(self, message: str, **extra: Any) -> None:
1347+
async def warning(
1348+
self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None
1349+
) -> None:
13381350
"""Send a warning log message."""
1339-
await self.log("warning", message, **extra)
1351+
await self.log("warning", message, logger_name=logger_name, extra=extra)
13401352

1341-
async def error(self, message: str, **extra: Any) -> None:
1353+
async def error(self, message: str, *, logger_name: str | None = None, extra: dict[str, Any] | None = None) -> None:
13421354
"""Send an error log message."""
1343-
await self.log("error", message, **extra)
1355+
await self.log("error", message, logger_name=logger_name, extra=extra)

tests/client/test_logging_callback.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Literal
1+
from typing import Any, Literal
22

33
import pytest
44

@@ -47,6 +47,23 @@ async def test_tool_with_log(
4747
)
4848
return True
4949

50+
@server.tool("test_tool_with_log_extra")
51+
async def test_tool_with_log_extra(
52+
message: str,
53+
level: Literal["debug", "info", "warning", "error"],
54+
logger: str,
55+
extra_string: str,
56+
extra_dict: dict[str, Any],
57+
) -> bool:
58+
"""Send a log notification to the client with extra fields."""
59+
await server.get_context().log(
60+
level=level,
61+
message=message,
62+
logger_name=logger,
63+
extra={"extra_string": extra_string, "extra_dict": extra_dict},
64+
)
65+
return True
66+
5067
# Create a message handler to catch exceptions
5168
async def message_handler(
5269
message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception,
@@ -74,10 +91,30 @@ async def message_handler(
7491
"logger": "test_logger",
7592
},
7693
)
94+
log_result_with_extra = await client_session.call_tool(
95+
"test_tool_with_log_extra",
96+
{
97+
"message": "Test log message",
98+
"level": "info",
99+
"logger": "test_logger",
100+
"extra_string": "example",
101+
"extra_dict": {"a": 1, "b": 2, "c": 3},
102+
},
103+
)
77104
assert log_result.isError is False
78-
assert len(logging_collector.log_messages) == 1
105+
assert log_result_with_extra.isError is False
106+
assert len(logging_collector.log_messages) == 2
79107
# Create meta object with related_request_id added dynamically
80108
log = logging_collector.log_messages[0]
81109
assert log.level == "info"
82110
assert log.logger == "test_logger"
83111
assert log.data == "Test log message"
112+
113+
log_with_extra = logging_collector.log_messages[1]
114+
assert log_with_extra.level == "info"
115+
assert log_with_extra.logger == "test_logger"
116+
assert log_with_extra.data == {
117+
"message": "Test log message",
118+
"extra_string": "example",
119+
"extra_dict": {"a": 1, "b": 2, "c": 3},
120+
}

0 commit comments

Comments
 (0)