Skip to content

Commit 2eda841

Browse files
committed
Add http server with github app auth (#2)
* Add go-githubauth * Add http server with github app auth
1 parent 60a5391 commit 2eda841

File tree

5 files changed

+406
-30
lines changed

5 files changed

+406
-30
lines changed

cmd/github-mcp-server/main.go

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,53 @@ var (
5959
return ghmcp.RunStdioServer(stdioServerConfig)
6060
},
6161
}
62+
63+
httpCmd = &cobra.Command{
64+
Use: "http",
65+
Short: "Start HTTP server",
66+
Long: `Start a server that communicates via HTTP using the Streamable-HTTP transport.`,
67+
RunE: func(_ *cobra.Command, _ []string) error {
68+
// Check if we have either a personal access token or GitHub App credentials
69+
token := viper.GetString("personal_access_token")
70+
appID := viper.GetString("app_id")
71+
appPrivateKey := viper.GetString("app_private_key")
72+
enableGitHubAppAuth := viper.GetBool("enable_github_app_auth")
73+
74+
if token == "" && (!enableGitHubAppAuth || appID == "" || appPrivateKey == "") {
75+
return errors.New("either GITHUB_PERSONAL_ACCESS_TOKEN or GitHub App credentials (GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY) must be set")
76+
}
77+
78+
// If you're wondering why we're not using viper.GetStringSlice("toolsets"),
79+
// it's because viper doesn't handle comma-separated values correctly for env
80+
// vars when using GetStringSlice.
81+
// https://github.com/spf13/viper/issues/380
82+
var enabledToolsets []string
83+
if err := viper.UnmarshalKey("toolsets", &enabledToolsets); err != nil {
84+
return fmt.Errorf("failed to unmarshal toolsets: %w", err)
85+
}
86+
87+
httpServerConfig := ghmcp.HttpServerConfig{
88+
Version: version,
89+
Host: viper.GetString("host"),
90+
Token: token,
91+
EnabledToolsets: enabledToolsets,
92+
DynamicToolsets: viper.GetBool("dynamic_toolsets"),
93+
ReadOnly: viper.GetBool("read-only"),
94+
ExportTranslations: viper.GetBool("export-translations"),
95+
EnableCommandLogging: viper.GetBool("enable-command-logging"),
96+
LogFilePath: viper.GetString("log-file"),
97+
Address: viper.GetString("http_address"),
98+
MCPPath: viper.GetString("http_mcp_path"),
99+
EnableCORS: viper.GetBool("http_enable_cors"),
100+
AppID: appID,
101+
AppPrivateKey: appPrivateKey,
102+
EnableGitHubAppAuth: enableGitHubAppAuth,
103+
InstallationIDHeader: viper.GetString("installation_id_header"),
104+
}
105+
106+
return ghmcp.RunHTTPServer(httpServerConfig)
107+
},
108+
}
62109
)
63110

