1
1
package main
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
6
+ "encoding/json"
5
7
"fmt"
6
8
"io"
7
9
stdlog "log"
@@ -39,12 +41,20 @@ var (
39
41
logFile := viper .GetString ("log-file" )
40
42
readOnly := viper .GetBool ("read-only" )
41
43
exportTranslations := viper .GetBool ("export-translations" )
44
+ prettyPrintJSON := viper .GetBool ("pretty-print-json" )
42
45
logger , err := initLogger (logFile )
43
46
if err != nil {
44
47
stdlog .Fatal ("Failed to initialize logger:" , err )
45
48
}
46
49
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 {
48
58
stdlog .Fatal ("failed to run stdio server:" , err )
49
59
}
50
60
},
@@ -60,13 +70,15 @@ func init() {
60
70
rootCmd .PersistentFlags ().Bool ("enable-command-logging" , false , "When enabled, the server will log all command requests and responses to the log file" )
61
71
rootCmd .PersistentFlags ().Bool ("export-translations" , false , "Save translations to a JSON file" )
62
72
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" )
63
74
64
75
// Bind flag to viper
65
76
_ = viper .BindPFlag ("read-only" , rootCmd .PersistentFlags ().Lookup ("read-only" ))
66
77
_ = viper .BindPFlag ("log-file" , rootCmd .PersistentFlags ().Lookup ("log-file" ))
67
78
_ = viper .BindPFlag ("enable-command-logging" , rootCmd .PersistentFlags ().Lookup ("enable-command-logging" ))
68
79
_ = viper .BindPFlag ("export-translations" , rootCmd .PersistentFlags ().Lookup ("export-translations" ))
69
80
_ = viper .BindPFlag ("gh-host" , rootCmd .PersistentFlags ().Lookup ("gh-host" ))
81
+ _ = viper .BindPFlag ("pretty-print-json" , rootCmd .PersistentFlags ().Lookup ("pretty-print-json" ))
70
82
71
83
// Add subcommands
72
84
rootCmd .AddCommand (stdioCmd )
@@ -95,15 +107,36 @@ func initLogger(outPath string) (*log.Logger, error) {
95
107
return logger , nil
96
108
}
97
109
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 {
99
132
// Create app context
100
133
ctx , stop := signal .NotifyContext (context .Background (), os .Interrupt , syscall .SIGTERM )
101
134
defer stop ()
102
135
103
136
// Create GH client
104
137
token := os .Getenv ("GITHUB_PERSONAL_ACCESS_TOKEN" )
105
138
if token == "" {
106
- logger .Fatal ("GITHUB_PERSONAL_ACCESS_TOKEN not set" )
139
+ cfg . logger .Fatal ("GITHUB_PERSONAL_ACCESS_TOKEN not set" )
107
140
}
108
141
ghClient := gogithub .NewClient (nil ).WithAuthToken (token )
109
142
ghClient .UserAgent = fmt .Sprintf ("github-mcp-server/%s" , version )
@@ -125,13 +158,13 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT
125
158
t , dumpTranslations := translations .TranslationHelper ()
126
159
127
160
// Create
128
- ghServer := github .NewServer (ghClient , readOnly , t )
161
+ ghServer := github .NewServer (ghClient , cfg . readOnly , t )
129
162
stdioServer := server .NewStdioServer (ghServer )
130
163
131
- stdLogger := stdlog .New (logger .Writer (), "stdioserver" , 0 )
164
+ stdLogger := stdlog .New (cfg . logger .Writer (), "stdioserver" , 0 )
132
165
stdioServer .SetErrorLogger (stdLogger )
133
166
134
- if exportTranslations {
167
+ if cfg . exportTranslations {
135
168
// Once server is initialized, all translations are loaded
136
169
dumpTranslations ()
137
170
}
@@ -141,11 +174,14 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT
141
174
go func () {
142
175
in , out := io .Reader (os .Stdin ), io .Writer (os .Stdout )
143
176
144
- if logCommands {
145
- loggedIO := iolog .NewIOLogger (in , out , logger )
177
+ if cfg . logCommands {
178
+ loggedIO := iolog .NewIOLogger (in , out , cfg . logger )
146
179
in , out = loggedIO , loggedIO
147
180
}
148
181
182
+ if cfg .prettyPrintJSON {
183
+ out = JSONPrettyPrintWriter {writer : out }
184
+ }
149
185
errC <- stdioServer .Listen (ctx , in , out )
150
186
}()
151
187
@@ -155,7 +191,7 @@ func runStdioServer(readOnly bool, logger *log.Logger, logCommands bool, exportT
155
191
// Wait for shutdown signal
156
192
select {
157
193
case <- ctx .Done ():
158
- logger .Infof ("shutting down server..." )
194
+ cfg . logger .Infof ("shutting down server..." )
159
195
case err := <- errC :
160
196
if err != nil {
161
197
return fmt .Errorf ("error running server: %w" , err )
0 commit comments