11package main
22
33import (
4+ "bytes"
45 "context"
6+ "encoding/json"
57 "fmt"
68 "io"
79 stdlog "log"
@@ -39,12 +41,20 @@ var (
3941 logFile := viper .GetString ("log-file" )
4042 readOnly := viper .GetBool ("read-only" )
4143 exportTranslations := viper .GetBool ("export-translations" )
44+ prettyPrintJSON := viper .GetBool ("pretty-print-json" )
4245 logger , err := initLogger (logFile )
4346 if err != nil {
4447 stdlog .Fatal ("Failed to initialize logger:" , err )
4548 }
4649 logCommands := viper .GetBool ("enable-command-logging" )
47- if err := runStdioServer (readOnly , logger , logCommands , exportTranslations ); err != nil {
50+ cfg := runConfig {
51+ readOnly : readOnly ,
52+ logger : logger ,
53+ logCommands : logCommands ,
54+ exportTranslations : exportTranslations ,
55+ prettyPrintJSON : prettyPrintJSON ,
56+ }
57+ if err := runStdioServer (cfg ); err != nil {
4858 stdlog .Fatal ("failed to run stdio server:" , err )
4959 }
5060 },
@@ -60,13 +70,15 @@ func init() {
6070 rootCmd .PersistentFlags ().Bool ("enable-command-logging" , false , "When enabled, the server will log all command requests and responses to the log file" )
6171 rootCmd .PersistentFlags ().Bool ("export-translations" , false , "Save translations to a JSON file" )
6272 rootCmd .PersistentFlags ().String ("gh-host" , "" , "Specify the GitHub hostname (for GitHub Enterprise etc.)" )
73+ rootCmd .PersistentFlags ().Bool ("pretty-print-json" , false , "Pretty print JSON output" )
6374
6475 // Bind flag to viper
6576 _ = viper .BindPFlag ("read-only" , rootCmd .PersistentFlags ().Lookup ("read-only" ))
6677 _ = viper .BindPFlag ("log-file" , rootCmd .PersistentFlags ().Lookup ("log-file" ))
6778 _ = viper .BindPFlag ("enable-command-logging" , rootCmd .PersistentFlags ().Lookup ("enable-command-logging" ))
6879 _ = viper .BindPFlag ("export-translations" , rootCmd .PersistentFlags ().Lookup ("export-translations" ))
6980 _ = viper .BindPFlag ("gh-host" , rootCmd .PersistentFlags ().Lookup ("gh-host" ))
81+ _ = viper .BindPFlag ("pretty-print-json" , rootCmd .PersistentFlags ().Lookup ("pretty-print-json" ))
7082
7183 // Add subcommands
7284 rootCmd .AddCommand (stdioCmd )
@@ -95,15 +107,36 @@ func initLogger(outPath string) (*log.Logger, error) {
95107 return logger , nil
96108}
97109
98- func runStdioServer (readOnly bool , logger * log.Logger , logCommands bool , exportTranslations bool ) error {
110+ type runConfig struct {
111+ readOnly bool
112+ logger * log.Logger
113+ logCommands bool
114+ exportTranslations bool
115+ prettyPrintJSON bool
116+ }
117+
118+ // JSONPrettyPrintWriter is a Writer that pretty prints input to indented JSON
119+ type JSONPrettyPrintWriter struct {
120+ writer io.Writer
121+ }
122+
123+ func (j JSONPrettyPrintWriter ) Write (p []byte ) (n int , err error ) {
124+ var prettyJSON bytes.Buffer
125+ if err := json .Indent (& prettyJSON , p , "" , "\t " ); err != nil {
126+ return 0 , err
127+ }
128+ return j .writer .Write (prettyJSON .Bytes ())
129+ }
130+
131+ func runStdioServer (cfg runConfig ) error {
99132 // Create app context
100133 ctx , stop := signal .NotifyContext (context .Background (), os .Interrupt , syscall .SIGTERM )
101134 defer stop ()
102135
103136 // Create GH client
104137 token := os .Getenv ("GITHUB_PERSONAL_ACCESS_TOKEN" )
105138 if token == "" {
106- logger .Fatal ("GITHUB_PERSONAL_ACCESS_TOKEN not set" )
139+ cfg . logger .Fatal ("GITHUB_PERSONAL_ACCESS_TOKEN not set" )
107140 }
108141 ghClient := gogithub .NewClient (nil ).WithAuthToken (token )
109142 ghClient .UserAgent = fmt .Sprintf ("github-mcp-server/%s" , version )
@@ -125,13 +158,13 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT
125158 t , dumpTranslations := translations .TranslationHelper ()
126159
127160 // Create
128- ghServer := github .NewServer (ghClient , readOnly , t )
161+ ghServer := github .NewServer (ghClient , cfg . readOnly , t )
129162 stdioServer := server .NewStdioServer (ghServer )
130163
131- stdLogger := stdlog .New (logger .Writer (), "stdioserver" , 0 )
164+ stdLogger := stdlog .New (cfg . logger .Writer (), "stdioserver" , 0 )
132165 stdioServer .SetErrorLogger (stdLogger )
133166
134- if exportTranslations {
167+ if cfg . exportTranslations {
135168 // Once server is initialized, all translations are loaded
136169 dumpTranslations ()
137170 }
@@ -141,11 +174,14 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT
141174 go func () {
142175 in , out := io .Reader (os .Stdin ), io .Writer (os .Stdout )
143176
144- if logCommands {
145- loggedIO := iolog .NewIOLogger (in , out , logger )
177+ if cfg . logCommands {
178+ loggedIO := iolog .NewIOLogger (in , out , cfg . logger )
146179 in , out = loggedIO , loggedIO
147180 }
148181
182+ if cfg .prettyPrintJSON {
183+ out = JSONPrettyPrintWriter {writer : out }
184+ }
149185 errC <- stdioServer .Listen (ctx , in , out )
150186 }()
151187
@@ -155,7 +191,7 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT
155191 // Wait for shutdown signal
156192 select {
157193 case <- ctx .Done ():
158- logger .Infof ("shutting down server..." )
194+ cfg . logger .Infof ("shutting down server..." )
159195 case err := <- errC :
160196 if err != nil {
161197 return fmt .Errorf ("error running server: %w" , err )
0 commit comments