Skip to main content
Glama
clpi

CLP MCP - DevOps Infrastructure Server

Official
by clpi
kubernetes.ts13.9 kB
import { z } from "zod"; export const kubernetesTools = { validateK8sManifest: { name: "validate_k8s_manifest", title: "Validate Kubernetes Manifest", description: "Validate Kubernetes manifest for syntax and best practices", inputSchema: z.object({ content: z.string().describe("The Kubernetes manifest YAML content to validate"), }), handler: async ({ content }: { content: string }) => { const issues: string[] = []; const warnings: string[] = []; // Basic structure checks if (!content.includes("apiVersion:")) { issues.push("Missing 'apiVersion' field"); } if (!content.includes("kind:")) { issues.push("Missing 'kind' field"); } if (!content.includes("metadata:")) { issues.push("Missing 'metadata' field"); } // Best practices for Deployments if (content.includes("kind: Deployment")) { if (!content.includes("replicas:")) { warnings.push("Consider specifying replica count explicitly"); } if (!content.includes("strategy:")) { warnings.push("Consider specifying update strategy"); } if (!content.includes("resources:")) { warnings.push("IMPORTANT: Resources (limits/requests) should be defined"); } if (!content.includes("livenessProbe:") || !content.includes("readinessProbe:")) { warnings.push("Consider adding liveness and readiness probes"); } } // Security checks if (content.includes("privileged: true")) { issues.push("SECURITY: Privileged containers should be avoided"); } if (content.includes("hostNetwork: true")) { warnings.push("SECURITY: Using host network should be carefully considered"); } if (content.includes("image:") && !content.includes("imagePullPolicy:")) { warnings.push("Consider specifying imagePullPolicy"); } // Check for image tags if (content.match(/image:.*:latest/)) { warnings.push("Avoid using 'latest' tag in production"); } return { content: [ { type: "text" as const, text: JSON.stringify({ valid: issues.length === 0, issues, warnings, recommendations: [ "Use kubectl apply --dry-run for validation", "Set resource limits and requests", "Implement health checks", "Use ConfigMaps for configuration", "Use Secrets for sensitive data", "Add labels for organization", ], }, null, 2), } ] }; }, }, generateK8sManifest: { name: "generate_k8s_manifest", title: "Generate Kubernetes Manifest", description: "Generate a Kubernetes manifest template", inputSchema: z.object({ resourceType: z.enum([ "deployment", "service", "configmap", "secret", "ingress", "statefulset", "daemonset", "job", "cronjob", ]).describe("Type of Kubernetes resource"), name: z.string().describe("Resource name"), namespace: z.string().default("default").describe("Namespace"), options: z.record(z.any()).optional().describe("Additional options"), }), handler: async ({ resourceType, name, namespace, options }: { resourceType: string; name: string; namespace: string; options?: Record<string, any>; }) => { const manifest = generateK8sResource(resourceType, name, namespace, options || {}); return { content: [ { type: "text" as const, text: manifest, } ] }; }, }, generateHelm: { name: "generate_helm_chart", title: "Generate Helm Chart", description: "Generate a Helm chart structure", inputSchema: z.object({ chartName: z.string().describe("Name of the Helm chart"), appVersion: z.string().default("1.0.0").describe("Application version"), description: z.string().describe("Chart description"), }), handler: async ({ chartName, appVersion, description }: { chartName: string; appVersion: string; description: string; }) => { const chartYaml = `apiVersion: v2 name: ${chartName} description: ${description} type: application version: 0.1.0 appVersion: "${appVersion}" `; const valuesYaml = `# Default values for ${chartName} replicaCount: 1 image: repository: nginx pullPolicy: IfNotPresent tag: "" service: type: ClusterIP port: 80 ingress: enabled: false className: "" annotations: {} hosts: - host: chart-example.local paths: - path: / pathType: ImplementationSpecific tls: [] resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi autoscaling: enabled: false minReplicas: 1 maxReplicas: 100 targetCPUUtilizationPercentage: 80 `; const deploymentYaml = `apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "${chartName}.fullname" . }} labels: {{- include "${chartName}.labels" . | nindent 4 }} spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: {{- include "${chartName}.selectorLabels" . | nindent 6 }} template: metadata: labels: {{- include "${chartName}.selectorLabels" . | nindent 8 }} spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: 80 protocol: TCP resources: {{- toYaml .Values.resources | nindent 12 }} `; return { content: [ { type: "text" as const, text: JSON.stringify({ "Chart.yaml": chartYaml, "values.yaml": valuesYaml, "templates/deployment.yaml": deploymentYaml, "templates/_helpers.tpl": generateHelmHelpers(chartName), }, null, 2), } ] }; }, }, analyzeK8sResources: { name: "analyze_k8s_resources", title: "Analyze Kubernetes Resources", description: "Analyze Kubernetes resources for optimization opportunities", inputSchema: z.object({ manifests: z.array(z.string()).describe("Array of Kubernetes manifest contents"), }), handler: async ({ manifests }: { manifests: string[] }) => { const analysis = { totalResources: manifests.length, resourceTypes: new Set<string>(), missingResources: [] as string[], missingLimits: [] as string[], missingProbes: [] as string[], securityIssues: [] as string[], recommendations: [] as string[], }; manifests.forEach((manifest, idx) => { const lines = manifest.split('\n'); const kind = lines.find(l => l.includes("kind:"))?.split(":")[1]?.trim(); if (kind) analysis.resourceTypes.add(kind); if (!manifest.includes("resources:")) { analysis.missingLimits.push(`Resource ${idx + 1}: Missing resource limits`); } if (manifest.includes("kind: Deployment")) { if (!manifest.includes("livenessProbe:")) { analysis.missingProbes.push(`Resource ${idx + 1}: Missing liveness probe`); } if (!manifest.includes("readinessProbe:")) { analysis.missingProbes.push(`Resource ${idx + 1}: Missing readiness probe`); } } if (manifest.includes("privileged: true")) { analysis.securityIssues.push(`Resource ${idx + 1}: Uses privileged mode`); } }); // Check for common missing resources if (!Array.from(analysis.resourceTypes).includes("Service")) { analysis.missingResources.push("No Service resource found"); } // Generate recommendations if (analysis.missingLimits.length > 0) { analysis.recommendations.push("Add resource limits to prevent resource exhaustion"); } if (analysis.missingProbes.length > 0) { analysis.recommendations.push("Add health probes for better reliability"); } if (analysis.securityIssues.length > 0) { analysis.recommendations.push("Review security settings and apply principle of least privilege"); } return { content: [ { type: "text" as const, text: JSON.stringify({ ...analysis, resourceTypes: Array.from(analysis.resourceTypes), }, null, 2), } ] }; }, }, generateKustomization: { name: "generate_kustomization", title: "Generate Kustomization File", description: "Generate a Kustomization file for Kubernetes manifests", inputSchema: z.object({ resources: z.array(z.string()).describe("List of resource files"), namespace: z.string().optional().describe("Target namespace"), namePrefix: z.string().optional().describe("Name prefix for resources"), commonLabels: z.record(z.string()).optional().describe("Common labels"), }), handler: async ({ resources, namespace, namePrefix, commonLabels }: { resources: string[]; namespace?: string; namePrefix?: string; commonLabels?: Record<string, string>; }) => { let kustomization = "apiVersion: kustomize.config.k8s.io/v1beta1\n"; kustomization += "kind: Kustomization\n\n"; if (namespace) { kustomization += `namespace: ${namespace}\n\n`; } if (namePrefix) { kustomization += `namePrefix: ${namePrefix}-\n\n`; } if (commonLabels) { kustomization += "commonLabels:\n"; Object.entries(commonLabels).forEach(([key, value]) => { kustomization += ` ${key}: ${value}\n`; }); kustomization += "\n"; } kustomization += "resources:\n"; resources.forEach(resource => { kustomization += ` - ${resource}\n`; }); return { content: [ { type: "text" as const, text: kustomization, } ] }; }, }, }; function generateK8sResource(resourceType: string, name: string, namespace: string, options: Record<string, any>): string { const templates: Record<string, string> = { deployment: `apiVersion: apps/v1 kind: Deployment metadata: name: ${name} namespace: ${namespace} labels: app: ${name} spec: replicas: ${options.replicas || 1} selector: matchLabels: app: ${name} template: metadata: labels: app: ${name} spec: containers: - name: ${name} image: ${options.image || 'nginx:latest'} ports: - containerPort: ${options.port || 80} resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m" livenessProbe: httpGet: path: /healthz port: ${options.port || 80} initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: ${options.port || 80} initialDelaySeconds: 5 periodSeconds: 5 `, service: `apiVersion: v1 kind: Service metadata: name: ${name} namespace: ${namespace} spec: selector: app: ${name} ports: - protocol: TCP port: ${options.port || 80} targetPort: ${options.targetPort || options.port || 80} type: ${options.type || 'ClusterIP'} `, configmap: `apiVersion: v1 kind: ConfigMap metadata: name: ${name} namespace: ${namespace} data: # Add your configuration key-value pairs here example.property: "value" `, secret: `apiVersion: v1 kind: Secret metadata: name: ${name} namespace: ${namespace} type: Opaque data: # Values must be base64 encoded # example: ZXhhbXBsZS12YWx1ZQ== `, ingress: `apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ${name} namespace: ${namespace} annotations: kubernetes.io/ingress.class: nginx spec: rules: - host: ${options.host || 'example.com'} http: paths: - path: / pathType: Prefix backend: service: name: ${name} port: number: ${options.port || 80} `, }; return templates[resourceType] || `# ${resourceType} template not available`; } function generateHelmHelpers(chartName: string): string { return `{{/* Expand the name of the chart. */}} {{- define "${chartName}.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Create a default fully qualified app name. */}} {{- define "${chartName}.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} {{- $name := default .Chart.Name .Values.nameOverride }} {{- if contains $name .Release.Name }} {{- .Release.Name | trunc 63 | trimSuffix "-" }} {{- else }} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} {{- end }} {{- end }} {{- end }} {{/* Common labels */}} {{- define "${chartName}.labels" -}} helm.sh/chart: {{ include "${chartName}.chart" . }} {{ include "${chartName}.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* Selector labels */}} {{- define "${chartName}.selectorLabels" -}} app.kubernetes.io/name: {{ include "${chartName}.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} `; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/clpi/clp-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server