Files
zooqueen 0b920d37c6 o11y: this module is hanzoai/o11y-mcp, and it consumes hanzoai/otel-collector
The module declared itself github.com/SigNoz/signoz-mcp-server and imported the
schema migrator from github.com/SigNoz/signoz-otel-collector — a fork that
hanzoai/otel-collector superseded, where the same package is
cmd/o11yschemamigrator/schema_migrator. Both now point at the repo that is
actually maintained, at v1.3.0.

Go identifiers came with it: the client type is Client rather than SigNoz, the
import alias is o11yclient, and the env-name constants are EnvURL and
EnvAPIKey.

NOT touched, because each is a contract with something running and renaming it
breaks that thing rather than debranding it:

  the 30 MCP tool names (signoz_list_alerts, signoz_get_trace_details, …) and
    the signoz://dashboard/* resource URIs, which are this server's public API
    and are what an agent calls;
  the env vars SIGNOZ_URL and SIGNOZ_API_KEY and the SIGNOZ-API-KEY header,
    which the deployment sets;
  the table names — signoz_traces.distributed_signoz_index_v3, signoz_logs.*,
    signoz_metrics.* — and the metric names signoz_calls_total and
    signoz_latency_*, which name rows and series that exist on the server.

Those want a coordinated change on both sides, not a rename here.

go build and go vet both clean.
2026-08-11 11:47:40 -07:00

106 lines
2.9 KiB
Go

package mcp_server
import (
"fmt"
"net/http"
"strings"
"github.com/mark3labs/mcp-go/server"
"go.uber.org/zap"
"github.com/hanzoai/o11y-mcp/internal/config"
"github.com/hanzoai/o11y-mcp/internal/handler/tools"
"github.com/hanzoai/o11y-mcp/pkg/util"
)
type MCPServer struct {
logger *zap.Logger
handler *tools.Handler
config *config.Config
}
func NewMCPServer(log *zap.Logger, handler *tools.Handler, cfg *config.Config) *MCPServer {
return &MCPServer{logger: log, handler: handler, config: cfg}
}
func (m *MCPServer) Start() error {
s := server.NewMCPServer("SigNozMCP", "0.0.1", server.WithLogging(), server.WithToolCapabilities(false))
m.logger.Info("Starting SigNoz MCP Server",
zap.String("server_name", "SigNozMCPServer"),
zap.String("transport_mode", m.config.TransportMode))
// Register all handlers
m.handler.RegisterMetricsHandlers(s)
m.handler.RegisterAlertsHandlers(s)
m.handler.RegisterDashboardHandlers(s)
m.handler.RegisterServiceHandlers(s)
m.handler.RegisterQueryBuilderV5Handlers(s)
m.handler.RegisterLogsHandlers(s)
m.handler.RegisterTracesHandlers(s)
m.logger.Info("All handlers registered successfully")
if m.config.TransportMode == "http" {
return m.startHTTP(s)
}
return m.startStdio(s)
}
func (m *MCPServer) startStdio(s *server.MCPServer) error {
m.logger.Info("MCP Server running in stdio mode")
return server.ServeStdio(s)
}
func (m *MCPServer) authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Extract Authorization header
authHeader := r.Header.Get("Authorization")
var apiKey string
if authHeader != "" {
// Support both "Bearer <token>" and raw token formats
if strings.HasPrefix(authHeader, "Bearer ") {
apiKey = strings.TrimPrefix(authHeader, "Bearer ")
} else {
apiKey = authHeader
}
// Store API key in request context
ctx := util.SetAPIKey(r.Context(), apiKey)
r = r.WithContext(ctx)
m.logger.Debug("API key extracted from Authorization header")
} else if m.config.APIKey != "" {
// Fallback to config API key if no Authorization header
ctx := util.SetAPIKey(r.Context(), m.config.APIKey)
r = r.WithContext(ctx)
m.logger.Debug("Using API key from environment config")
} else {
m.logger.Warn("No API key found in Authorization header or environment")
http.Error(w, "Authorization header required", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
func (m *MCPServer) startHTTP(s *server.MCPServer) error {
m.logger.Info("MCP Server running in HTTP mode")
addr := fmt.Sprintf(":%s", m.config.Port)
mux := http.NewServeMux()
httpServer := server.NewStreamableHTTPServer(s)
mux.Handle("/mcp", m.authMiddleware(httpServer))
m.logger.Info("Listening for MCP clients",
zap.String("addr", addr),
zap.String("mcp_endpoint", "/mcp"))
return http.ListenAndServe(addr, mux)
}