Skip to main content
Glama
logging.go6.31 kB
// Package logging provides MCP-compatible logging functionality // // This package bridges Go's standard logging (slog) with the Model Context Protocol (MCP) // notification system. It serves several important purposes: // // 1. Adapts standard Go logging to MCP's notification system, which allows using familiar // logging functions (slog.Info, slog.Debug, etc.) throughout the codebase. // // 2. Enables logging to multiple outputs simultaneously via MultiHandler - console logs // for local debugging and MCP notifications for client awareness. // // 3. Handles the conversion between Go's log levels and MCP log levels automatically. // // 4. Maintains a single, consistent logging approach across the application while still // delivering logs to both local console and MCP clients. // // 5. Makes codebase maintenance easier by centralizing logging logic, allowing future // changes to logging behavior without modifying every log statement. // // Without this package, we would need to have duplicate logging calls throughout // the codebase - one for console output and another for MCP client notifications. package logging import ( "context" "log/slog" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) // MultiHandler is a handler that forwards records to multiple handlers type MultiHandler struct { handlers []slog.Handler } // NewMultiHandler creates a handler that forwards records to multiple handlers func NewMultiHandler(handlers ...slog.Handler) *MultiHandler { return &MultiHandler{handlers: handlers} } // Enabled implements slog.Handler func (h *MultiHandler) Enabled(ctx context.Context, level slog.Level) bool { for _, handler := range h.handlers { if handler.Enabled(ctx, level) { return true } } return false } // Handle implements slog.Handler func (h *MultiHandler) Handle(ctx context.Context, record slog.Record) error { for _, handler := range h.handlers { if handler.Enabled(ctx, record.Level) { if err := handler.Handle(ctx, record.Clone()); err != nil { return err } } } return nil } // WithAttrs implements slog.Handler func (h *MultiHandler) WithAttrs(attrs []slog.Attr) slog.Handler { handlers := make([]slog.Handler, len(h.handlers)) for i, handler := range h.handlers { handlers[i] = handler.WithAttrs(attrs) } return &MultiHandler{handlers: handlers} } // WithGroup implements slog.Handler func (m *MultiHandler) WithGroup(name string) slog.Handler { // Create a new slice for the handlers with the group applied. groupedHandlers := make([]slog.Handler, len(m.handlers)) for i, h := range m.handlers { groupedHandlers[i] = h.WithGroup(name) } // Return a new MultiHandler instance with the grouped handlers. return NewMultiHandler(groupedHandlers...) } // NotificationSender interface for sending notifications to clients type NotificationSender interface { SendNotificationToAllClients(method string, params map[string]any) } // MCPNotificationHandler is a handler that sends logs as MCP notifications type MCPNotificationHandler struct { s NotificationSender level slog.Level } // NewMCPNotificationHandler creates a new handler that forwards logs to MCP clients func NewMCPNotificationHandler(s NotificationSender, level slog.Level) *MCPNotificationHandler { return &MCPNotificationHandler{ s: s, level: level, } } // Enabled implements slog.Handler func (h *MCPNotificationHandler) Enabled(ctx context.Context, level slog.Level) bool { return level >= h.level } // Handle implements slog.Handler func (h *MCPNotificationHandler) Handle(ctx context.Context, record slog.Record) error { // Check if this handler is enabled for the record's level if !h.Enabled(ctx, record.Level) { return nil } // Convert slog level to MCP logging level level := slogLevelToMCPLevel(record.Level) // Extract the message message := record.Message // Create a logging message notification using the MCP helper function notification := mcp.NewLoggingMessageNotification(level, "luno-mcp", message) // Send the notification to all clients - need to create a map to pass the params correctly h.s.SendNotificationToAllClients(notification.Method, map[string]any{ "level": string(level), "logger": "luno-mcp", "data": message, }) return nil } // WithAttrs implements slog.Handler func (h *MCPNotificationHandler) WithAttrs(attrs []slog.Attr) slog.Handler { // For simplicity, we ignore attrs in this implementation return h } // WithGroup implements slog.Handler func (h *MCPNotificationHandler) WithGroup(name string) slog.Handler { // For simplicity, we ignore group in this implementation return h } // slogLevelToMCPLevel converts a slog.Level to an MCP LoggingLevel func slogLevelToMCPLevel(level slog.Level) mcp.LoggingLevel { switch { case level <= slog.LevelDebug: return mcp.LoggingLevelDebug case level <= slog.LevelInfo: return mcp.LoggingLevelInfo case level <= slog.LevelWarn: return mcp.LoggingLevelWarning default: return mcp.LoggingLevelError } } // LogRequestHook is the function registered for BeforeAny hook. // It logs all incoming requests at debug level. func LogRequestHook(ctx context.Context, id any, method mcp.MCPMethod, message any) { slog.DebugContext(ctx, "MCP request received", slog.String("method", string(method)), slog.Any("id", id)) } // LogSuccessHook is the function registered for OnSuccess hook. // It logs all outgoing successful responses at debug level. func LogSuccessHook(ctx context.Context, id any, method mcp.MCPMethod, message any, result any) { slog.DebugContext(ctx, "MCP response sent", slog.Any("id", id), slog.String("method", string(method))) } // LogErrorHook is the function registered for OnError hook. // It logs all errors at error level. func LogErrorHook(ctx context.Context, id any, method mcp.MCPMethod, message any, err error) { slog.ErrorContext(ctx, "MCP error occurred", slog.Any("id", id), // Added id for consistency slog.String("method", string(method)), slog.Any("error", err)) } // MCPHooks returns hooks for the MCP server that handle logging func MCPHooks() *server.Hooks { hooks := &server.Hooks{} hooks.AddBeforeAny(LogRequestHook) hooks.AddOnSuccess(LogSuccessHook) hooks.AddOnError(LogErrorHook) return hooks }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/luno/luno-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server