diff --git a/cmd/github-mcp-server/main.go b/cmd/github-mcp-server/main.go index cad002666..73095764d 100644 --- a/cmd/github-mcp-server/main.go +++ b/cmd/github-mcp-server/main.go @@ -59,6 +59,23 @@ var ( return ghmcp.RunStdioServer(stdioServerConfig) }, } + + webCmd = &cobra.Command{ + Use: "web", + Short: "Start web server with login page", + Long: `Start a web server that serves a simple login page with username and password fields.`, + RunE: func(_ *cobra.Command, _ []string) error { + port := viper.GetString("port") + if port == "" { + port = "8080" + } + + webServerConfig := ghmcp.WebServerConfig{ + Port: port, + } + return ghmcp.RunWebServer(webServerConfig) + }, + } ) func init() { @@ -76,6 +93,9 @@ func init() { rootCmd.PersistentFlags().Bool("export-translations", false, "Save translations to a JSON file") rootCmd.PersistentFlags().String("gh-host", "", "Specify the GitHub hostname (for GitHub Enterprise etc.)") + // Add web-specific flags + webCmd.Flags().String("port", "8080", "Port to serve the web interface on") + // Bind flag to viper _ = viper.BindPFlag("toolsets", rootCmd.PersistentFlags().Lookup("toolsets")) _ = viper.BindPFlag("dynamic_toolsets", rootCmd.PersistentFlags().Lookup("dynamic-toolsets")) @@ -84,9 +104,11 @@ func init() { _ = viper.BindPFlag("enable-command-logging", rootCmd.PersistentFlags().Lookup("enable-command-logging")) _ = viper.BindPFlag("export-translations", rootCmd.PersistentFlags().Lookup("export-translations")) _ = viper.BindPFlag("host", rootCmd.PersistentFlags().Lookup("gh-host")) + _ = viper.BindPFlag("port", webCmd.Flags().Lookup("port")) // Add subcommands rootCmd.AddCommand(stdioCmd) + rootCmd.AddCommand(webCmd) } func initConfig() { diff --git a/internal/ghmcp/server.go b/internal/ghmcp/server.go index d993b130a..6816aa8bd 100644 --- a/internal/ghmcp/server.go +++ b/internal/ghmcp/server.go @@ -11,6 +11,7 @@ import ( "os/signal" "strings" "syscall" + "time" "github.com/github/github-mcp-server/pkg/errors" "github.com/github/github-mcp-server/pkg/github" @@ -251,6 +252,239 @@ func RunStdioServer(cfg StdioServerConfig) error { return nil } +type WebServerConfig struct { + // Port to serve the web interface on + Port string +} + +// RunWebServer starts a simple HTTP server with a login page +func RunWebServer(cfg WebServerConfig) error { + // Create app context + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + + mux := http.NewServeMux() + + // Serve the login page + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + w.Header().Set("Content-Type", "text/html") + _, _ = w.Write([]byte(loginPageHTML)) + }) + + // Serve basic CSS + mux.HandleFunc("/style.css", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "text/css") + _, _ = w.Write([]byte(loginPageCSS)) + }) + + // Handle login form submission (just echo back for now) + mux.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + username := r.FormValue("username") + _ = r.FormValue("password") // password not used in this basic implementation + + // For now, just display a simple response + w.Header().Set("Content-Type", "text/html") + fmt.Fprintf(w, ` + +
+Username: %s
+Password: ***
+Note: This is just a UI mockup. No actual authentication is performed.
+ Back to Login +This is a basic login interface for future authentication features.
+Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: