// Command server is the Einherjar MCP server: a streamable-HTTP service // exposing framework knowledge tools to AI assistants. // // The framework index is embedded into the binary at build time by // cmd/indexer. The server itself only reads it. package main import ( "flag" "fmt" "log/slog" "net" "net/http" "os" mcpmod "code.nochebuena.dev/einherjar/mcp" "code.nochebuena.dev/einherjar/mcp/internal/index" "code.nochebuena.dev/einherjar/mcp/internal/tools" "github.com/coreos/go-systemd/v22/activation" "github.com/modelcontextprotocol/go-sdk/mcp" ) const ( serverName = "einherjar-mcp" serverVersion = "v0.1.0" ) func main() { addr := flag.String("addr", envOr("EINHERJAR_MCP_ADDR", ":8080"), "listen address") path := flag.String("path", envOr("EINHERJAR_MCP_PATH", "/mcp"), "HTTP path for the MCP streamable endpoint") flag.Parse() log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) idx, err := index.Load(mcpmod.IndexJSON) if err != nil { log.Error("load index", "err", err) os.Exit(1) } log.Info("index loaded", "modules", len(idx.Modules), "builtAt", idx.BuiltAt) server := mcp.NewServer(&mcp.Implementation{ Name: serverName, Version: serverVersion, }, nil) tools.Register(server, idx) handler := mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server { return server }, nil) mux := http.NewServeMux() mux.Handle(*path, handler) mux.HandleFunc(*path+"/healthz", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprintln(w, "ok") }) ln, mode, err := chooseListener(*addr) if err != nil { log.Error("listen", "err", err) os.Exit(1) } log.Info("listening", "mode", mode, "addr", ln.Addr().String(), "path", *path) srv := &http.Server{Handler: mux} if err := srv.Serve(ln); err != nil && err != http.ErrServerClosed { log.Error("server", "err", err) os.Exit(1) } } // chooseListener returns a socket-activated listener when systemd inherited // one, falling back to a plain TCP listener on addr. The mode string is // "socket-activated" or "tcp" for logging. func chooseListener(addr string) (net.Listener, string, error) { listeners, err := activation.Listeners() if err == nil && len(listeners) > 0 { return listeners[0], "socket-activated", nil } ln, err := net.Listen("tcp", addr) if err != nil { return nil, "", err } return ln, "tcp", nil } func envOr(key, def string) string { if v := os.Getenv(key); v != "" { return v } return def }