diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 55123b7..6322fdf 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -22,7 +22,8 @@ serverscom-mcp/ ├── networks.go # server networks (IPv4/IPv6, gateway/route) ├── l2_segments.go # L2 segment management ├── drives.go # drive slot listing - └── rbs.go # Remote Block Storage volumes + ├── rbs.go # Remote Block Storage volumes + └── kubernetes_clusters.go # Kubernetes cluster and node inspection ``` ## How to Add a New Tool @@ -129,8 +130,9 @@ API tag groups map to tool files as follows: | Locations, Order options | `locations.go` | | L2 Segment | `l2_segments.go` | | Remote Block Storage (RBS) | `rbs.go` | +| Kubernetes Cluster | `kubernetes_clusters.go` | -Not yet implemented: Cloud Computing, Cloud Block Storage, Load Balancers, Network Pool, Kubernetes Cluster, Racks, Billing/Account, Metrics. +Not yet implemented: Cloud Computing, Cloud Block Storage, Load Balancers, Network Pool, Racks, Billing/Account, Metrics. ## Async Operations diff --git a/README.md b/README.md index 56198d4..d209c6c 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ SC_TOKEN=your-api-token npx @servers.com/mcp ## Available Tools -73 tools across 6 categories — see **[TOOLS.md](TOOLS.md)** for the full reference. +78 tools across 7 categories — see **[TOOLS.md](TOOLS.md)** for the full reference. ## Async Operations diff --git a/TOOLS.md b/TOOLS.md index c143249..8323f56 100644 --- a/TOOLS.md +++ b/TOOLS.md @@ -156,3 +156,19 @@ L2 segments unite dedicated servers within a location group into a single broadc **Member modes:** `native` (no OS config needed, 1 per interface per type) · `trunk` (requires VLAN sub-interface on server OS, up to 16 per type) **Limits per server:** 1 native public + 1 native private + 16 public trunk + 16 private trunk = 34 max + +## Kubernetes Clusters + +Managed Kubernetes clusters on Servers.com infrastructure. Clusters are provisioned through the Servers.com portal; these tools provide read and label-management access. + +| Tool | Description | +|---|---| +| `list_kubernetes_clusters` | List all Kubernetes clusters in the account | +| `get_kubernetes_cluster` | Get cluster details: status, location, labels | +| `update_kubernetes_cluster` | Update cluster labels (replaces all existing labels) | +| `list_kubernetes_cluster_nodes` | List all nodes in a cluster: role, type, status, IP addresses | +| `get_kubernetes_cluster_node` | Get full details of a specific cluster node | + +**Node roles:** `master` · `node` + +**Node types:** `cloud` · `baremetal` diff --git a/internal/tools/kubernetes_clusters.go b/internal/tools/kubernetes_clusters.go new file mode 100644 index 0000000..80ff6b7 --- /dev/null +++ b/internal/tools/kubernetes_clusters.go @@ -0,0 +1,102 @@ +package tools + +import ( + "context" + + "github.com/modelcontextprotocol/go-sdk/mcp" + serverscom "github.com/serverscom/serverscom-go-client/pkg" +) + +func registerKubernetesClusterTools(server *mcp.Server, h *handler) { + registerListKubernetesClusters(server, h) + registerGetKubernetesCluster(server, h) + registerUpdateKubernetesCluster(server, h) + registerListKubernetesClusterNodes(server, h) + registerGetKubernetesClusterNode(server, h) +} + +type kubernetesClusterIDArgs struct { + ClusterID string `json:"cluster_id" jsonschema:"cluster ID,required"` +} + +type updateKubernetesClusterArgs struct { + ClusterID string `json:"cluster_id" jsonschema:"cluster ID,required"` + Labels map[string]string `json:"labels,omitempty" jsonschema:"key-value labels (replaces all existing labels)"` +} + +type listKubernetesClusterNodesArgs struct { + ClusterID string `json:"cluster_id" jsonschema:"cluster ID,required"` +} + +type kubernetesClusterNodeArgs struct { + ClusterID string `json:"cluster_id" jsonschema:"cluster ID,required"` + NodeID string `json:"node_id" jsonschema:"node ID,required"` +} + +func registerListKubernetesClusters(server *mcp.Server, h *handler) { + mcp.AddTool(server, &mcp.Tool{ + Name: "list_kubernetes_clusters", + Description: "List all Kubernetes clusters in the account. Returns cluster ID, name, status, location_id, labels, and timestamps for each cluster.", + }, func(ctx context.Context, req *mcp.CallToolRequest, _ struct{}) (*mcp.CallToolResult, any, error) { + clusters, err := h.client.KubernetesClusters.Collection().Collect(ctx) + if err != nil { + return errorResult(err), nil, nil + } + return jsonResult(clusters) + }) +} + +func registerGetKubernetesCluster(server *mcp.Server, h *handler) { + mcp.AddTool(server, &mcp.Tool{ + Name: "get_kubernetes_cluster", + Description: "Get details of a Kubernetes cluster by ID. Returns name, status, location_id, labels, and timestamps.", + }, func(ctx context.Context, req *mcp.CallToolRequest, args kubernetesClusterIDArgs) (*mcp.CallToolResult, any, error) { + cluster, err := h.client.KubernetesClusters.Get(ctx, args.ClusterID) + if err != nil { + return errorResult(err), nil, nil + } + return jsonResult(cluster) + }) +} + +func registerUpdateKubernetesCluster(server *mcp.Server, h *handler) { + mcp.AddTool(server, &mcp.Tool{ + Name: "update_kubernetes_cluster", + Description: "Update labels on a Kubernetes cluster. The provided labels replace all existing labels on the cluster.", + }, func(ctx context.Context, req *mcp.CallToolRequest, args updateKubernetesClusterArgs) (*mcp.CallToolResult, any, error) { + input := serverscom.KubernetesClusterUpdateInput{ + Labels: args.Labels, + } + cluster, err := h.client.KubernetesClusters.Update(ctx, args.ClusterID, input) + if err != nil { + return errorResult(err), nil, nil + } + return jsonResult(cluster) + }) +} + +func registerListKubernetesClusterNodes(server *mcp.Server, h *handler) { + mcp.AddTool(server, &mcp.Tool{ + Name: "list_kubernetes_cluster_nodes", + Description: "List all nodes in a Kubernetes cluster. Each node includes: role (node/master), type (cloud/baremetal), status, private and public IPv4 addresses, hostname, configuration, and ref_id.", + }, func(ctx context.Context, req *mcp.CallToolRequest, args listKubernetesClusterNodesArgs) (*mcp.CallToolResult, any, error) { + nodes, err := h.client.KubernetesClusters.Nodes(args.ClusterID).Collect(ctx) + if err != nil { + return errorResult(err), nil, nil + } + return jsonResult(nodes) + }) +} + +func registerGetKubernetesClusterNode(server *mcp.Server, h *handler) { + mcp.AddTool(server, &mcp.Tool{ + Name: "get_kubernetes_cluster_node", + Description: "Get details of a specific node in a Kubernetes cluster. Returns role, type, status, IP addresses, hostname, configuration, ref_id, labels, and timestamps. Use list_kubernetes_cluster_nodes to find node IDs.", + }, func(ctx context.Context, req *mcp.CallToolRequest, args kubernetesClusterNodeArgs) (*mcp.CallToolResult, any, error) { + node, err := h.client.KubernetesClusters.GetNode(ctx, args.ClusterID, args.NodeID) + if err != nil { + return errorResult(err), nil, nil + } + return jsonResult(node) + }) +} diff --git a/internal/tools/tools.go b/internal/tools/tools.go index 94f10c4..5a20caa 100644 --- a/internal/tools/tools.go +++ b/internal/tools/tools.go @@ -34,6 +34,7 @@ func Register(server *mcp.Server, token, endpoint, version string) { registerNetworkTools(server, h) registerL2SegmentTools(server, h) registerRBSTools(server, h) + registerKubernetesClusterTools(server, h) } // jsonResult serialises v as indented JSON and wraps it in a TextContent result.