|
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 |
|
@@ -198,23 +207,27 @@ func (m *MCPServerOptions) Run() error { |
198 | 207 | } |
199 | 208 | defer mcpServer.Close() |
200 | 209 |
|
201 | | - ctx := context.Background() |
202 | | - |
203 | | - if m.StaticConfig.SSEPort > 0 { |
204 | | - sseServer := mcpServer.ServeSse(m.StaticConfig.SSEBaseURL) |
205 | | - defer func() { _ = sseServer.Shutdown(ctx) }() |
206 | | - klog.V(0).Infof("SSE server starting on port %d and path /sse", m.StaticConfig.SSEPort) |
207 | | - if err := sseServer.Start(fmt.Sprintf(":%d", m.StaticConfig.SSEPort)); err != nil { |
208 | | - return fmt.Errorf("failed to start SSE server: %w\n", err) |
| 210 | + if m.StaticConfig.Port != "" { |
| 211 | + mux := http.NewServeMux() |
| 212 | + httpServer := &http.Server{ |
| 213 | + Addr: ":" + m.StaticConfig.Port, |
| 214 | + Handler: mux, |
209 | 215 | } |
210 | | - } |
211 | 216 |
|
212 | | - if m.StaticConfig.HTTPPort > 0 { |
213 | | - httpServer := mcpServer.ServeHTTP() |
214 | | - klog.V(0).Infof("Streaming HTTP server starting on port %d and path /mcp", m.StaticConfig.HTTPPort) |
215 | | - if err := httpServer.Start(fmt.Sprintf(":%d", m.StaticConfig.HTTPPort)); err != nil { |
216 | | - return fmt.Errorf("failed to start streaming HTTP server: %w\n", err) |
| 217 | + sseServer := mcpServer.ServeSse(m.SSEBaseUrl, httpServer) |
| 218 | + streamableHttpServer := mcpServer.ServeHTTP(httpServer) |
| 219 | + mux.Handle("/sse", sseServer) |
| 220 | + mux.Handle("/message", sseServer) |
| 221 | + mux.Handle("/mcp", streamableHttpServer) |
| 222 | + mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { |
| 223 | + w.WriteHeader(http.StatusOK) |
| 224 | + }) |
| 225 | + |
| 226 | + klog.V(0).Infof("Streaming and SSE HTTP servers starting on port %s and paths /mcp, /sse, /message", m.StaticConfig.Port) |
| 227 | + if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { |
| 228 | + return err |
217 | 229 | } |
| 230 | + return nil |
218 | 231 | } |
219 | 232 |
|
220 | 233 | if err := mcpServer.ServeStdio(); err != nil && !errors.Is(err, context.Canceled) { |
|
0 commit comments