64111
func init() {
@@ -76,17 +123,36 @@ func init() {
76123
rootCmd.PersistentFlags().Bool("export-translations", false, "Save translations to a JSON file")
77124
rootCmd.PersistentFlags().String("gh-host", "", "Specify the GitHub hostname (for GitHub Enterprise etc.)")
78125

79-
// Bind flag to viper
126+
// GitHub App authentication flags
127+
rootCmd.PersistentFlags().String("app-id", "", "GitHub App ID for authentication")
128+
rootCmd.PersistentFlags().String("app-private-key", "", "GitHub App private key for authentication")
129+
rootCmd.PersistentFlags().Bool("enable-github-app-auth", false, "Enable GitHub App authentication via custom headers")
130+
rootCmd.PersistentFlags().String("installation-id-header", "X-GitHub-Installation-ID", "Custom header name to read installation ID from")
131+
132+
// HTTP server specific flags
133+
httpCmd.Flags().String("http-address", ":8080", "HTTP server address to bind to")
134+
httpCmd.Flags().String("http-mcp-path", "/mcp", "HTTP path for MCP endpoint")
135+
httpCmd.Flags().Bool("http-enable-cors", false, "Enable CORS for cross-origin requests")
136+
137+
// Bind flags to viper
80138
_ = viper.BindPFlag("toolsets", rootCmd.PersistentFlags().Lookup("toolsets"))
81139
_ = viper.BindPFlag("dynamic_toolsets", rootCmd.PersistentFlags().Lookup("dynamic-toolsets"))
82140
_ = viper.BindPFlag("read-only", rootCmd.PersistentFlags().Lookup("read-only"))
83141
_ = viper.BindPFlag("log-file", rootCmd.PersistentFlags().Lookup("log-file"))
84142
_ = viper.BindPFlag("enable-command-logging", rootCmd.PersistentFlags().Lookup("enable-command-logging"))
85143
_ = viper.BindPFlag("export-translations", rootCmd.PersistentFlags().Lookup("export-translations"))
86144
_ = viper.BindPFlag("host", rootCmd.PersistentFlags().Lookup("gh-host"))
145+
_ = viper.BindPFlag("app_id", rootCmd.PersistentFlags().Lookup("app-id"))
146+
_ = viper.BindPFlag("app_private_key", rootCmd.PersistentFlags().Lookup("app-private-key"))
147+
_ = viper.BindPFlag("enable_github_app_auth", rootCmd.PersistentFlags().Lookup("enable-github-app-auth"))
148+
_ = viper.BindPFlag("installation_id_header", rootCmd.PersistentFlags().Lookup("installation-id-header"))
149+
_ = viper.BindPFlag("http_address", httpCmd.Flags().Lookup("http-address"))
150+
_ = viper.BindPFlag("http_mcp_path", httpCmd.Flags().Lookup("http-mcp-path"))
151+
_ = viper.BindPFlag("http_enable_cors", httpCmd.Flags().Lookup("http-enable-cors"))
87152

88153
// Add subcommands
89154
rootCmd.AddCommand(stdioCmd)
155+
rootCmd.AddCommand(httpCmd)
90156
}
91157

92158
func initConfig() {

go.mod

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ require (
1616
require (
1717
github.com/go-openapi/jsonpointer v0.19.5 // indirect
1818
github.com/go-openapi/swag v0.21.1 // indirect
19+
github.com/golang-jwt/jwt/v5 v5.2.3 // indirect
20+
github.com/google/go-github/v69 v69.2.0 // indirect
21+
github.com/jferrl/go-githubauth v1.2.0 // indirect
1922
github.com/josharian/intern v1.0.0 // indirect
2023
github.com/mailru/easyjson v0.7.7 // indirect
2124
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
@@ -30,7 +33,7 @@ require (
3033
github.com/google/go-github/v71 v71.0.0 // indirect
3134
github.com/google/go-querystring v1.1.0 // indirect
3235
github.com/google/uuid v1.6.0 // indirect
33-
github.com/gorilla/mux v1.8.0 // indirect
36+
github.com/gorilla/mux v1.8.1 // indirect
3437
github.com/inconshreveable/mousetrap v1.1.0 // indirect
3538
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
3639
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
@@ -45,10 +48,10 @@ require (
4548
github.com/subosito/gotenv v1.6.0 // indirect
4649
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
4750
go.uber.org/multierr v1.11.0 // indirect
48-
golang.org/x/oauth2 v0.29.0 // indirect
51+
golang.org/x/oauth2 v0.30.0 // indirect
4952
golang.org/x/sys v0.31.0 // indirect
5053
golang.org/x/text v0.23.0 // indirect
51-
golang.org/x/time v0.5.0 // indirect
54+
golang.org/x/time v0.6.0 // indirect
5255
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
5356
gopkg.in/yaml.v3 v3.0.1 // indirect
5457
)

go.sum

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@ github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrK
1515
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
1616
github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
1717
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
18+
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
19+
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
1820
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
1921
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
2022
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
23+
github.com/google/go-github/v69 v69.2.0 h1:wR+Wi/fN2zdUx9YxSmYE0ktiX9IAR/BeePzeaUUbEHE=
24+
github.com/google/go-github/v69 v69.2.0/go.mod h1:xne4jymxLR6Uj9b7J7PyTpkMYstEMMwGZa0Aehh1azM=
2125
github.com/google/go-github/v71 v71.0.0 h1:Zi16OymGKZZMm8ZliffVVJ/Q9YZreDKONCr+WUd0Z30=
2226
github.com/google/go-github/v71 v71.0.0/go.mod h1:URZXObp2BLlMjwu0O8g4y6VBneUj2bCHgnI8FfgZ51M=
2327
github.com/google/go-github/v73 v73.0.0 h1:aR+Utnh+Y4mMkS+2qLQwcQ/cF9mOTpdwnzlaw//rG24=
@@ -28,8 +32,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
2832
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
2933
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
3034
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
35+
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
36+
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
3137
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
3238
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
39+
github.com/jferrl/go-githubauth v1.2.0 h1:K138gEpO2e/yBf6OI5Vb7+0xgZZa7N7/su/iAAG0ieU=
40+
github.com/jferrl/go-githubauth v1.2.0/go.mod h1:mglSJcfvt4HSvuzQKYx4vkvi1PtlMj88m2gz660QuC0=
3341
github.com/josephburnett/jd v1.9.2 h1:ECJRRFXCCqbtidkAHckHGSZm/JIaAxS1gygHLF8MI5Y=
3442
github.com/josephburnett/jd v1.9.2/go.mod h1:bImDr8QXpxMb3SD+w1cDRHp97xP6UwI88xUAuxwDQfM=
3543
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -98,13 +106,17 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0
98106
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
99107
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
100108
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
109+
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
110+
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
101111
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
102112
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
103113
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
104114
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
105115
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
106116
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
107117
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
118+
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
119+
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
108120
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
109121
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
110122
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

internal/ghmcp/http_server.go

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package ghmcp
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
"os"
8+
"os/signal"
9+
"strconv"
10+
"syscall"
11+
"time"
12+
13+
"github.com/github/github-mcp-server/pkg/translations"
14+
"github.com/mark3labs/mcp-go/server"
15+
"github.com/sirupsen/logrus"
16+
)
17+
18+
type HttpServerConfig struct {
19+
// Version of the server
20+
Version string
21+
22+
// GitHub Host to target for API requests (e.g. github.com or github.enterprise.com)
23+
Host string
24+
25+
// GitHub Token to authenticate with the GitHub API
26+
Token string
27+
28+
// EnabledToolsets is a list of toolsets to enable
29+
// See: https://github.com/github/github-mcp-server?tab=readme-ov-file#tool-configuration
30+
EnabledToolsets []string
31+
32+
// Whether to enable dynamic toolsets
33+
// See: https://github.com/github/github-mcp-server?tab=readme-ov-file#dynamic-tool-discovery
34+
DynamicToolsets bool
35+
36+
// ReadOnly indicates if we should only register read-only tools
37+
ReadOnly bool
38+
39+
// ExportTranslations indicates if we should export translations
40+
// See: https://github.com/github/github-mcp-server?tab=readme-ov-file#i18n--overriding-descriptions
41+
ExportTranslations bool
42+
43+
// EnableCommandLogging indicates if we should log commands
44+
EnableCommandLogging bool
45+
46+
// Path to the log file if not stderr
47+
LogFilePath string
48+
49+
// HTTP server configuration
50+
Address string
51+
52+
// MCP endpoint path (defaults to "/mcp")
53+
MCPPath string
54+
55+
// Enable CORS for cross-origin requests
56+
EnableCORS bool
57+
58+
// GITHUB APP ID
59+
AppID string
60+
61+
// GITHUB APP PRIVATE KEY
62+
AppPrivateKey string
63+
64+
// Whether to enable GitHub App authentication via headers
65+
EnableGitHubAppAuth bool
66+
67+
// Custom header name to read installation ID from (defaults to "X-GitHub-Installation-ID")
68+
InstallationIDHeader string
69+
}
70+
71+
const installationContextKey = "installation_id"
72+
73+
// RunHTTPServer is not concurrent safe.
74+
func RunHTTPServer(cfg HttpServerConfig) error {
75+
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
76+
defer stop()
77+
78+
t, dumpTranslations := translations.TranslationHelper()
79+
80+
mcpCfg := MCPServerConfig{
81+
Version: cfg.Version,
82+
Host: cfg.Host,
83+
Token: cfg.Token,
84+
EnabledToolsets: cfg.EnabledToolsets,
85+
DynamicToolsets: cfg.DynamicToolsets,
86+
ReadOnly: cfg.ReadOnly,
87+
Translator: t,
88+
AppID: cfg.AppID,
89+
AppPrivateKey: cfg.AppPrivateKey,
90+
EnableGitHubAppAuth: cfg.EnableGitHubAppAuth,
91+
InstallationIDHeader: cfg.InstallationIDHeader,
92+
}
93+
94+
ghServer, err := NewMCPServer(mcpCfg)
95+
if err != nil {
96+
return fmt.Errorf("failed to create MCP server: %w", err)
97+
}
98+
99+
httpServer := server.NewStreamableHTTPServer(ghServer)
100+
101+
logrusLogger := logrus.New()
102+
if cfg.LogFilePath != "" {
103+
file, err := os.OpenFile(cfg.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
104+
if err != nil {
105+
return fmt.Errorf("failed to open log file: %w", err)
106+
}
107+
logrusLogger.SetLevel(logrus.DebugLevel)
108+
logrusLogger.SetOutput(file)
109+
} else {
110+
logrusLogger.SetLevel(logrus.InfoLevel)
111+
}
112+
113+
if cfg.Address == "" {
114+
cfg.Address = ":8080"
115+
}
116+
if cfg.MCPPath == "" {
117+
cfg.MCPPath = "/mcp"
118+
}
119+
if cfg.InstallationIDHeader == "" {
120+
cfg.InstallationIDHeader = "X-GitHub-Installation-ID"
121+
}
122+
123+
mux := http.NewServeMux()
124+
var handler http.Handler = httpServer
125+
126+
// Apply middlewares in the correct order: CORS first, then auth
127+
if cfg.EnableCORS {
128+
handler = corsMiddleware(handler)
129+
}
130+
if cfg.EnableGitHubAppAuth {
131+
handler = authMiddleware(handler, cfg.InstallationIDHeader, logrusLogger)
132+
}
133+
134+
mux.Handle(cfg.MCPPath, handler)
135+
136+
srv := &http.Server{
137+
Addr: cfg.Address,
138+
Handler: mux,
139+
}
140+
141+
if cfg.ExportTranslations {
142+
dumpTranslations()
143+
}
144+
145+
errC := make(chan error, 1)
146+
go func() {
147+
logrusLogger.Infof("Starting HTTP server on %s", cfg.Address)
148+
logrusLogger.Infof("MCP endpoint available at http://localhost%s%s", cfg.Address, cfg.MCPPath)
149+
if cfg.EnableGitHubAppAuth {
150+
logrusLogger.Infof("GitHub App authentication enabled with header: %s", cfg.InstallationIDHeader)
151+
}
152+
errC <- srv.ListenAndServe()
153+
}()
154+
155+
_, _ = fmt.Fprintf(os.Stderr, "GitHub MCP Server running on HTTP at %s\n", cfg.Address)
156+
_, _ = fmt.Fprintf(os.Stderr, "MCP endpoint: http://localhost%s%s\n", cfg.Address, cfg.MCPPath)
157+
if cfg.EnableGitHubAppAuth {
158+
_, _ = fmt.Fprintf(os.Stderr, "GitHub App authentication enabled with header: %s\n", cfg.InstallationIDHeader)
159+
}
160+
161+
select {
162+
case <-ctx.Done():
163+
logrusLogger.Infof("shutting down server...")
164+
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
165+
defer cancel()
166+
if err := srv.Shutdown(shutdownCtx); err != nil {
167+
logrusLogger.Errorf("error during server shutdown: %v", err)
168+
}
169+
case err := <-errC:
170+
if err != nil && err != http.ErrServerClosed {
171+
return fmt.Errorf("error running server: %w", err)
172+
}
173+
}
174+
175+
return nil
176+
}
177+
178+
// corsMiddleware adds CORS headers to allow cross-origin requests
179+
func corsMiddleware(next http.Handler) http.Handler {
180+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
181+
// Set CORS headers
182+
w.Header().Set("Access-Control-Allow-Origin", "*")
183+
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
184+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, Accept-Encoding, Accept-Language, Cache-Control, Connection, Host, Origin, Referer, User-Agent")
185+
186+
// Handle preflight requests
187+
if r.Method == "OPTIONS" {
188+
w.WriteHeader(http.StatusOK)
189+
return
190+
}
191+
192+
next.ServeHTTP(w, r)
193+
})
194+
}
195+
196+
// authMiddleware extracts installation IDs from custom headers and adds them to the request context
197+
func authMiddleware(next http.Handler, headerName string, logger *logrus.Logger) http.Handler {
198+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
199+
installationIDStr := r.Header.Get(headerName)
200+
if installationIDStr == "" {
201+
next.ServeHTTP(w, r)
202+
return
203+
}
204+
205+
installationID, err := strconv.ParseInt(installationIDStr, 10, 64)
206+
if err != nil {
207+
logger.Warnf("Invalid installation ID format in header %s", headerName)
208+
http.Error(w, "Invalid installation ID format", http.StatusBadRequest)
209+
return
210+
}
211+
212+
if installationID <= 0 {
213+
logger.Warnf("Invalid installation ID value: %d", installationID)
214+
http.Error(w, "Invalid installation ID value", http.StatusBadRequest)
215+
return
216+
}
217+
218+
ctx := context.WithValue(r.Context(), installationContextKey, installationID)
219+
r = r.WithContext(ctx)
220+
221+
if logger.GetLevel() == logrus.DebugLevel {
222+
logger.Debugf("Authenticated request with installation ID %d", installationID)
223+
} else {
224+
logger.Debug("Request authenticated with GitHub App installation")
225+
}
226+
227+
next.ServeHTTP(w, r)
228+
})
229+
}

0 commit comments

Comments
 (0)