|
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