11package main
22
33import (
4+ "bytes"
45"context"
6+ "encoding/json"
57"fmt"
68"io"
79stdlog"log"
@@ -39,12 +41,20 @@ var (
3941logFile := viper .GetString ("log-file" )
4042readOnly := viper .GetBool ("read-only" )
4143exportTranslations := viper .GetBool ("export-translations" )
44+ prettyPrintJSON := viper .GetBool ("pretty-print-json" )
4245logger ,err := initLogger (logFile )
4346if err != nil {
4447stdlog .Fatal ("Failed to initialize logger:" ,err )
4548}
4649logCommands := 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 {
4858stdlog .Fatal ("failed to run stdio server:" ,err )
4959}
5060},
@@ -60,13 +70,15 @@ func init() {
6070rootCmd .PersistentFlags ().Bool ("enable-command-logging" ,false ,"When enabled, the server will log all command requests and responses to the log file" )
6171rootCmd .PersistentFlags ().Bool ("export-translations" ,false ,"Save translations to a JSON file" )
6272rootCmd .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
7284rootCmd .AddCommand (stdioCmd )
@@ -95,15 +107,36 @@ func initLogger(outPath string) (*log.Logger, error) {
95107return 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
100133ctx ,stop := signal .NotifyContext (context .Background (),os .Interrupt ,syscall .SIGTERM )
101134defer stop ()
102135
103136// Create GH client
104137token := os .Getenv ("GITHUB_PERSONAL_ACCESS_TOKEN" )
105138if token == "" {
106- logger .Fatal ("GITHUB_PERSONAL_ACCESS_TOKEN not set" )
139+ cfg . logger .Fatal ("GITHUB_PERSONAL_ACCESS_TOKEN not set" )
107140}
108141ghClient := gogithub .NewClient (nil ).WithAuthToken (token )
109142ghClient .UserAgent = fmt .Sprintf ("github-mcp-server/%s" ,version )
@@ -125,13 +158,13 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT
125158t ,dumpTranslations := translations .TranslationHelper ()
126159
127160// Create
128- ghServer := github .NewServer (ghClient ,readOnly ,t )
161+ ghServer := github .NewServer (ghClient ,cfg . readOnly ,t )
129162stdioServer := server .NewStdioServer (ghServer )
130163
131- stdLogger := stdlog .New (logger .Writer (),"stdioserver" ,0 )
164+ stdLogger := stdlog .New (cfg . logger .Writer (),"stdioserver" ,0 )
132165stdioServer .SetErrorLogger (stdLogger )
133166
134- if exportTranslations {
167+ if cfg . exportTranslations {
135168// Once server is initialized, all translations are loaded
136169dumpTranslations ()
137170}
@@ -141,11 +174,14 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT
141174go func () {
142175in ,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 )
146179in ,out = loggedIO ,loggedIO
147180}
148181
182+ if cfg .prettyPrintJSON {
183+ out = JSONPrettyPrintWriter {writer :out }
184+ }
149185errC <- 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
156192select {
157193case <- ctx .Done ():
158- logger .Infof ("shutting down server..." )
194+ cfg . logger .Infof ("shutting down server..." )
159195case err := <- errC :
160196if err != nil {
161197return fmt .Errorf ("error running server: %w" ,err )