Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 43 additions & 13 deletions pkg/kubernetes-mcp-server/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,18 @@ Kubernetes Model Context Protocol (MCP) server
# TODO: add more examples`,
Run: func(cmd *cobra.Command, args []string) {
initLogging()
klog.V(5).Infof("Starting kubernetes-mcp-server")
profile := mcp.ProfileFromString(viper.GetString("profile"))
if profile == nil {
fmt.Printf("Invalid profile name: %s, valid names are: %s\n", viper.GetString("profile"), mcp.ProfileNames)
os.Exit(1)
}
klog.V(1).Infof("Starting kubernetes-mcp-server with profile: %s", profile.GetName())
if viper.GetBool("version") {
fmt.Println(version.Version)
return
}
mcpServer, err := mcp.NewSever(mcp.Configuration{
Profile: profile,
Kubeconfig: viper.GetString("kubeconfig"),
})
if err != nil {
Expand All @@ -70,27 +76,51 @@ Kubernetes Model Context Protocol (MCP) server
},
}

func init() {
rootCmd.Flags().BoolP("version", "v", false, "Print version information and quit")
rootCmd.Flags().IntP("log-level", "", 0, "Set the log level (from 0 to 9)")
rootCmd.Flags().IntP("sse-port", "", 0, "Start a SSE server on the specified port")
rootCmd.Flags().StringP("sse-base-url", "", "", "SSE public base URL to use when sending the endpoint message (e.g. https://example.com)")
rootCmd.Flags().StringP("kubeconfig", "", "", "Path to the kubeconfig file to use for authentication")
_ = viper.BindPFlags(rootCmd.Flags())
}

func Execute() {
if err := rootCmd.Execute(); err != nil {
panic(err)
klog.Errorf("Failed to execute command: %s", err)
os.Exit(1)
}
}

func initLogging() {
logger := textlogger.NewLogger(textlogger.NewConfig(textlogger.Output(os.Stdout)))
klog.SetLoggerWithOptions(logger)
flagSet := flag.NewFlagSet("kubernetes-mcp-server", flag.ContinueOnError)
klog.InitFlags(flagSet)
loggerOptions := []textlogger.ConfigOption{textlogger.Output(os.Stdout)}
if logLevel := viper.GetInt("log-level"); logLevel >= 0 {
loggerOptions = append(loggerOptions, textlogger.Verbosity(logLevel))
_ = flagSet.Parse([]string{"--v", strconv.Itoa(logLevel)})
}
logger := textlogger.NewLogger(textlogger.NewConfig(loggerOptions...))
klog.SetLoggerWithOptions(logger)
}

type profileFlag struct {
mcp.Profile
}

func (p *profileFlag) String() string {
return p.GetName()
}

func (p *profileFlag) Set(v string) error {
p.Profile = mcp.ProfileFromString(v)
if p.Profile != nil {
return nil
}
return fmt.Errorf("invalid profile name: %s, valid names are: %s", v, mcp.ProfileNames)
}

func (p *profileFlag) Type() string {
return "profile"
}

func init() {
rootCmd.Flags().BoolP("version", "v", false, "Print version information and quit")
rootCmd.Flags().IntP("log-level", "", 0, "Set the log level (from 0 to 9)")
rootCmd.Flags().IntP("sse-port", "", 0, "Start a SSE server on the specified port")
rootCmd.Flags().StringP("sse-base-url", "", "", "SSE public base URL to use when sending the endpoint message (e.g. https://example.com)")
rootCmd.Flags().StringP("kubeconfig", "", "", "Path to the kubeconfig file to use for authentication")
rootCmd.Flags().Var(&profileFlag{&mcp.FullProfile{}}, "profile", "MCP profile to use")
_ = viper.BindPFlags(rootCmd.Flags())
}
2 changes: 1 addition & 1 deletion pkg/mcp/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (c *mcpContext) beforeEach(t *testing.T) {
c.ctx, c.cancel = context.WithCancel(context.Background())
c.tempDir = t.TempDir()
c.withKubeConfig(nil)
if c.mcpServer, err = NewSever(Configuration{}); err != nil {
if c.mcpServer, err = NewSever(Configuration{Profile: &FullProfile{}}); err != nil {
t.Fatal(err)
return
}
Expand Down
11 changes: 2 additions & 9 deletions pkg/mcp/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import (
"github.com/manusa/kubernetes-mcp-server/pkg/version"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"slices"
)

type Configuration struct {
Profile Profile
Kubeconfig string
}

Expand Down Expand Up @@ -43,14 +43,7 @@ func (s *Server) reloadKubernetesClient() error {
return err
}
s.k = k
s.server.SetTools(slices.Concat(
s.initConfiguration(),
s.initEvents(),
s.initNamespaces(),
s.initPods(),
s.initResources(),
s.initHelm(),
)...)
s.server.SetTools(s.configuration.Profile.GetTools(s)...)
return nil
}

Expand Down
53 changes: 53 additions & 0 deletions pkg/mcp/profiles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package mcp

import (
"github.com/mark3labs/mcp-go/server"
"slices"
)

var Profiles = []Profile{
&FullProfile{},
}

var ProfileNames []string

type Profile interface {
GetName() string
GetDescription() string
GetTools(s *Server) []server.ServerTool
}

func ProfileFromString(name string) Profile {
for _, profile := range Profiles {
if profile.GetName() == name {
return profile
}
}
return nil
}

type FullProfile struct{}

func (p *FullProfile) GetName() string {
return "full"
}
func (p *FullProfile) GetDescription() string {
return "Complete profile with all tools and extended outputs"
}
func (p *FullProfile) GetTools(s *Server) []server.ServerTool {
return slices.Concat(
s.initConfiguration(),
s.initEvents(),
s.initNamespaces(),
s.initPods(),
s.initResources(),
s.initHelm(),
)
}

func init() {
ProfileNames = make([]string, 0)
for _, profile := range Profiles {
ProfileNames = append(ProfileNames, profile.GetName())
}
}
Loading