Skip to content

Conversation

@mbillow
Copy link
Contributor

@mbillow mbillow commented Nov 19, 2025

In order to allow third parties to build their own cluster providers, I'd like to propose a very small change to make the Manager initialization function newManager public. This allows people to easily extend the upstream package without having to fork it.

Currently, we can create our own implementation of Provider and a register a provider configuration, but the provider implementation itself needs to be able to initialize Managers for each cluster it wants to use, which is impossible since only the NewInClusterManager and NewKubeconfigManager functions are public.

A super simple example would look like:

pkg/custom_config.go

import (
	// ...others...
	"github.com/containers/kubernetes-mcp-server/pkg/config"
)

type Config struct {
	DemoConfig string `toml:"demo"`
}

func init() {
	// Register config parser with the upstream MCP server
	config.RegisterProviderConfig(ProviderStrategy, parseConfig)
}

func parseConfig(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (config.ProviderConfig, error) {
	// ...impl specific config parsing...
}

pkg/custom_provider.go

import (
	// ...others...
	"github.com/containers/kubernetes-mcp-server/pkg/config"
	"github.com/containers/kubernetes-mcp-server/pkg/kubernetes"
)

const ProviderStrategy = "custom"

type Provider struct {
	clusters map[string]*ClusterInfo
	managers map[string]*kubernetes.Manager
}
var _ kubernetes.Provider = &Provider{}

func init() {
	// Register this provider with the upstream MCP server
	kubernetes.RegisterProvider(ProviderStrategy, NewProvider)
}

func NewProvider(cfg *config.StaticConfig) (kubernetes.Provider, error) {
	p := &Provider{}
	p.fetchClusters()
	return p, nil
}

func (p *Provider) fetchClusters() error {
	p.clusters = company.magicClusterDiscovery()
}

func (p *Provider) managerForCluster(shortname string) (*kubernetes.Manager, error) {
	if m, ok := p.managers[shortname]; ok && m != nil {
		return m, nil
	}
	cluster, ok := p.clusters[shortname]
	if !ok {
		return nil, fmt.Errorf("cluster %s not found", shortname)
	}

	// we'd build the necessary clientcmd configs, etc. here
	// then we can call our newly public function
	m, err := kubernetes.NewManager(...)
	p.managers[shortname] = m
	return m, nil
}

// rest of the provider implementation where functions like `VerifyToken` call `p.managerForCluster`...

main.go

import (
	// ...others...

	// import mcp server
	"github.com/containers/kubernetes-mcp-server/pkg/kubernetes-mcp-server/cmd"

	// import our custom provider - this triggers init() which registers the provider
	_ "github.com/foo/bar/pkg/custom_provider"
)

func main() {
	flags := pflag.NewFlagSet("kubernetes-mcp-server", pflag.ExitOnError)
	pflag.CommandLine = flags

	root := cmd.NewMCPServer(genericiooptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr})
	if err := root.Execute(); err != nil {
		os.Exit(1)
	}
}

config.toml

cluster_provider_strategy = "custom"

[cluster_provider_configs.custom]
demo = "hello, world"

@mbillow mbillow changed the title Allow for the Creation of External Kubernetes Cluster Providers feat(kubernetes): public new manager to enable external providers Nov 19, 2025
@Cali0707 Cali0707 requested a review from manusa November 19, 2025 17:44
@manusa
Copy link
Member

manusa commented Nov 20, 2025

Hi @mbillow
Thanks for sending this, your use case looks very interesting and aligned with our goal of making this MCP server a good base to build other toolsets on top.
Let me evaluate how does this intersect with the work I'm doing in #473 and what are other alternatives we might want to consider to add support for your use case.

Signed-off-by: Marc Billow <mbillow@indeed.com>
@manusa manusa added this to the 0.1.0 milestone Nov 27, 2025
@manusa manusa force-pushed the public-new-manager branch from 3f990b6 to 82fc894 Compare November 27, 2025 14:10
Copy link
Member

@manusa manusa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added some validations to the NewManager function and rebased your branch.

LGTM, thx!

I think it does no harm to expose the NewManager function but it would be great if you could describe a little bit more how you're using this.

Signed-off-by: Marc Nuri <marc@marcnuri.com>
@manusa manusa requested a review from Cali0707 November 27, 2025 14:17
@mbillow
Copy link
Contributor Author

mbillow commented Nov 27, 2025

I think it does no harm to expose the NewManager function but it would be great if you could describe a little bit more how you're using this.

We are running the MCP server in a single cluster and want our users to be able to use it against our entire fleet. We have a central API that exposes connection info and metadata about our clusters. I wrote a ClusterProvider that queries this API and builds Managers for each cluster. This allows us to enumerate our clusters without having to inject some kind of generated kubeconfig or something similar. The code example in the PR description provides a pretty simple example of what we’re doing.

Generally, I’m trying to do it this way to avoid having to maintain a fork of the server. This way we can just use it as a package and our repo is just the internal plugins (cluster provider, config for that, etc.).

Copy link
Collaborator

@Cali0707 Cali0707 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks @mbillow !

@Cali0707 Cali0707 merged commit bb17628 into containers:main Nov 27, 2025
6 checks passed
@manusa
Copy link
Member

manusa commented Nov 28, 2025

Thanks for sharing @mbillow ❤️

We'll keep you in the loop for any changes to the Provider API

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants