From 1fc20c6392c4125ab5e5f9cad24c249897f98711 Mon Sep 17 00:00:00 2001 From: Smeet Patel Date: Wed, 13 Aug 2025 12:58:08 -0700 Subject: [PATCH] add heartbeat interval for GET requests to MCP server --- cmd/github-mcp-server/main.go | 7 +++++++ internal/ghmcp/http_server.go | 11 ++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cmd/github-mcp-server/main.go b/cmd/github-mcp-server/main.go index 2aa9ae2b9..568876fd7 100644 --- a/cmd/github-mcp-server/main.go +++ b/cmd/github-mcp-server/main.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "strings" + "time" "github.com/github/github-mcp-server/internal/ghmcp" "github.com/github/github-mcp-server/pkg/github" @@ -84,6 +85,9 @@ var ( return fmt.Errorf("failed to unmarshal toolsets: %w", err) } + // Pre-compute heartbeat interval + hbInterval, _ := time.ParseDuration(viper.GetString("http_heartbeat_interval")) + httpServerConfig := ghmcp.HttpServerConfig{ Version: version, Host: viper.GetString("host"), @@ -101,6 +105,7 @@ var ( AppPrivateKey: appPrivateKey, EnableGitHubAppAuth: enableGitHubAppAuth, InstallationIDHeader: viper.GetString("installation_id_header"), + HeartbeatInterval: hbInterval, } return ghmcp.RunHTTPServer(httpServerConfig) @@ -133,6 +138,7 @@ func init() { httpCmd.Flags().String("http-address", ":8080", "HTTP server address to bind to") httpCmd.Flags().String("http-mcp-path", "/mcp", "HTTP path for MCP endpoint") httpCmd.Flags().Bool("http-enable-cors", false, "Enable CORS for cross-origin requests") + httpCmd.Flags().String("http-heartbeat-interval", "15s", "Interval for SSE heartbeats on GET listener (e.g., 15s; set 0 to disable)") // Bind flags to viper _ = viper.BindPFlag("toolsets", rootCmd.PersistentFlags().Lookup("toolsets")) @@ -149,6 +155,7 @@ func init() { _ = viper.BindPFlag("http_address", httpCmd.Flags().Lookup("http-address")) _ = viper.BindPFlag("http_mcp_path", httpCmd.Flags().Lookup("http-mcp-path")) _ = viper.BindPFlag("http_enable_cors", httpCmd.Flags().Lookup("http-enable-cors")) + _ = viper.BindPFlag("http_heartbeat_interval", httpCmd.Flags().Lookup("http-heartbeat-interval")) // Add subcommands rootCmd.AddCommand(stdioCmd) diff --git a/internal/ghmcp/http_server.go b/internal/ghmcp/http_server.go index a5d9ddc05..d68708904 100644 --- a/internal/ghmcp/http_server.go +++ b/internal/ghmcp/http_server.go @@ -66,6 +66,10 @@ type HttpServerConfig struct { // Custom header name to read installation ID from (defaults to "X-GitHub-Installation-ID") InstallationIDHeader string + + // HeartbeatInterval controls how often the server sends SSE heartbeat pings on the GET listener + // Set to 0 to disable heartbeats + HeartbeatInterval time.Duration } const installationContextKey = "installation_id" @@ -96,7 +100,12 @@ func RunHTTPServer(cfg HttpServerConfig) error { return fmt.Errorf("failed to create MCP server: %w", err) } - httpServer := server.NewStreamableHTTPServer(ghServer) + // Configure the streamable HTTP server with optional heartbeat pings + var httpOpts []server.StreamableHTTPOption + if cfg.HeartbeatInterval > 0 { + httpOpts = append(httpOpts, server.WithHeartbeatInterval(cfg.HeartbeatInterval)) + } + httpServer := server.NewStreamableHTTPServer(ghServer, httpOpts...) logrusLogger := logrus.New() if cfg.LogFilePath != "" {