|  | 
| 5 | 5 | 	"errors" | 
| 6 | 6 | 	"flag" | 
| 7 | 7 | 	"fmt" | 
|  | 8 | +	"net/http" | 
| 8 | 9 | 	"strconv" | 
| 9 | 10 | 	"strings" | 
| 10 | 11 | 
 | 
| @@ -35,16 +36,17 @@ kubernetes-mcp-server --version | 
| 35 | 36 | kubernetes-mcp-server | 
| 36 | 37 | 
 | 
| 37 | 38 | # start a SSE server on port 8080 | 
| 38 |  | -kubernetes-mcp-server --sse-port 8080 | 
|  | 39 | +kubernetes-mcp-server --port 8080 | 
| 39 | 40 | 
 | 
| 40 | 41 | # start a SSE server on port 8443 with a public HTTPS host of example.com | 
| 41 |  | -kubernetes-mcp-server --sse-port 8443 --sse-base-url https://example.com:8443 | 
|  | 42 | +kubernetes-mcp-server --port 8443 --sse-base-url https://example.com:8443 | 
| 42 | 43 | `)) | 
| 43 | 44 | ) | 
| 44 | 45 | 
 | 
| 45 | 46 | type MCPServerOptions struct { | 
| 46 | 47 | 	Version            bool | 
| 47 | 48 | 	LogLevel           int | 
|  | 49 | +	Port               string | 
| 48 | 50 | 	SSEPort            int | 
| 49 | 51 | 	HttpPort           int | 
| 50 | 52 | 	SSEBaseUrl         string | 
| @@ -95,7 +97,10 @@ func NewMCPServer(streams genericiooptions.IOStreams) *cobra.Command { | 
| 95 | 97 | 	cmd.Flags().IntVar(&o.LogLevel, "log-level", o.LogLevel, "Set the log level (from 0 to 9)") | 
| 96 | 98 | 	cmd.Flags().StringVar(&o.ConfigPath, "config", o.ConfigPath, "Path of the config file. Each profile has its set of defaults.") | 
| 97 | 99 | 	cmd.Flags().IntVar(&o.SSEPort, "sse-port", o.SSEPort, "Start a SSE server on the specified port") | 
|  | 100 | +	cmd.Flag("sse-port").Deprecated = "Use --port instead" | 
| 98 | 101 | 	cmd.Flags().IntVar(&o.HttpPort, "http-port", o.HttpPort, "Start a streamable HTTP server on the specified port") | 
|  | 102 | +	cmd.Flag("http-port").Deprecated = "Use --port instead" | 
|  | 103 | +	cmd.Flags().StringVar(&o.Port, "port", o.Port, "Start a streamable HTTP and SSE HTTP server on the specified port (e.g. 8080)") | 
| 99 | 104 | 	cmd.Flags().StringVar(&o.SSEBaseUrl, "sse-base-url", o.SSEBaseUrl, "SSE public base URL to use when sending the endpoint message (e.g. https://example.com)") | 
| 100 | 105 | 	cmd.Flags().StringVar(&o.Kubeconfig, "kubeconfig", o.Kubeconfig, "Path to the kubeconfig file to use for authentication") | 
| 101 | 106 | 	cmd.Flags().StringVar(&o.Profile, "profile", o.Profile, "MCP profile to use (one of: "+strings.Join(mcp.ProfileNames, ", ")+")") | 
| @@ -126,11 +131,12 @@ func (m *MCPServerOptions) loadFlags(cmd *cobra.Command) { | 
| 126 | 131 | 	if cmd.Flag("log-level").Changed { | 
| 127 | 132 | 		m.StaticConfig.LogLevel = m.LogLevel | 
| 128 | 133 | 	} | 
| 129 |  | -	if cmd.Flag("sse-port").Changed { | 
| 130 |  | -		m.StaticConfig.SSEPort = m.SSEPort | 
| 131 |  | -	} | 
| 132 |  | -	if cmd.Flag("http-port").Changed { | 
| 133 |  | -		m.StaticConfig.HTTPPort = m.HttpPort | 
|  | 134 | +	if cmd.Flag("port").Changed { | 
|  | 135 | +		m.StaticConfig.Port = m.Port | 
|  | 136 | +	} else if cmd.Flag("sse-port").Changed { | 
|  | 137 | +		m.StaticConfig.Port = strconv.Itoa(m.SSEPort) | 
|  | 138 | +	} else if cmd.Flag("http-port").Changed { | 
|  | 139 | +		m.StaticConfig.Port = strconv.Itoa(m.HttpPort) | 
| 134 | 140 | 	} | 
| 135 | 141 | 	if cmd.Flag("sse-base-url").Changed { | 
| 136 | 142 | 		m.StaticConfig.SSEBaseURL = m.SSEBaseUrl | 
| @@ -162,6 +168,9 @@ func (m *MCPServerOptions) initializeLogging() { | 
| 162 | 168 | } | 
| 163 | 169 | 
 | 
| 164 | 170 | func (m *MCPServerOptions) Validate() error { | 
|  | 171 | +	if m.Port != "" && (m.SSEPort > 0 || m.HttpPort > 0) { | 
|  | 172 | +		return fmt.Errorf("--port is mutually exclusive with deprecated --http-port and --sse-port flags") | 
|  | 173 | +	} | 
| 165 | 174 | 	return nil | 
| 166 | 175 | } | 
| 167 | 176 | 
 | 
| @@ -195,23 +204,27 @@ func (m *MCPServerOptions) Run() error { | 
| 195 | 204 | 	} | 
| 196 | 205 | 	defer mcpServer.Close() | 
| 197 | 206 | 
 | 
| 198 |  | -	ctx := context.Background() | 
| 199 |  | - | 
| 200 |  | -	if m.StaticConfig.SSEPort > 0 { | 
| 201 |  | -		sseServer := mcpServer.ServeSse(m.StaticConfig.SSEBaseURL) | 
| 202 |  | -		defer func() { _ = sseServer.Shutdown(ctx) }() | 
| 203 |  | -		klog.V(0).Infof("SSE server starting on port %d and path /sse", m.StaticConfig.SSEPort) | 
| 204 |  | -		if err := sseServer.Start(fmt.Sprintf(":%d", m.StaticConfig.SSEPort)); err != nil { | 
| 205 |  | -			return fmt.Errorf("failed to start SSE server: %w\n", err) | 
|  | 207 | +	if m.StaticConfig.Port != "" { | 
|  | 208 | +		mux := http.NewServeMux() | 
|  | 209 | +		httpServer := &http.Server{ | 
|  | 210 | +			Addr:    ":" + m.StaticConfig.Port, | 
|  | 211 | +			Handler: mux, | 
| 206 | 212 | 		} | 
| 207 |  | -	} | 
| 208 | 213 | 
 | 
| 209 |  | -	if m.StaticConfig.HTTPPort > 0 { | 
| 210 |  | -		httpServer := mcpServer.ServeHTTP() | 
| 211 |  | -		klog.V(0).Infof("Streaming HTTP server starting on port %d and path /mcp", m.StaticConfig.HTTPPort) | 
| 212 |  | -		if err := httpServer.Start(fmt.Sprintf(":%d", m.StaticConfig.HTTPPort)); err != nil { | 
| 213 |  | -			return fmt.Errorf("failed to start streaming HTTP server: %w\n", err) | 
|  | 214 | +		sseServer := mcpServer.ServeSse(m.SSEBaseUrl, httpServer) | 
|  | 215 | +		streamableHttpServer := mcpServer.ServeHTTP(httpServer) | 
|  | 216 | +		mux.Handle("/sse", sseServer) | 
|  | 217 | +		mux.Handle("/message", sseServer) | 
|  | 218 | +		mux.Handle("/mcp", streamableHttpServer) | 
|  | 219 | +		mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { | 
|  | 220 | +			w.WriteHeader(http.StatusOK) | 
|  | 221 | +		}) | 
|  | 222 | + | 
|  | 223 | +		klog.V(0).Infof("Streaming and SSE HTTP servers starting on port %s and paths /mcp, /sse, /message", m.StaticConfig.Port) | 
|  | 224 | +		if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { | 
|  | 225 | +			return err | 
| 214 | 226 | 		} | 
|  | 227 | +		return nil | 
| 215 | 228 | 	} | 
| 216 | 229 | 
 | 
| 217 | 230 | 	if err := mcpServer.ServeStdio(); err != nil && !errors.Is(err, context.Canceled) { | 
|  | 
0 commit comments