From 52bed282669507ddc1509a944bf17dbf6d734426 Mon Sep 17 00:00:00 2001 From: Ariel Deitcher Date: Fri, 4 Apr 2025 17:21:34 -0700 Subject: [PATCH 1/2] create a config struct for running the server --- cmd/github-mcp-server/main.go | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/cmd/github-mcp-server/main.go b/cmd/github-mcp-server/main.go index 8620a0fa..de50ccd6 100644 --- a/cmd/github-mcp-server/main.go +++ b/cmd/github-mcp-server/main.go @@ -44,7 +44,13 @@ var ( stdlog.Fatal("Failed to initialize logger:", err) } logCommands := viper.GetBool("enable-command-logging") - if err := runStdioServer(readOnly, logger, logCommands, exportTranslations); err != nil { + cfg := runConfig{ + readOnly: readOnly, + logger: logger, + logCommands: logCommands, + exportTranslations: exportTranslations, + } + if err := runStdioServer(cfg); err != nil { stdlog.Fatal("failed to run stdio server:", err) } }, @@ -95,7 +101,14 @@ func initLogger(outPath string) (*log.Logger, error) { return logger, nil } -func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportTranslations bool) error { +type runConfig struct { + readOnly bool + logger *log.Logger + logCommands bool + exportTranslations bool +} + +func runStdioServer(cfg runConfig) error { // Create app context ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() @@ -103,7 +116,7 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT // Create GH client token := os.Getenv("GITHUB_PERSONAL_ACCESS_TOKEN") if token == "" { - logger.Fatal("GITHUB_PERSONAL_ACCESS_TOKEN not set") + cfg.logger.Fatal("GITHUB_PERSONAL_ACCESS_TOKEN not set") } ghClient := gogithub.NewClient(nil).WithAuthToken(token) ghClient.UserAgent = fmt.Sprintf("github-mcp-server/%s", version) @@ -125,13 +138,13 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT t, dumpTranslations := translations.TranslationHelper() // Create - ghServer := github.NewServer(ghClient, readOnly, t) + ghServer := github.NewServer(ghClient, cfg.readOnly, t) stdioServer := server.NewStdioServer(ghServer) - stdLogger := stdlog.New(logger.Writer(), "stdioserver", 0) + stdLogger := stdlog.New(cfg.logger.Writer(), "stdioserver", 0) stdioServer.SetErrorLogger(stdLogger) - if exportTranslations { + if cfg.exportTranslations { // Once server is initialized, all translations are loaded dumpTranslations() } @@ -141,8 +154,8 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT go func() { in, out := io.Reader(os.Stdin), io.Writer(os.Stdout) - if logCommands { - loggedIO := iolog.NewIOLogger(in, out, logger) + if cfg.logCommands { + loggedIO := iolog.NewIOLogger(in, out, cfg.logger) in, out = loggedIO, loggedIO } @@ -155,7 +168,7 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT // Wait for shutdown signal select { case <-ctx.Done(): - logger.Infof("shutting down server...") + cfg.logger.Infof("shutting down server...") case err := <-errC: if err != nil { return fmt.Errorf("error running server: %w", err) From 164bc050ac6fed8563d0253d44c0f9f94910bf9c Mon Sep 17 00:00:00 2001 From: Ariel Deitcher Date: Fri, 4 Apr 2025 17:31:52 -0700 Subject: [PATCH 2/2] pretty print json --- cmd/github-mcp-server/main.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/cmd/github-mcp-server/main.go b/cmd/github-mcp-server/main.go index de50ccd6..f3dff6d3 100644 --- a/cmd/github-mcp-server/main.go +++ b/cmd/github-mcp-server/main.go @@ -1,7 +1,9 @@ package main import ( + "bytes" "context" + "encoding/json" "fmt" "io" stdlog "log" @@ -39,6 +41,7 @@ var ( logFile := viper.GetString("log-file") readOnly := viper.GetBool("read-only") exportTranslations := viper.GetBool("export-translations") + prettyPrintJSON := viper.GetBool("pretty-print-json") logger, err := initLogger(logFile) if err != nil { stdlog.Fatal("Failed to initialize logger:", err) @@ -49,6 +52,7 @@ var ( logger: logger, logCommands: logCommands, exportTranslations: exportTranslations, + prettyPrintJSON: prettyPrintJSON, } if err := runStdioServer(cfg); err != nil { stdlog.Fatal("failed to run stdio server:", err) @@ -66,6 +70,7 @@ func init() { rootCmd.PersistentFlags().Bool("enable-command-logging", false, "When enabled, the server will log all command requests and responses to the log file") rootCmd.PersistentFlags().Bool("export-translations", false, "Save translations to a JSON file") rootCmd.PersistentFlags().String("gh-host", "", "Specify the GitHub hostname (for GitHub Enterprise etc.)") + rootCmd.PersistentFlags().Bool("pretty-print-json", false, "Pretty print JSON output") // Bind flag to viper _ = viper.BindPFlag("read-only", rootCmd.PersistentFlags().Lookup("read-only")) @@ -73,6 +78,7 @@ func init() { _ = viper.BindPFlag("enable-command-logging", rootCmd.PersistentFlags().Lookup("enable-command-logging")) _ = viper.BindPFlag("export-translations", rootCmd.PersistentFlags().Lookup("export-translations")) _ = viper.BindPFlag("gh-host", rootCmd.PersistentFlags().Lookup("gh-host")) + _ = viper.BindPFlag("pretty-print-json", rootCmd.PersistentFlags().Lookup("pretty-print-json")) // Add subcommands rootCmd.AddCommand(stdioCmd) @@ -106,6 +112,20 @@ type runConfig struct { logger *log.Logger logCommands bool exportTranslations bool + prettyPrintJSON bool +} + +// JSONPrettyPrintWriter is a Writer that pretty prints input to indented JSON +type JSONPrettyPrintWriter struct { + writer io.Writer +} + +func (j JSONPrettyPrintWriter) Write(p []byte) (n int, err error) { + var prettyJSON bytes.Buffer + if err := json.Indent(&prettyJSON, p, "", "\t"); err != nil { + return 0, err + } + return j.writer.Write(prettyJSON.Bytes()) } func runStdioServer(cfg runConfig) error { @@ -159,6 +179,9 @@ func runStdioServer(cfg runConfig) error { in, out = loggedIO, loggedIO } + if cfg.prettyPrintJSON { + out = JSONPrettyPrintWriter{writer: out} + } errC <- stdioServer.Listen(ctx, in, out) }()