Provide a log_handler function when creating the client. For robust logging, the log messages can be integrated with Python’s standard logging module.
import loggingfrom fastmcp import Clientfrom fastmcp.client.logging import LogMessage# In a real app, you might configure this in your main entry pointlogging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')# Get a logger for the module where the client is usedlogger = logging.getLogger(__name__)# This mapping is useful for converting MCP level strings to Python's levelsLOGGING_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,)
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.
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.
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")