Spaces:
Running
Running
File size: 3,752 Bytes
ae32ca4 a266504 f7b7ab6 ae32ca4 9804745 ae32ca4 4a12d22 ae32ca4 4a12d22 ae32ca4 4a12d22 ae32ca4 4a12d22 ae32ca4 4a12d22 9804745 ae32ca4 9804745 ae32ca4 9804745 4a12d22 9804745 ae32ca4 4a12d22 ae32ca4 4a12d22 ae32ca4 4a12d22 ae32ca4 4a12d22 ae32ca4 2651ffb ae32ca4 2651ffb ae32ca4 4a12d22 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | ---
title: Server Logging
sidebarTitle: Logging
description: Receive and handle log messages from MCP servers.
icon: receipt
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="2.0.0" />
MCP servers can emit log messages to clients. The client can handle these logs through a log handler callback.
## Log Handler
Provide a `log_handler` function when creating the client. For robust logging, the log messages can be integrated with Python's standard `logging` module.
```python
import logging
from fastmcp import Client
from fastmcp.client.logging import LogMessage
# In a real app, you might configure this in your main entry point
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Get a logger for the module where the client is used
logger = logging.getLogger(__name__)
# This mapping is useful for converting MCP level strings to Python's levels
LOGGING_LEVEL_MAP = logging.getLevelNamesMapping()
async def log_handler(message: LogMessage):
"""
Handles incoming logs from the MCP server and forwards them
to the standard Python logging system.
"""
msg = message.data.get('msg')
extra = message.data.get('extra')
# Convert the MCP log level to a Python log level
level = LOGGING_LEVEL_MAP.get(message.level.upper(), logging.INFO)
# Log the message using the standard logging library
logger.log(level, msg, extra=extra)
client = Client(
"my_mcp_server.py",
log_handler=log_handler,
)
```
## Handling Structured Logs
The `message.data` attribute is a dictionary that contains the log payload from the server. This enables structured logging, allowing you to receive rich, contextual information.
The dictionary contains two keys:
- `msg`: The string log message.
- `extra`: A dictionary containing any extra data sent from the server.
This structure is preserved even when logs are forwarded through a FastMCP proxy, making it a powerful tool for debugging complex, multi-server applications.
### Handler Parameters
The `log_handler` is called every time a log message is received. It receives a `LogMessage` object:
<Card icon="code" title="Log Handler Parameters">
<ResponseField name="LogMessage" type="Log Message Object">
<Expandable title="attributes">
<ResponseField name="level" type='Literal["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]'>
The log level
</ResponseField>
<ResponseField name="logger" type="str | None">
The logger name (optional, may be None)
</ResponseField>
<ResponseField name="data" type="dict">
The log payload, containing `msg` and `extra` keys.
</ResponseField>
</Expandable>
</ResponseField>
</Card>
```python
async def detailed_log_handler(message: LogMessage):
msg = message.data.get('msg')
extra = message.data.get('extra')
if message.level == "error":
print(f"ERROR: {msg} | Details: {extra}")
elif message.level == "warning":
print(f"WARNING: {msg} | Details: {extra}")
else:
print(f"{message.level.upper()}: {msg}")
```
## Default Log Handling
If you don't provide a custom `log_handler`, FastMCP's default handler routes server logs to the appropriate Python logging levels. The MCP levels are mapped as follows: `notice` → INFO; `alert` and `emergency` → CRITICAL. If the server includes a logger name, it is prefixed in the message, and any `extra` data is forwarded via the logging `extra` parameter.
```python
client = Client("my_mcp_server.py")
async with client:
# Server logs are forwarded at their proper severity (DEBUG/INFO/WARNING/ERROR/CRITICAL)
await client.call_tool("some_tool")
```
|