Meet Prefect Horizon
The enterprise MCP gateway built by the team behind FastMCP.
Get Started
Log Handler
Provide a log_handler function when creating the client:
import logging
from fastmcp import Client
from fastmcp.client.logging import LogMessage
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
LOGGING_LEVEL_MAP = logging.getLevelNamesMapping()
async def log_handler(message: LogMessage):
"""Forward MCP server logs to Python's logging system."""
msg = message.data.get('msg')
extra = message.data.get('extra')
level = LOGGING_LEVEL_MAP.get(message.level.upper(), logging.INFO)
logger.log(level, msg, extra=extra)
client = Client(
"my_mcp_server.py",
log_handler=log_handler,
)
The handler receives a LogMessage object:
LogMessage
- level: Literal["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]
The log level - logger: str | None
The logger name (may be None) - data: dict
The log payload, containingmsgandextrakeys
Structured Logs
The message.data attribute is a dictionary containing the log payload. This enables structured logging with rich contextual information.
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}")
This structure is preserved even when logs are forwarded through a FastMCP proxy, making it useful for debugging multi-server applications.
Default Behavior
If you do not provide a custom log_handler, FastMCP’s default handler routes server logs to Python’s logging system at the appropriate severity level. The MCP levels map as follows: notice becomes INFO; alert and emergency become CRITICAL.
client = Client("my_mcp_server.py")
async with client:
# Server logs are forwarded at proper severity automatically
await client.call_tool("some_tool")