#
tokens: 48660/50000 13/82 files (page 3/5)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 3 of 5. Use http://codebase.md/blankcut/kubernetes-mcp-server?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitignore
├── docs
│   ├── .astro
│   │   ├── collections
│   │   │   └── docs.schema.json
│   │   ├── content-assets.mjs
│   │   ├── content-modules.mjs
│   │   ├── content.d.ts
│   │   ├── data-store.json
│   │   ├── settings.json
│   │   └── types.d.ts
│   ├── .gitignore
│   ├── astro.config.mjs
│   ├── package-lock.json
│   ├── package.json
│   ├── public
│   │   └── images
│   │       └── logo.svg
│   ├── README.md
│   ├── src
│   │   ├── components
│   │   │   ├── CodeBlock.astro
│   │   │   ├── DocSidebar.astro
│   │   │   ├── Footer.astro
│   │   │   ├── Header.astro
│   │   │   ├── HeadSEO.astro
│   │   │   ├── Search.astro
│   │   │   ├── Sidebar.astro
│   │   │   └── TableOfContents.astro
│   │   ├── content
│   │   │   ├── config.ts
│   │   │   └── docs
│   │   │       ├── api-overview.md
│   │   │       ├── configuration.md
│   │   │       ├── installation.md
│   │   │       ├── introduction.md
│   │   │       ├── model-context-protocol.md
│   │   │       ├── quick-start.md
│   │   │       └── troubleshooting-resources.md
│   │   ├── env.d.ts
│   │   ├── layouts
│   │   │   ├── BaseLayout.astro
│   │   │   └── DocLayout.astro
│   │   ├── pages
│   │   │   ├── [...slug].astro
│   │   │   ├── 404.astro
│   │   │   ├── docs
│   │   │   │   └── index.astro
│   │   │   ├── docs-test.astro
│   │   │   ├── examples
│   │   │   │   └── index.astro
│   │   │   └── index.astro
│   │   └── styles
│   │       └── global.css
│   ├── tailwind.config.cjs
│   └── tsconfig.json
├── go.mod
├── kubernetes-claude-mcp
│   ├── .gitignore
│   ├── cmd
│   │   └── server
│   │       └── main.go
│   ├── docker-compose.yml
│   ├── Dockerfile
│   ├── go.mod
│   ├── go.sum
│   ├── internal
│   │   ├── api
│   │   │   ├── namespace_routes.go
│   │   │   ├── routes.go
│   │   │   └── server.go
│   │   ├── argocd
│   │   │   ├── applications.go
│   │   │   ├── client.go
│   │   │   └── history.go
│   │   ├── auth
│   │   │   ├── credentials.go
│   │   │   ├── secrets.go
│   │   │   └── vault.go
│   │   ├── claude
│   │   │   ├── client.go
│   │   │   └── protocol.go
│   │   ├── correlator
│   │   │   ├── gitops.go
│   │   │   ├── helm_correlator.go
│   │   │   └── troubleshoot.go
│   │   ├── gitlab
│   │   │   ├── client.go
│   │   │   ├── mergerequests.go
│   │   │   ├── pipelines.go
│   │   │   └── repositories.go
│   │   ├── helm
│   │   │   └── parser.go
│   │   ├── k8s
│   │   │   ├── client.go
│   │   │   ├── enhanced_client.go
│   │   │   ├── events.go
│   │   │   ├── resource_mapper.go
│   │   │   └── resources.go
│   │   ├── mcp
│   │   │   ├── context.go
│   │   │   ├── namespace_analyzer.go
│   │   │   ├── prompt.go
│   │   │   └── protocol.go
│   │   └── models
│   │       ├── argocd.go
│   │       ├── context.go
│   │       ├── gitlab.go
│   │       └── kubernetes.go
│   └── pkg
│       ├── config
│       │   └── config.go
│       ├── logging
│       │   └── logging.go
│       └── utils
│           ├── serialization.go
│           └── truncation.go
├── LICENSE
└── README.md
```

# Files

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/gitlab/mergerequests.go:
--------------------------------------------------------------------------------

```go
  1 | // internal/gitlab/mergerequests.go
  2 | 
  3 | package gitlab
  4 | 
  5 | import (
  6 | 	"context"
  7 | 	"encoding/json"
  8 | 	"fmt"
  9 | 	"net/http"
 10 | 	"net/url"
 11 | 	"strings"
 12 | 
 13 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
 14 | )
 15 | 
 16 | // ListMergeRequests returns a list of merge requests for a project
 17 | func (c *Client) ListMergeRequests(ctx context.Context, projectID string, state string) ([]models.GitLabMergeRequest, error) {
 18 | 	c.logger.Debug("Listing merge requests", "projectID", projectID, "state", state)
 19 | 
 20 | 	// Create endpoint with query parameters
 21 | 	endpoint := fmt.Sprintf("projects/%s/merge_requests", url.PathEscape(projectID))
 22 | 
 23 | 	u, err := url.Parse(endpoint)
 24 | 	if err != nil {
 25 | 		return nil, fmt.Errorf("invalid endpoint: %w", err)
 26 | 	}
 27 | 
 28 | 	q := u.Query()
 29 | 	if state != "" {
 30 | 		q.Set("state", state)
 31 | 	}
 32 | 	q.Set("order_by", "updated_at")
 33 | 	q.Set("sort", "desc")
 34 | 	q.Set("per_page", "20")
 35 | 	u.RawQuery = q.Encode()
 36 | 
 37 | 	resp, err := c.doRequest(ctx, http.MethodGet, u.String(), nil)
 38 | 	if err != nil {
 39 | 		return nil, err
 40 | 	}
 41 | 	defer resp.Body.Close()
 42 | 
 43 | 	var mergeRequests []models.GitLabMergeRequest
 44 | 	if err := json.NewDecoder(resp.Body).Decode(&mergeRequests); err != nil {
 45 | 		return nil, fmt.Errorf("failed to decode response: %w", err)
 46 | 	}
 47 | 
 48 | 	c.logger.Debug("Listed merge requests", "projectID", projectID, "count", len(mergeRequests))
 49 | 	return mergeRequests, nil
 50 | }
 51 | 
 52 | // GetMergeRequest returns details about a specific merge request
 53 | func (c *Client) GetMergeRequest(ctx context.Context, projectID string, mergeRequestIID int) (*models.GitLabMergeRequest, error) {
 54 | 	c.logger.Debug("Getting merge request", "projectID", projectID, "mergeRequestIID", mergeRequestIID)
 55 | 
 56 | 	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d", url.PathEscape(projectID), mergeRequestIID)
 57 | 	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
 58 | 	if err != nil {
 59 | 		return nil, err
 60 | 	}
 61 | 	defer resp.Body.Close()
 62 | 
 63 | 	var mergeRequest models.GitLabMergeRequest
 64 | 	if err := json.NewDecoder(resp.Body).Decode(&mergeRequest); err != nil {
 65 | 		return nil, fmt.Errorf("failed to decode response: %w", err)
 66 | 	}
 67 | 
 68 | 	return &mergeRequest, nil
 69 | }
 70 | 
 71 | // GetMergeRequestChanges returns the changes in a specific merge request
 72 | func (c *Client) GetMergeRequestChanges(ctx context.Context, projectID string, mergeRequestIID int) (*models.GitLabMergeRequest, error) {
 73 | 	c.logger.Debug("Getting merge request changes", "projectID", projectID, "mergeRequestIID", mergeRequestIID)
 74 | 
 75 | 	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d/changes", url.PathEscape(projectID), mergeRequestIID)
 76 | 	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
 77 | 	if err != nil {
 78 | 		return nil, err
 79 | 	}
 80 | 	defer resp.Body.Close()
 81 | 
 82 | 	var mergeRequest models.GitLabMergeRequest
 83 | 	if err := json.NewDecoder(resp.Body).Decode(&mergeRequest); err != nil {
 84 | 		return nil, fmt.Errorf("failed to decode response: %w", err)
 85 | 	}
 86 | 
 87 | 	return &mergeRequest, nil
 88 | }
 89 | 
 90 | // GetMergeRequestApprovals returns approval information for a merge request
 91 | func (c *Client) GetMergeRequestApprovals(ctx context.Context, projectID string, mergeRequestIID int) (*models.GitLabMergeRequestApproval, error) {
 92 | 	c.logger.Debug("Getting merge request approvals", "projectID", projectID, "mergeRequestIID", mergeRequestIID)
 93 | 
 94 | 	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", url.PathEscape(projectID), mergeRequestIID)
 95 | 	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
 96 | 	if err != nil {
 97 | 		return nil, err
 98 | 	}
 99 | 	defer resp.Body.Close()
100 | 
101 | 	var approvals models.GitLabMergeRequestApproval
102 | 	if err := json.NewDecoder(resp.Body).Decode(&approvals); err != nil {
103 | 		return nil, fmt.Errorf("failed to decode response: %w", err)
104 | 	}
105 | 
106 | 	return &approvals, nil
107 | }
108 | 
109 | // GetMergeRequestComments returns comments on a merge request
110 | func (c *Client) GetMergeRequestComments(ctx context.Context, projectID string, mergeRequestIID int) ([]models.GitLabMergeRequestComment, error) {
111 | 	c.logger.Debug("Getting merge request comments", "projectID", projectID, "mergeRequestIID", mergeRequestIID)
112 | 
113 | 	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d/notes", url.PathEscape(projectID), mergeRequestIID)
114 | 	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
115 | 	if err != nil {
116 | 		return nil, err
117 | 	}
118 | 	defer resp.Body.Close()
119 | 
120 | 	var comments []models.GitLabMergeRequestComment
121 | 	if err := json.NewDecoder(resp.Body).Decode(&comments); err != nil {
122 | 		return nil, fmt.Errorf("failed to decode response: %w", err)
123 | 	}
124 | 
125 | 	c.logger.Debug("Got merge request comments", "projectID", projectID, "mergeRequestIID", mergeRequestIID, "count", len(comments))
126 | 	return comments, nil
127 | }
128 | 
129 | // GetMergeRequestCommits returns the commits in a merge request
130 | func (c *Client) GetMergeRequestCommits(ctx context.Context, projectID string, mergeRequestIID int) ([]models.GitLabCommit, error) {
131 | 	c.logger.Debug("Getting merge request commits", "projectID", projectID, "mergeRequestIID", mergeRequestIID)
132 | 
133 | 	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d/commits", url.PathEscape(projectID), mergeRequestIID)
134 | 	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
135 | 	if err != nil {
136 | 		return nil, err
137 | 	}
138 | 	defer resp.Body.Close()
139 | 
140 | 	var commits []models.GitLabCommit
141 | 	if err := json.NewDecoder(resp.Body).Decode(&commits); err != nil {
142 | 		return nil, fmt.Errorf("failed to decode response: %w", err)
143 | 	}
144 | 
145 | 	c.logger.Debug("Got merge request commits", "projectID", projectID, "mergeRequestIID", mergeRequestIID, "count", len(commits))
146 | 	return commits, nil
147 | }
148 | 
149 | // AnalyzeMergeRequest analyzes a merge request for Kubernetes/Helm changes
150 | func (c *Client) AnalyzeMergeRequest(ctx context.Context, projectID string, mergeRequestIID int) (*models.GitLabMergeRequest, error) {
151 | 	c.logger.Debug("Analyzing merge request", "projectID", projectID, "mergeRequestIID", mergeRequestIID)
152 | 
153 | 	// Get basic merge request data
154 | 	mr, err := c.GetMergeRequest(ctx, projectID, mergeRequestIID)
155 | 	if err != nil {
156 | 		return nil, fmt.Errorf("failed to get merge request: %w", err)
157 | 	}
158 | 
159 | 	// Get changes
160 | 	mrChanges, err := c.GetMergeRequestChanges(ctx, projectID, mergeRequestIID)
161 | 	if err != nil {
162 | 		return nil, fmt.Errorf("failed to get merge request changes: %w", err)
163 | 	}
164 | 
165 | 	// Copy changes to the original merge request
166 | 	mr.Changes = mrChanges.Changes
167 | 
168 | 	// Initialize context analysis
169 | 	mr.MergeRequestContext.AffectedFiles = make([]string, 0)
170 | 	mr.MergeRequestContext.HelmChartAffected = false
171 | 	mr.MergeRequestContext.KubernetesManifest = false
172 | 
173 | 	// Analyze changes
174 | 	for _, change := range mr.Changes {
175 | 		mr.MergeRequestContext.AffectedFiles = append(mr.MergeRequestContext.AffectedFiles, change.NewPath)
176 | 
177 | 		// Check for Helm charts
178 | 		if strings.Contains(change.NewPath, "Chart.yaml") ||
179 | 			strings.Contains(change.NewPath, "values.yaml") ||
180 | 			(strings.Contains(change.NewPath, "templates/") && strings.HasSuffix(change.NewPath, ".yaml")) {
181 | 			mr.MergeRequestContext.HelmChartAffected = true
182 | 		}
183 | 
184 | 		// Check for Kubernetes manifests
185 | 		if strings.HasSuffix(change.NewPath, ".yaml") || strings.HasSuffix(change.NewPath, ".yml") {
186 | 			// Look for Kubernetes kind in the file content
187 | 			if strings.Contains(change.Diff, "kind:") &&
188 | 				(strings.Contains(change.Diff, "Deployment") ||
189 | 					strings.Contains(change.Diff, "Service") ||
190 | 					strings.Contains(change.Diff, "ConfigMap") ||
191 | 					strings.Contains(change.Diff, "Secret") ||
192 | 					strings.Contains(change.Diff, "Pod")) {
193 | 				mr.MergeRequestContext.KubernetesManifest = true
194 | 			}
195 | 		}
196 | 	}
197 | 
198 | 	// Get commits
199 | 	commits, err := c.GetMergeRequestCommits(ctx, projectID, mergeRequestIID)
200 | 	if err != nil {
201 | 		c.logger.Warn("Failed to get merge request commits", "error", err)
202 | 	} else {
203 | 		// Extract commit messages
204 | 		mr.MergeRequestContext.CommitMessages = make([]string, 0)
205 | 		for _, commit := range commits {
206 | 			mr.MergeRequestContext.CommitMessages = append(mr.MergeRequestContext.CommitMessages, commit.Title)
207 | 		}
208 | 	}
209 | 
210 | 	return mr, nil
211 | }
212 | 
213 | // CreateMergeRequestComment creates a new comment on a merge request
214 | func (c *Client) CreateMergeRequestComment(ctx context.Context, projectID string, mergeRequestIID int, body string) (*models.GitLabMergeRequestComment, error) {
215 | 	c.logger.Debug("Creating merge request comment", "projectID", projectID, "mergeRequestIID", mergeRequestIID)
216 | 
217 | 	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d/notes", url.PathEscape(projectID), mergeRequestIID)
218 | 
219 | 	// Create request payload
220 | 	reqBody := map[string]string{
221 | 		"body": body,
222 | 	}
223 | 
224 | 	jsonBody, err := json.Marshal(reqBody)
225 | 	if err != nil {
226 | 		return nil, fmt.Errorf("failed to marshal request body: %w", err)
227 | 	}
228 | 
229 | 	resp, err := c.doRequest(ctx, http.MethodPost, endpoint, strings.NewReader(string(jsonBody)))
230 | 	if err != nil {
231 | 		return nil, err
232 | 	}
233 | 	defer resp.Body.Close()
234 | 
235 | 	var comment models.GitLabMergeRequestComment
236 | 	if err := json.NewDecoder(resp.Body).Decode(&comment); err != nil {
237 | 		return nil, fmt.Errorf("failed to decode response: %w", err)
238 | 	}
239 | 
240 | 	return &comment, nil
241 | }
242 | 
```

--------------------------------------------------------------------------------
/docs/src/pages/examples/index.astro:
--------------------------------------------------------------------------------

```
  1 | ---
  2 | import BaseLayout from '../../layouts/BaseLayout.astro';
  3 | import CodeBlock from '../../components/CodeBlock.astro';
  4 | 
  5 | const exampleCategories = [
  6 |   {
  7 |     title: "Basic API Usage",
  8 |     description: "Examples of common API calls for resource analysis",
  9 |     examples: [
 10 |       {
 11 |         title: "Analyzing a Pod",
 12 |         description: "Query the status and health of a pod",
 13 |         code: `curl -X POST \\
 14 |   -H "Content-Type: application/json" \\
 15 |   -H "X-API-Key: your_api_key" \\
 16 |   -d '{
 17 |     "resource": "pod",
 18 |     "name": "my-app-pod",
 19 |     "namespace": "default",
 20 |     "query": "Is this pod healthy? What do the resource usage metrics show?"
 21 |   }' \\
 22 |   http://mcp-server.example.com/api/v1/mcp/resource`,
 23 |         language: "bash"
 24 |       },
 25 |       {
 26 |         title: "Checking Service Connectivity",
 27 |         description: "Investigate connectivity issues between services",
 28 |         code: `curl -X POST \\
 29 |   -H "Content-Type: application/json" \\
 30 |   -H "X-API-Key: your_api_key" \\
 31 |   -d '{
 32 |     "resource": "service",
 33 |     "name": "backend-service",
 34 |     "namespace": "default",
 35 |     "query": "Why can't my frontend pods connect to this service?"
 36 |   }' \\
 37 |   http://mcp-server.example.com/api/v1/mcp/resource`,
 38 |         language: "bash"
 39 |       },
 40 |       {
 41 |         title: "Deployment Analysis",
 42 |         description: "Understand why a deployment isn't scaling properly",
 43 |         code: `curl -X POST \\
 44 |   -H "Content-Type: application/json" \\
 45 |   -H "X-API-Key: your_api_key" \\
 46 |   -d '{
 47 |     "resource": "deployment",
 48 |     "name": "web-frontend",
 49 |     "namespace": "default",
 50 |     "query": "Why is this deployment not scaling to the requested replicas?"
 51 |   }' \\
 52 |   http://mcp-server.example.com/api/v1/mcp/resource`,
 53 |         language: "bash"
 54 |       }
 55 |     ]
 56 |   },
 57 |   {
 58 |     title: "Troubleshooting",
 59 |     description: "Examples for diagnosing and fixing common issues",
 60 |     examples: [
 61 |       {
 62 |         title: "CrashLoopBackOff Investigation",
 63 |         description: "Troubleshoot a pod in CrashLoopBackOff state",
 64 |         code: `curl -X POST \\
 65 |   -H "Content-Type: application/json" \\
 66 |   -H "X-API-Key: your_api_key" \\
 67 |   -d '{
 68 |     "resource": "pod",
 69 |     "name": "crashing-pod",
 70 |     "namespace": "production",
 71 |     "query": "This pod is in CrashLoopBackOff. What's causing it and how can I fix it?"
 72 |   }' \\
 73 |   http://mcp-server.example.com/api/v1/mcp/troubleshoot`,
 74 |         language: "bash"
 75 |       },
 76 |       {
 77 |         title: "Ingress Issues",
 78 |         description: "Debug problems with an Ingress resource",
 79 |         code: `curl -X POST \\
 80 |   -H "Content-Type: application/json" \\
 81 |   -H "X-API-Key: your_api_key" \\
 82 |   -d '{
 83 |     "resource": "ingress",
 84 |     "name": "app-ingress",
 85 |     "namespace": "default",
 86 |     "query": "External users are getting 404 errors when accessing the application. What's wrong with this ingress?"
 87 |   }' \\
 88 |   http://mcp-server.example.com/api/v1/mcp/troubleshoot`,
 89 |         language: "bash"
 90 |       },
 91 |       {
 92 |         title: "Storage Problems",
 93 |         description: "Troubleshoot issues with PersistentVolumeClaims",
 94 |         code: `curl -X POST \\
 95 |   -H "Content-Type: application/json" \\
 96 |   -H "X-API-Key: your_api_key" \\
 97 |   -d '{
 98 |     "resource": "persistentvolumeclaim",
 99 |     "name": "database-storage",
100 |     "namespace": "database",
101 |     "query": "Why is this PVC stuck in pending state?"
102 |   }' \\
103 |   http://mcp-server.example.com/api/v1/mcp/troubleshoot`,
104 |         language: "bash"
105 |       }
106 |     ]
107 |   },
108 |   {
109 |     title: "GitOps Workflows",
110 |     description: "Examples for CI/CD integration and GitOps analysis",
111 |     examples: [
112 |       {
113 |         title: "ArgoCD Application Analysis",
114 |         description: "Check sync status and health of an ArgoCD application",
115 |         code: `curl -X POST \\
116 |   -H "Content-Type: application/json" \\
117 |   -H "X-API-Key: your_api_key" \\
118 |   -d '{
119 |     "resource": "application",
120 |     "name": "production-app",
121 |     "namespace": "argocd",
122 |     "query": "Is this application synced and healthy? If not, what are the issues?"
123 |   }' \\
124 |   http://mcp-server.example.com/api/v1/mcp/resource`,
125 |         language: "bash"
126 |       },
127 |       {
128 |         title: "Commit Impact Analysis",
129 |         description: "Analyze how a specific commit affected the cluster",
130 |         code: `curl -X POST \\
131 |   -H "Content-Type: application/json" \\
132 |   -H "X-API-Key: your_api_key" \\
133 |   -d '{
134 |     "projectId": "mygroup/myproject",
135 |     "commitSha": "a1b2c3d4e5f6",
136 |     "query": "What changes were made in this commit and how have they affected the deployed resources?"
137 |   }' \\
138 |   http://mcp-server.example.com/api/v1/mcp/commit`,
139 |         language: "bash"
140 |       },
141 |       {
142 |         title: "ArgoCD Sync Failure",
143 |         description: "Troubleshoot why an ArgoCD application isn't syncing",
144 |         code: `curl -X POST \\
145 |   -H "Content-Type: application/json" \\
146 |   -H "X-API-Key: your_api_key" \\
147 |   -d '{
148 |     "resource": "application",
149 |     "name": "failing-app",
150 |     "namespace": "argocd",
151 |     "query": "Why is this application failing to sync? What specific errors are occurring?"
152 |   }' \\
153 |   http://mcp-server.example.com/api/v1/mcp/troubleshoot`,
154 |         language: "bash"
155 |       }
156 |     ]
157 |   },
158 |   {
159 |     title: "Advanced Usage",
160 |     description: "Examples for more complex scenarios and integrations",
161 |     examples: [
162 |       {
163 |         title: "Resource Relationship Analysis",
164 |         description: "Understanding dependencies between resources",
165 |         code: `curl -X POST \\
166 |   -H "Content-Type: application/json" \\
167 |   -H "X-API-Key: your_api_key" \\
168 |   -d '{
169 |     "resource": "deployment",
170 |     "name": "application",
171 |     "namespace": "production",
172 |     "query": "Create a map of all resources related to this deployment, including services, configmaps, secrets, and ingresses."
173 |   }' \\
174 |   http://mcp-server.example.com/api/v1/mcp/resource`,
175 |         language: "bash"
176 |       },
177 |       {
178 |         title: "Multi-Resource Correlation",
179 |         description: "Analyze interactions between multiple resources",
180 |         code: `curl -X POST \\
181 |   -H "Content-Type: application/json" \\
182 |   -H "X-API-Key: your_api_key" \\
183 |   -d '{
184 |     "query": "Analyze the connection between the frontend deployment, backend service, and redis statefulset in the web namespace. Are there any connectivity or configuration issues?"
185 |   }' \\
186 |   http://mcp-server.example.com/api/v1/mcp`,
187 |         language: "bash"
188 |       },
189 |       {
190 |         title: "GitOps Security Audit",
191 |         description: "Audit resources for security issues and best practices",
192 |         code: `curl -X POST \\
193 |   -H "Content-Type: application/json" \\
194 |   -H "X-API-Key: your_api_key" \\
195 |   -d '{
196 |     "resource": "namespace",
197 |     "name": "production",
198 |     "query": "Perform a security audit of all resources in this namespace. Check for security best practices, RBAC issues, and potential vulnerabilities."
199 |   }' \\
200 |   http://mcp-server.example.com/api/v1/mcp/resource`,
201 |         language: "bash"
202 |       }
203 |     ]
204 |   }
205 | ];
206 | ---
207 | 
208 | <BaseLayout title="Examples | Kubernetes Claude MCP">
209 |   <div class="container mx-auto px-4 py-12">
210 |     <div class="max-w-5xl mx-auto">
211 |       <h1 class="text-4xl font-bold mb-6 text-primary-600">Examples</h1>
212 |       <p class="text-xl text-slate-600 mb-10">
213 |         Explore practical examples of using the Kubernetes Claude MCP server for various use cases. 
214 |         These examples demonstrate how to leverage the API for resource analysis, troubleshooting, and GitOps workflows.
215 |       </p>
216 |       
217 |       <div class="space-y-16">
218 |         {exampleCategories.map(category => (
219 |           <section class="example-category">
220 |             <h2 class="text-2xl font-bold mb-3 text-primary-600">{category.title}</h2>
221 |             <p class="text-lg text-slate-600 mb-6">{category.description}</p>
222 |             
223 |             <div class="space-y-8">
224 |               {category.examples.map(example => (
225 |                 <div class="example-card border border-secondary-300 rounded-lg overflow-hidden bg-secondary-50">
226 |                   <div class="p-5 border-b border-secondary-300 bg-secondary-100">
227 |                     <h3 class="text-xl font-semibold text-primary-600">{example.title}</h3>
228 |                     <p class="text-slate-600 mt-1">{example.description}</p>
229 |                   </div>
230 |                   <div class="p-5">
231 |                     <CodeBlock 
232 |                       code={example.code} 
233 |                       lang={example.language}
234 |                       showLineNumbers={true}
235 |                     />
236 |                   </div>
237 |                 </div>
238 |               ))}
239 |             </div>
240 |           </section>
241 |         ))}
242 |       </div>
243 |       
244 |       <div class="mt-12 text-center">
245 |         <h2 class="text-2xl font-bold mb-4 text-primary-600">Need More Help?</h2>
246 |         <p class="text-lg text-slate-600 mb-6">
247 |           Check out the detailed usage guides in the documentation or visit our GitHub repository.
248 |         </p>
249 |         <div class="flex flex-col sm:flex-row gap-4 justify-center">
250 |           <a href="/docs/api-overview" class="btn bg-primary-500 hover:bg-primary-600 text-white font-medium py-2 px-6 rounded-md">
251 |             API Reference
252 |           </a>
253 |           
254 |         </div>
255 |       </div>
256 |     </div>
257 |   </div>
258 | </BaseLayout>
```

--------------------------------------------------------------------------------
/docs/src/content/docs/api-overview.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: API Overview
  3 | description: Comprehensive documentation of the Kubernetes Claude MCP REST API endpoints, parameters, and response formats.
  4 | date: 2025-03-01
  5 | order: 5
  6 | tags: ['api', 'reference']
  7 | ---
  8 | 
  9 | # API Overview
 10 | 
 11 | Kubernetes Claude MCP provides a comprehensive REST API for interacting with Kubernetes resources, ArgoCD applications, GitLab repositories, and Claude's AI capabilities. This document provides an overview of all available endpoints, their parameters, and response formats.
 12 | 
 13 | ## API Structure
 14 | 
 15 | The API is organized into the following sections:
 16 | 
 17 | - **General**: Health check and general information
 18 | - **Kubernetes API**: Direct access to Kubernetes resources
 19 | - **ArgoCD API**: Access to ArgoCD applications and sync status
 20 | - **MCP API**: AI-powered analysis and troubleshooting
 21 | 
 22 | All API calls require authentication using an API key, which is passed in the `X-API-Key` header or as a bearer token in the `Authorization` header.
 23 | 
 24 | ```bash
 25 | # Using X-API-Key header
 26 | curl -H "X-API-Key: your_api_key" https://mcp.example.com/api/v1/health
 27 | 
 28 | # Using Authorization header
 29 | curl -H "Authorization: Bearer your_api_key" https://mcp.example.com/api/v1/health
 30 | ```
 31 | 
 32 | ## Health Check
 33 | 
 34 | ### GET /api/v1/health
 35 | 
 36 | Check the health status of the server and its connected services.
 37 | 
 38 | **Response:**
 39 | 
 40 | ```json
 41 | {
 42 |   "status": "ok",
 43 |   "services": {
 44 |     "kubernetes": "available",
 45 |     "argocd": "available",
 46 |     "gitlab": "available",
 47 |     "claude": "assumed available"
 48 |   }
 49 | }
 50 | ```
 51 | 
 52 | The `status` field will be `ok` if all required services are available, or `degraded` if some services are unavailable.
 53 | 
 54 | ## Kubernetes API
 55 | 
 56 | ### GET /api/v1/namespaces
 57 | 
 58 | List all namespaces in the Kubernetes cluster.
 59 | 
 60 | **Response:**
 61 | 
 62 | ```json
 63 | {
 64 |   "namespaces": [
 65 |     "default",
 66 |     "kube-system",
 67 |     "monitoring",
 68 |     "argocd"
 69 |   ]
 70 | }
 71 | ```
 72 | 
 73 | ### GET /api/v1/resources/{kind}?namespace={ns}
 74 | 
 75 | List all resources of a specific kind, optionally filtered by namespace.
 76 | 
 77 | **Parameters:**
 78 | - `kind`: The Kubernetes resource kind (e.g., `pod`, `deployment`, `service`)
 79 | - `namespace`: (Optional) The namespace to filter by
 80 | 
 81 | **Response:**
 82 | 
 83 | ```json
 84 | {
 85 |   "resources": [
 86 |     {
 87 |       "apiVersion": "v1",
 88 |       "kind": "Pod",
 89 |       "metadata": {
 90 |         "name": "example-pod",
 91 |         "namespace": "default",
 92 |         "...": "..."
 93 |       },
 94 |       "spec": { "...": "..." },
 95 |       "status": { "...": "..." }
 96 |     },
 97 |     // More resources...
 98 |   ]
 99 | }
100 | ```
101 | 
102 | ### GET /api/v1/resources/{kind}/{name}?namespace={ns}
103 | 
104 | Get a specific resource by kind, name, and namespace.
105 | 
106 | **Parameters:**
107 | - `kind`: The Kubernetes resource kind
108 | - `name`: The resource name
109 | - `namespace`: (Optional) The namespace of the resource
110 | 
111 | **Response:**
112 | 
113 | ```json
114 | {
115 |   "apiVersion": "v1",
116 |   "kind": "Pod",
117 |   "metadata": {
118 |     "name": "example-pod",
119 |     "namespace": "default",
120 |     "...": "..."
121 |   },
122 |   "spec": { "...": "..." },
123 |   "status": { "...": "..." }
124 | }
125 | ```
126 | 
127 | ### GET /api/v1/events?namespace={ns}&resource={kind}&name={name}
128 | 
129 | Get events related to a specific resource.
130 | 
131 | **Parameters:**
132 | - `namespace`: The namespace of the resource
133 | - `resource`: The resource kind
134 | - `name`: The resource name
135 | 
136 | **Response:**
137 | 
138 | ```json
139 | {
140 |   "events": [
141 |     {
142 |       "reason": "Created",
143 |       "message": "Created container nginx",
144 |       "type": "Normal",
145 |       "count": 1,
146 |       "firstTime": "2025-03-01T12:00:00Z",
147 |       "lastTime": "2025-03-01T12:00:00Z",
148 |       "object": {
149 |         "kind": "Pod",
150 |         "name": "example-pod",
151 |         "namespace": "default"
152 |       }
153 |     },
154 |     // More events...
155 |   ]
156 | }
157 | ```
158 | 
159 | ## ArgoCD API
160 | 
161 | ### GET /api/v1/argocd/applications
162 | 
163 | List all ArgoCD applications.
164 | 
165 | **Response:**
166 | 
167 | ```json
168 | {
169 |   "applications": [
170 |     {
171 |       "metadata": {
172 |         "name": "example-app",
173 |         "namespace": "argocd"
174 |       },
175 |       "spec": {
176 |         "source": {
177 |           "repoURL": "https://github.com/example/repo.git",
178 |           "path": "manifests",
179 |           "targetRevision": "HEAD"
180 |         },
181 |         "destination": {
182 |           "server": "https://kubernetes.default.svc",
183 |           "namespace": "default"
184 |         }
185 |       },
186 |       "status": {
187 |         "sync": {
188 |           "status": "Synced"
189 |         },
190 |         "health": {
191 |           "status": "Healthy"
192 |         }
193 |       }
194 |     },
195 |     // More applications...
196 |   ]
197 | }
198 | ```
199 | 
200 | ### GET /api/v1/argocd/applications/{name}
201 | 
202 | Get a specific ArgoCD application by name.
203 | 
204 | **Parameters:**
205 | - `name`: The ArgoCD application name
206 | 
207 | **Response:**
208 | 
209 | ```json
210 | {
211 |   "metadata": {
212 |     "name": "example-app",
213 |     "namespace": "argocd"
214 |   },
215 |   "spec": {
216 |     "source": {
217 |       "repoURL": "https://github.com/example/repo.git",
218 |       "path": "manifests",
219 |       "targetRevision": "HEAD"
220 |     },
221 |     "destination": {
222 |       "server": "https://kubernetes.default.svc",
223 |       "namespace": "default"
224 |     }
225 |   },
226 |   "status": {
227 |     "sync": {
228 |       "status": "Synced"
229 |     },
230 |     "health": {
231 |       "status": "Healthy"
232 |     }
233 |   }
234 | }
235 | ```
236 | 
237 | ## MCP API
238 | 
239 | The MCP API provides access to Claude's AI capabilities for analyzing Kubernetes resources and GitOps workflows.
240 | 
241 | ### POST /api/v1/mcp
242 | 
243 | Generic MCP request for Claude analysis.
244 | 
245 | **Request:**
246 | 
247 | ```json
248 | {
249 |   "action": "string",
250 |   "resource": "string",
251 |   "name": "string",
252 |   "namespace": "string",
253 |   "query": "string",
254 |   "commitSha": "string",
255 |   "projectId": "string",
256 |   "resourceSpecs": {},
257 |   "context": "string"
258 | }
259 | ```
260 | 
261 | **Response:**
262 | 
263 | ```json
264 | {
265 |   "success": true,
266 |   "message": "Successfully processed request",
267 |   "analysis": "Detailed analysis from Claude...",
268 |   "actions": ["suggested actions..."],
269 |   "context": {}
270 | }
271 | ```
272 | 
273 | ### POST /api/v1/mcp/resource
274 | 
275 | Analyze a specific Kubernetes resource.
276 | 
277 | **Request:**
278 | 
279 | ```json
280 | {
281 |   "resource": "pod",
282 |   "name": "example-pod",
283 |   "namespace": "default",
284 |   "query": "Is this pod healthy? If not, what are the issues?"
285 | }
286 | ```
287 | 
288 | **Response:**
289 | 
290 | ```json
291 | {
292 |   "success": true,
293 |   "message": "Successfully processed queryResource request",
294 |   "analysis": "Detailed analysis of the pod's health status...",
295 |   "context": {
296 |     "kind": "Pod",
297 |     "name": "example-pod",
298 |     "namespace": "default",
299 |     "argoApplication": {},
300 |     "gitlabProject": {},
301 |     "events": []
302 |   }
303 | }
304 | ```
305 | 
306 | ### POST /api/v1/mcp/commit
307 | 
308 | Analyze the impact of a specific GitLab commit.
309 | 
310 | **Request:**
311 | 
312 | ```json
313 | {
314 |   "projectId": "group/project",
315 |   "commitSha": "abcdef123456",
316 |   "query": "What changes were made in this commit and how do they affect the deployed resources?"
317 | }
318 | ```
319 | 
320 | **Response:**
321 | 
322 | ```json
323 | {
324 |   "success": true,
325 |   "message": "Successfully processed queryCommit request",
326 |   "analysis": "Detailed analysis of the commit and its impact...",
327 |   "context": {
328 |     "commit": {},
329 |     "affectedResources": []
330 |   }
331 | }
332 | ```
333 | 
334 | ### POST /api/v1/mcp/troubleshoot
335 | 
336 | Troubleshoot a specific Kubernetes resource.
337 | 
338 | **Request:**
339 | 
340 | ```json
341 | {
342 |   "resource": "deployment",
343 |   "name": "example-deployment",
344 |   "namespace": "default",
345 |   "query": "Why is this deployment not scaling properly?"
346 | }
347 | ```
348 | 
349 | **Response:**
350 | 
351 | ```json
352 | {
353 |   "success": true,
354 |   "message": "Successfully processed troubleshoot request",
355 |   "analysis": "Detailed troubleshooting analysis...",
356 |   "troubleshootResult": {
357 |     "issues": [
358 |       {
359 |         "title": "Resource Constraint",
360 |         "category": "ResourceIssue",
361 |         "severity": "Warning",
362 |         "source": "Kubernetes",
363 |         "description": "Deployment cannot scale due to insufficient CPU resources"
364 |       }
365 |     ],
366 |     "recommendations": [
367 |       "Increase CPU request to allow for additional replicas",
368 |       "Check node resources to ensure sufficient capacity"
369 |     ]
370 |   }
371 | }
372 | ```
373 | 
374 | ## Error Handling
375 | 
376 | All API endpoints return standard HTTP status codes:
377 | 
378 | - `200 OK`: Request was successful
379 | - `400 Bad Request`: Invalid request format or parameters
380 | - `401 Unauthorized`: Missing or invalid API key
381 | - `404 Not Found`: Resource not found
382 | - `500 Internal Server Error`: Server error
383 | 
384 | Error responses include a JSON body with details:
385 | 
386 | ```json
387 | {
388 |   "error": "Failed to get resource",
389 |   "details": "pod 'example-pod' not found in namespace 'default'"
390 | }
391 | ```
392 | 
393 | ## Pagination
394 | 
395 | For endpoints that return collections, pagination is supported using the following query parameters:
396 | 
397 | - `limit`: Maximum number of items to return (default: 100)
398 | - `page`: Page number to return (default: 1)
399 | 
400 | Example:
401 | 
402 | ```
403 | GET /api/v1/resources/pods?namespace=default&limit=10&page=2
404 | ```
405 | 
406 | Response includes pagination metadata:
407 | 
408 | ```json
409 | {
410 |   "resources": [...],
411 |   "pagination": {
412 |     "total": 25,
413 |     "pages": 3,
414 |     "currentPage": 2,
415 |     "limit": 10
416 |   }
417 | }
418 | ```
419 | 
420 | ## API Versioning
421 | 
422 | The API version is included in the URL path (`/api/v1/`). Future API versions will be made available at different paths (e.g., `/api/v2/`) to ensure backward compatibility.
423 | 
424 | ## Rate Limiting
425 | 
426 | The API implements rate limiting to prevent abuse. Rate limits vary by endpoint:
427 | 
428 | - General endpoints: 100 requests per minute
429 | - Kubernetes endpoints: 60 requests per minute
430 | - ArgoCD endpoints: 60 requests per minute
431 | - MCP endpoints: 20 requests per minute
432 | 
433 | Rate limit information is included in response headers:
434 | 
435 | - `X-RateLimit-Limit`: Total requests allowed per minute
436 | - `X-RateLimit-Remaining`: Remaining requests in the current window
437 | - `X-RateLimit-Reset`: Seconds until the rate limit resets
```

--------------------------------------------------------------------------------
/docs/src/pages/index.astro:
--------------------------------------------------------------------------------

```
  1 | ---
  2 | import BaseLayout from '../layouts/BaseLayout.astro';
  3 | ---
  4 | 
  5 | <BaseLayout title="Kubernetes Claude MCP - AI-powered GitOps with Claude" 
  6 |   description="The official documentation for Kubernetes Claude MCP Server, integrating Claude AI with Kubernetes, ArgoCD, and GitLab">
  7 |   
  8 |   <!-- Hero Section -->
  9 |   <section class="py-20 px-4 bg-gradient-to-b from-secondary-200 to-secondary-100">
 10 |     <div class="container mx-auto max-w-5xl">
 11 |       <div class="flex flex-col items-center text-center">
 12 |         <img src="/images/logo.svg" alt="Kubernetes Claude MCP Logo" class="w-48 h-48 mb-6">
 13 |         <h1 class="text-5xl font-bold tracking-tight mb-6 text-primary-600">
 14 |           Kubernetes Claude MCP
 15 |         </h1>
 16 |         <p class="text-xl text-slate-700 mb-8 max-w-2xl">
 17 |           Model Context Protocol server for Kubernetes, integrating Claude AI with ArgoCD and GitLab for advanced GitOps workflows.
 18 |         </p>
 19 |         <div class="flex flex-col sm:flex-row gap-4">
 20 |           <a href="/docs/introduction" class="btn bg-primary-500 hover:bg-primary-600 text-white font-medium py-2 px-6 rounded-md">
 21 |             Get Started
 22 |           </a>
 23 |           <a href="https://github.com/blankcut/kubernetes-mcp-server" class="btn bg-secondary-200 hover:bg-secondary-300 text-primary-700 font-medium py-2 px-6 rounded-md" target="_blank" rel="noopener">
 24 |             GitHub
 25 |           </a>
 26 |         </div>
 27 |       </div>
 28 |     </div>
 29 |   </section>
 30 | 
 31 |   <!-- Features Section -->
 32 |   <section class="py-16 px-4 bg-secondary-100">
 33 |     <div class="container mx-auto max-w-6xl">
 34 |       <h2 class="text-3xl font-bold text-center mb-12 text-primary-600">Key Features</h2>
 35 |       
 36 |       <div class="grid md:grid-cols-3 gap-8">
 37 |         <!-- Feature 1 -->
 38 |         <div class="feature-card p-6 rounded-lg border border-secondary-200 shadow-sm bg-secondary-100">
 39 |           <div class="mb-4 text-primary-500">
 40 |             <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 41 |               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
 42 |             </svg>
 43 |           </div>
 44 |           <h3 class="text-xl font-semibold mb-2 text-primary-600">Kubernetes Integration</h3>
 45 |           <p class="text-slate-600">
 46 |             Seamlessly connects to your Kubernetes cluster, providing automated analysis and troubleshooting of resources.
 47 |           </p>
 48 |         </div>
 49 | 
 50 |         <!-- Feature 2 -->
 51 |         <div class="feature-card p-6 rounded-lg border border-secondary-200 shadow-sm bg-secondary-100">
 52 |           <div class="mb-4 text-primary-500">
 53 |             <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 54 |               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
 55 |             </svg>
 56 |           </div>
 57 |           <h3 class="text-xl font-semibold mb-2 text-primary-600">Claude AI Powered</h3>
 58 |           <p class="text-slate-600">
 59 |             Leverages Claude's AI capabilities to analyze configurations, explain issues, and recommend solutions for your cluster.
 60 |           </p>
 61 |         </div>
 62 | 
 63 |         <!-- Feature 3 -->
 64 |         <div class="feature-card p-6 rounded-lg border border-secondary-200 shadow-sm bg-secondary-100">
 65 |           <div class="mb-4 text-primary-500">
 66 |             <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 67 |               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10" />
 68 |             </svg>
 69 |           </div>
 70 |           <h3 class="text-xl font-semibold mb-2 text-primary-600">GitOps Integration</h3>
 71 |           <p class="text-slate-600">
 72 |             Integrates with ArgoCD and GitLab to provide complete GitOps context for your deployments and configuration changes.
 73 |           </p>
 74 |         </div>
 75 |       </div>
 76 |       
 77 |       <div class="grid md:grid-cols-2 gap-8 mt-8">
 78 |         <!-- Feature 4 -->
 79 |         <div class="feature-card p-6 rounded-lg border border-secondary-200 shadow-sm bg-secondary-100">
 80 |           <div class="mb-4 text-primary-500">
 81 |             <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 82 |               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
 83 |             </svg>
 84 |           </div>
 85 |           <h3 class="text-xl font-semibold mb-2 text-primary-600">Troubleshooting & Analysis</h3>
 86 |           <p class="text-slate-600">
 87 |             Quickly identify issues in your cluster with comprehensive troubleshooting and analysis capabilities that trace through your entire deployment pipeline.
 88 |           </p>
 89 |         </div>
 90 | 
 91 |         <!-- Feature 5 -->
 92 |         <div class="feature-card p-6 rounded-lg border border-secondary-200 shadow-sm bg-secondary-100">
 93 |           <div class="mb-4 text-primary-500">
 94 |             <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 95 |               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
 96 |             </svg>
 97 |           </div>
 98 |           <h3 class="text-xl font-semibold mb-2 text-primary-600">RESTful API</h3>
 99 |           <p class="text-slate-600">
100 |             Comprehensive API for integrating with your existing tools and workflows, with detailed documentation and examples.
101 |           </p>
102 |         </div>
103 |       </div>
104 |     </div>
105 |   </section>
106 | 
107 |   <!-- How It Works Section -->
108 |   <section class="py-16 px-4">
109 |     <div class="container mx-auto max-w-6xl">
110 |       <h2 class="text-3xl font-bold text-center mb-12 text-primary-600">How It Works</h2>
111 |       
112 |       <div class="grid md:grid-cols-3 gap-8 text-center">
113 |         <!-- Step 1 -->
114 |         <div class="step">
115 |           <div class="bg-secondary-200 rounded-full h-16 w-16 flex items-center justify-center mx-auto mb-4 border border-secondary-300">
116 |             <span class="text-primary-600 text-xl font-bold">1</span>
117 |           </div>
118 |           <h3 class="text-xl font-semibold mb-2 text-primary-600">Connect</h3>
119 |           <p class="text-slate-600">
120 |             Connect the server to your Kubernetes cluster, ArgoCD instance, and GitLab repositories.
121 |           </p>
122 |         </div>
123 |         
124 |         <!-- Step 2 -->
125 |         <div class="step">
126 |           <div class="bg-secondary-200 rounded-full h-16 w-16 flex items-center justify-center mx-auto mb-4 border border-secondary-300">
127 |             <span class="text-primary-600 text-xl font-bold">2</span>
128 |           </div>
129 |           <h3 class="text-xl font-semibold mb-2 text-primary-600">Query</h3>
130 |           <p class="text-slate-600">
131 |             Query resources, deployments, or commits through the API to get AI-powered analysis and context.
132 |           </p>
133 |         </div>
134 |         
135 |         <!-- Step 3 -->
136 |         <div class="step">
137 |           <div class="bg-secondary-200 rounded-full h-16 w-16 flex items-center justify-center mx-auto mb-4 border border-secondary-300">
138 |             <span class="text-primary-600 text-xl font-bold">3</span>
139 |           </div>
140 |           <h3 class="text-xl font-semibold mb-2 text-primary-600">Analyze</h3>
141 |           <p class="text-slate-600">
142 |             Receive detailed analysis, troubleshooting recommendations, and insights from Claude AI.
143 |           </p>
144 |         </div>
145 |       </div>
146 |     </div>
147 |   </section>
148 | 
149 |   <!-- CTA Section -->
150 |   <section class="py-16 px-4 bg-secondary-100">
151 |     <div class="container mx-auto max-w-5xl">
152 |       <div class="bg-primary-500 text-white rounded-lg p-8 md:p-12 shadow-lg">
153 |         <div class="text-center">
154 |           <h2 class="text-3xl font-bold mb-4">Ready to transform your Kubernetes experience?</h2>
155 |           <p class="text-lg opacity-90 mb-8 max-w-2xl mx-auto">
156 |             Get started with Kubernetes Claude MCP today and enhance your GitOps workflows with AI-powered analysis and troubleshooting.
157 |           </p>
158 |           <div class="flex flex-col sm:flex-row gap-4 justify-center">
159 |             <a href="/docs/quick-start" class="btn bg-white text-primary-700 hover:bg-secondary-100 font-medium py-2 px-6 rounded-md">
160 |               Quick Start Guide
161 |             </a>
162 |             <a href="/docs/installation" class="btn bg-primary-600 hover:bg-primary-700 text-white font-medium py-2 px-6 rounded-md border border-primary-400">
163 |               Installation Guide
164 |             </a>
165 |           </div>
166 |         </div>
167 |       </div>
168 |     </div>
169 |   </section>
170 | </BaseLayout>
```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/argocd/client.go:
--------------------------------------------------------------------------------

```go
  1 | package argocd
  2 | 
  3 | import (
  4 | 	"bytes"
  5 | 	"context"
  6 | 	"crypto/tls"
  7 | 	"encoding/json"
  8 | 	"fmt"
  9 | 	"io"
 10 | 	"net/http"
 11 | 	"net/url"
 12 | 	"path"
 13 | 	"strings"
 14 | 	"time"
 15 | 
 16 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/auth"
 17 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/config"
 18 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
 19 | )
 20 | 
 21 | // Client handles communication with the ArgoCD API
 22 | type Client struct {
 23 | 	baseURL            string
 24 | 	httpClient         *http.Client
 25 | 	credentialProvider *auth.CredentialProvider
 26 | 	config             *config.ArgoCDConfig
 27 | 	logger             *logging.Logger
 28 | }
 29 | 
 30 | // NewClient creates a new ArgoCD API client
 31 | func NewClient(cfg *config.ArgoCDConfig, credProvider *auth.CredentialProvider, logger *logging.Logger) *Client {
 32 | 	if logger == nil {
 33 | 		logger = logging.NewLogger().Named("argocd")
 34 | 	}
 35 | 
 36 | 	// Create transport with optional insecure mode
 37 | 	transport := &http.Transport{
 38 | 		TLSClientConfig: &tls.Config{
 39 | 			InsecureSkipVerify: cfg.Insecure,
 40 | 		},
 41 | 	}
 42 | 
 43 | 	return &Client{
 44 | 		baseURL: cfg.URL,
 45 | 		httpClient: &http.Client{
 46 | 			Timeout:   30 * time.Second,
 47 | 			Transport: transport,
 48 | 		},
 49 | 		credentialProvider: credProvider,
 50 | 		config:             cfg,
 51 | 		logger:             logger,
 52 | 	}
 53 | }
 54 | 
 55 | // CheckConnectivity tests the connection to the ArgoCD API
 56 | func (c *Client) CheckConnectivity(ctx context.Context) error {
 57 | 	c.logger.Debug("Checking ArgoCD connectivity")
 58 | 
 59 | 	// Try to get ArgoCD version as a basic connectivity test
 60 | 	endpoint := "/api/version"
 61 | 	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
 62 | 	if err != nil {
 63 | 		return fmt.Errorf("failed to connect to ArgoCD: %w", err)
 64 | 	}
 65 | 	defer resp.Body.Close()
 66 | 
 67 | 	var version struct {
 68 | 		Version string `json:"version"`
 69 | 	}
 70 | 
 71 | 	if err := json.NewDecoder(resp.Body).Decode(&version); err != nil {
 72 | 		return fmt.Errorf("failed to decode ArgoCD version: %w", err)
 73 | 	}
 74 | 
 75 | 	c.logger.Debug("ArgoCD connectivity check successful", "version", version.Version)
 76 | 	return nil
 77 | }
 78 | 
 79 | // doRequest performs an HTTP request to the ArgoCD API with authentication
 80 | func (c *Client) doRequest(ctx context.Context, method, endpoint string, body io.Reader) (*http.Response, error) {
 81 |     // Try the request with current credentials
 82 |     resp, err := c.attemptRequest(ctx, method, endpoint, body)
 83 |     
 84 |     // If we get a 401 unauthorized, try to refresh the token and retry once
 85 |     if err != nil && resp != nil && resp.StatusCode == http.StatusUnauthorized {
 86 |         c.logger.Debug("Received 401 from ArgoCD, attempting to refresh token")
 87 |         
 88 |         // Only try to refresh the token if we have username/password
 89 |         creds, err := c.credentialProvider.GetCredentials(auth.ServiceArgoCD)
 90 |         if err == nil && creds.Username != "" && creds.Password != "" {
 91 |             // Attempt to create a new session
 92 |             newToken, _, err := c.createSession(ctx, creds.Username, creds.Password)
 93 |             if err != nil {
 94 |                 return nil, fmt.Errorf("failed to refresh ArgoCD token: %w", err)
 95 |             }
 96 |             
 97 |             // Update the credentials with the new token
 98 |             c.credentialProvider.UpdateArgoToken(ctx, newToken)
 99 |             
100 |             // Retry the request with the new token
101 |             return c.attemptRequest(ctx, method, endpoint, body)
102 |         }
103 |     }
104 |     
105 |     return resp, err
106 | }
107 | 
108 | // attemptRequest makes a single request attempt
109 | func (c *Client) attemptRequest(ctx context.Context, method, endpoint string, body io.Reader) (*http.Response, error) {
110 |     // This contains the original doRequest logic
111 |     u, err := url.Parse(c.baseURL)
112 |     if err != nil {
113 |         return nil, fmt.Errorf("invalid ArgoCD URL: %w", err)
114 |     }
115 |     u.Path = path.Join(u.Path, endpoint)
116 | 
117 |     req, err := http.NewRequestWithContext(ctx, method, u.String(), body)
118 |     if err != nil {
119 |         return nil, fmt.Errorf("failed to create request: %w", err)
120 |     }
121 | 
122 |     if err := c.addAuth(req); err != nil {
123 |         return nil, fmt.Errorf("failed to add authentication: %w", err)
124 |     }
125 | 
126 |     req.Header.Set("Content-Type", "application/json")
127 | 
128 |     c.logger.Debug("Sending request to ArgoCD API", "method", method, "endpoint", endpoint)
129 |     resp, err := c.httpClient.Do(req)
130 |     if err != nil {
131 |         return nil, fmt.Errorf("request failed: %w", err)
132 |     }
133 | 
134 |     if resp.StatusCode >= 400 && resp.StatusCode != 401 {
135 |         defer resp.Body.Close()
136 |         body, _ := io.ReadAll(resp.Body)
137 |         return nil, fmt.Errorf("ArgoCD API error (status %d): %s", resp.StatusCode, string(body))
138 |     }
139 | 
140 |     return resp, nil
141 | }
142 | 
143 | // createSession creates a new ArgoCD session
144 | func (c *Client) createSession(ctx context.Context, username, password string) (string, time.Time, error) {
145 |     // Create session request
146 |     sessionReq := struct {
147 |         Username string `json:"username"`
148 |         Password string `json:"password"`
149 |     }{
150 |         Username: username,
151 |         Password: password,
152 |     }
153 | 
154 |     // Convert to JSON
155 |     sessionReqBody, err := json.Marshal(sessionReq)
156 |     if err != nil {
157 |         return "", time.Time{}, fmt.Errorf("failed to marshal session request: %w", err)
158 |     }
159 | 
160 |     // Create a new HTTP client without authentication for this request
161 |     u, err := url.Parse(c.baseURL)
162 |     if err != nil {
163 |         return "", time.Time{}, fmt.Errorf("invalid ArgoCD URL: %w", err)
164 |     }
165 |     u.Path = path.Join(u.Path, "/api/v1/session")
166 | 
167 |     req, err := http.NewRequestWithContext(
168 |         ctx,
169 |         http.MethodPost,
170 |         u.String(),
171 |         bytes.NewReader(sessionReqBody),
172 |     )
173 |     if err != nil {
174 |         return "", time.Time{}, fmt.Errorf("failed to create session request: %w", err)
175 |     }
176 | 
177 |     req.Header.Set("Content-Type", "application/json")
178 | 
179 |     resp, err := c.httpClient.Do(req)
180 |     if err != nil {
181 |         return "", time.Time{}, fmt.Errorf("session request failed: %w", err)
182 |     }
183 |     defer resp.Body.Close()
184 | 
185 |     if resp.StatusCode != http.StatusOK {
186 |         body, _ := io.ReadAll(resp.Body)
187 |         return "", time.Time{}, fmt.Errorf("failed to create session (status %d): %s", resp.StatusCode, string(body))
188 |     }
189 | 
190 |     var sessionResp struct {
191 |         Token string `json:"token"`
192 |     }
193 | 
194 |     if err := json.NewDecoder(resp.Body).Decode(&sessionResp); err != nil {
195 |         return "", time.Time{}, fmt.Errorf("failed to decode session response: %w", err)
196 |     }
197 | 
198 |     // ArgoCD tokens will expire after 24 hours by default...
199 |     expiry := time.Now().Add(24 * time.Hour)
200 | 
201 |     return sessionResp.Token, expiry, nil
202 | }
203 | 
204 | // addAuth adds authentication to the request
205 | func (c *Client) addAuth(req *http.Request) error {
206 |     creds, err := c.credentialProvider.GetCredentials(auth.ServiceArgoCD)
207 |     if err != nil {
208 |         return fmt.Errorf("failed to get ArgoCD credentials: %w", err)
209 |     }
210 | 
211 |     if creds.Token != "" {
212 |         // Set both header formats that ArgoCD might accept
213 |         req.Header.Set("Authorization", "Bearer "+creds.Token)
214 |         req.Header.Set("Cookie", "argocd.token="+creds.Token)
215 |         return nil
216 |     }
217 | 
218 |     if creds.Username != "" && creds.Password != "" {
219 |         // We need to get a session token first
220 |         token, _, err := c.createSession(req.Context(), creds.Username, creds.Password)
221 |         if err != nil {
222 |             return fmt.Errorf("failed to create ArgoCD session: %w", err)
223 |         }
224 |         
225 |         // Update credentials with the new token
226 |         c.credentialProvider.UpdateArgoToken(req.Context(), token)
227 |         
228 |         // Set both header formats
229 |         req.Header.Set("Authorization", "Bearer "+token)
230 |         req.Header.Set("Cookie", "argocd.token="+token)
231 |         return nil
232 |     }
233 | 
234 |     return fmt.Errorf("no valid ArgoCD credentials available")
235 | }
236 | 
237 | // refreshToken gets a new token using username/password credentials
238 | func (c *Client) refreshToken(ctx context.Context) (string, time.Time, error) {
239 | 	creds, err := c.credentialProvider.GetCredentials(auth.ServiceArgoCD)
240 | 	if err != nil {
241 | 		return "", time.Time{}, fmt.Errorf("failed to get ArgoCD credentials: %w", err)
242 | 	}
243 | 
244 | 	if creds.Username == "" || creds.Password == "" {
245 | 		return "", time.Time{}, fmt.Errorf("username/password required for token refresh")
246 | 	}
247 | 
248 | 	// Create session request
249 | 	sessionReq := struct {
250 | 		Username string `json:"username"`
251 | 		Password string `json:"password"`
252 | 	}{
253 | 		Username: creds.Username,
254 | 		Password: creds.Password,
255 | 	}
256 | 
257 | 	// Convert to JSON
258 | 	sessionReqBody, err := json.Marshal(sessionReq)
259 | 	if err != nil {
260 | 		return "", time.Time{}, fmt.Errorf("failed to marshal session request: %w", err)
261 | 	}
262 | 
263 | 	// Create a new HTTP client without authentication for this request
264 | 	req, err := http.NewRequestWithContext(
265 | 		ctx,
266 | 		http.MethodPost,
267 | 		fmt.Sprintf("%s/api/v1/session", c.baseURL),
268 | 		io.NopCloser(strings.NewReader(string(sessionReqBody))),
269 | 	)
270 | 	if err != nil {
271 | 		return "", time.Time{}, fmt.Errorf("failed to create session request: %w", err)
272 | 	}
273 | 
274 | 	req.Header.Set("Content-Type", "application/json")
275 | 
276 | 	resp, err := c.httpClient.Do(req)
277 | 	if err != nil {
278 | 		return "", time.Time{}, fmt.Errorf("session request failed: %w", err)
279 | 	}
280 | 	defer resp.Body.Close()
281 | 
282 | 	if resp.StatusCode != http.StatusOK {
283 | 		body, _ := io.ReadAll(resp.Body)
284 | 		return "", time.Time{}, fmt.Errorf("failed to create session (status %d): %s", resp.StatusCode, string(body))
285 | 	}
286 | 
287 | 	var sessionResp struct {
288 | 		Token string `json:"token"`
289 | 	}
290 | 
291 | 	if err := json.NewDecoder(resp.Body).Decode(&sessionResp); err != nil {
292 | 		return "", time.Time{}, fmt.Errorf("failed to decode session response: %w", err)
293 | 	}
294 | 
295 | 	// ArgoCD tokens typically expire after 24 hours
296 | 	expiry := time.Now().Add(24 * time.Hour)
297 | 
298 | 	return sessionResp.Token, expiry, nil
299 | }
300 | 
```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/mcp/protocol.go:
--------------------------------------------------------------------------------

```go
  1 | package mcp
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"fmt"
  6 | 	"strings"
  7 | 	"time"
  8 | 
  9 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/claude"
 10 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/correlator"
 11 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/k8s"
 12 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
 13 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
 14 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/utils"
 15 | 
 16 | 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 17 | )
 18 | 
 19 | // ProtocolHandler handles the Model Context Protocol for Kubernetes
 20 | type ProtocolHandler struct {
 21 | 	claudeClient     *claude.Client
 22 | 	claudeProtocol   *claude.ProtocolHandler
 23 | 	gitOpsCorrelator *correlator.GitOpsCorrelator
 24 | 	k8sClient        *k8s.Client
 25 | 	contextManager   *ContextManager
 26 | 	promptGenerator  *PromptGenerator
 27 | 	logger           *logging.Logger
 28 | }
 29 | 
 30 | // NewProtocolHandler creates a new MCP protocol handler
 31 | func NewProtocolHandler(
 32 | 	claudeClient *claude.Client,
 33 | 	gitOpsCorrelator *correlator.GitOpsCorrelator,
 34 | 	k8sClient *k8s.Client,
 35 | 	logger *logging.Logger,
 36 | ) *ProtocolHandler {
 37 | 	if logger == nil {
 38 | 		logger = logging.NewLogger().Named("mcp")
 39 | 	}
 40 | 
 41 | 	return &ProtocolHandler{
 42 | 		claudeClient:     claudeClient,
 43 | 		claudeProtocol:   claude.NewProtocolHandler(claudeClient),
 44 | 		gitOpsCorrelator: gitOpsCorrelator,
 45 | 		k8sClient:        k8sClient,
 46 | 		contextManager:   NewContextManager(100000, logger.Named("context")),
 47 | 		promptGenerator:  NewPromptGenerator(logger.Named("prompt")),
 48 | 		logger:           logger,
 49 | 	}
 50 | }
 51 | 
 52 | // ProcessRequest processes an MCP request
 53 | func (h *ProtocolHandler) ProcessRequest(ctx context.Context, request *models.MCPRequest) (*models.MCPResponse, error) {
 54 | 	startTime := time.Now()
 55 | 	h.logger.Info("Processing MCP request", "action", request.Action)
 56 | 
 57 | 	var resourceContext string
 58 | 	var err error
 59 | 
 60 | 	// Handle different types of queries
 61 | 	switch request.Action {
 62 | 	case "queryResource":
 63 | 		// If we have pre-populated context, use it
 64 | 		if request.Context != "" {
 65 | 			resourceContext = request.Context
 66 | 		} else {
 67 | 			// Trace deployment for a specific resource
 68 | 			resourceInfo, err := h.gitOpsCorrelator.TraceResourceDeployment(
 69 | 				ctx,
 70 | 				request.Namespace,
 71 | 				request.Resource,
 72 | 				request.Name,
 73 | 			)
 74 | 			if err != nil {
 75 | 				return nil, fmt.Errorf("failed to trace resource deployment: %w", err)
 76 | 			}
 77 | 
 78 | 			// For non-namespace resources, enhance with the actual resource data
 79 | 			if !strings.EqualFold(request.Resource, "namespace") {
 80 | 				// Get the full resource details
 81 | 				resource, err := h.k8sClient.GetResource(ctx, request.Resource, request.Namespace, request.Name)
 82 | 				if err == nil && resource != nil {
 83 | 					// Add the full resource details to the context
 84 | 					resourceData, err := utils.ToJSON(resource.Object)
 85 | 					if err == nil {
 86 | 						resourceInfo.ResourceData = resourceData
 87 | 
 88 | 						// Extract important deployment-specific information if available
 89 | 						if strings.EqualFold(request.Resource, "deployment") {
 90 | 							// Extract replicas info
 91 | 							specReplicas, found, _ := unstructured.NestedInt64(resource.Object, "spec", "replicas")
 92 | 							if found {
 93 | 								if resourceInfo.Metadata == nil {
 94 | 									resourceInfo.Metadata = make(map[string]interface{})
 95 | 								}
 96 | 								resourceInfo.Metadata["desiredReplicas"] = specReplicas
 97 | 							}
 98 | 
 99 | 							// Extract status replica counts
100 | 							statusReplicas, found, _ := unstructured.NestedInt64(resource.Object, "status", "replicas")
101 | 							if found {
102 | 								if resourceInfo.Metadata == nil {
103 | 									resourceInfo.Metadata = make(map[string]interface{})
104 | 								}
105 | 								resourceInfo.Metadata["currentReplicas"] = statusReplicas
106 | 							}
107 | 
108 | 							// Extract readyReplicas
109 | 							readyReplicas, found, _ := unstructured.NestedInt64(resource.Object, "status", "readyReplicas")
110 | 							if found {
111 | 								if resourceInfo.Metadata == nil {
112 | 									resourceInfo.Metadata = make(map[string]interface{})
113 | 								}
114 | 								resourceInfo.Metadata["readyReplicas"] = readyReplicas
115 | 							}
116 | 
117 | 							// Extract availableReplicas
118 | 							availableReplicas, found, _ := unstructured.NestedInt64(resource.Object, "status", "availableReplicas")
119 | 							if found {
120 | 								if resourceInfo.Metadata == nil {
121 | 									resourceInfo.Metadata = make(map[string]interface{})
122 | 								}
123 | 								resourceInfo.Metadata["availableReplicas"] = availableReplicas
124 | 							}
125 | 
126 | 							// Extract container info
127 | 							containers, found, _ := unstructured.NestedSlice(resource.Object, "spec", "template", "spec", "containers")
128 | 							if found {
129 | 								var containerInfo []map[string]interface{}
130 | 								for _, c := range containers {
131 | 									container, ok := c.(map[string]interface{})
132 | 									if !ok {
133 | 										continue
134 | 									}
135 | 
136 | 									containerData := map[string]interface{}{
137 | 										"name": container["name"],
138 | 									}
139 | 
140 | 									if image, ok := container["image"].(string); ok {
141 | 										containerData["image"] = image
142 | 									}
143 | 
144 | 									if resources, ok := container["resources"].(map[string]interface{}); ok {
145 | 										containerData["resources"] = resources
146 | 									}
147 | 
148 | 									containerInfo = append(containerInfo, containerData)
149 | 								}
150 | 
151 | 								if resourceInfo.Metadata == nil {
152 | 									resourceInfo.Metadata = make(map[string]interface{})
153 | 								}
154 | 								resourceInfo.Metadata["containers"] = containerInfo
155 | 							}
156 | 						}
157 | 					}
158 | 				}
159 | 			}
160 | 
161 | 			formattedContext, err := h.contextManager.FormatResourceContext(resourceInfo)
162 | 			if err != nil {
163 | 				return nil, fmt.Errorf("failed to format resource context: %w", err)
164 | 			}
165 | 
166 | 			resourceContext = formattedContext
167 | 		}
168 | 
169 | 	case "queryMergeRequest":
170 | 		// Analyze merge request
171 | 		resources, err := h.gitOpsCorrelator.AnalyzeMergeRequest(
172 | 			ctx,
173 | 			request.ProjectID,
174 | 			request.MergeRequestIID,
175 | 		)
176 | 		if err != nil {
177 | 			return nil, fmt.Errorf("failed to analyze merge request: %w", err)
178 | 		}
179 | 
180 | 		resourceContext, err = h.contextManager.CombineContexts(ctx, resources)
181 | 		if err != nil {
182 | 			return nil, fmt.Errorf("failed to combine resource contexts: %w", err)
183 | 		}
184 | 
185 | 	default:
186 | 		return nil, fmt.Errorf("unsupported action: %s", request.Action)
187 | 	}
188 | 
189 | 	// Generate prompts for Claude
190 | 	h.logger.Debug("Generating prompts for Claude")
191 | 	systemPrompt := h.promptGenerator.GenerateSystemPrompt()
192 | 	userPrompt := h.promptGenerator.GenerateUserPrompt(resourceContext, request.Query)
193 | 
194 | 	// Get completion from Claude
195 | 	h.logger.Debug("Sending request to Claude",
196 | 		"systemPromptLength", len(systemPrompt),
197 | 		"userPromptLength", len(userPrompt))
198 | 
199 | 	analysis, err := h.claudeProtocol.GetCompletion(ctx, systemPrompt, userPrompt)
200 | 	if err != nil {
201 | 		return nil, fmt.Errorf("failed to get completion from Claude: %w", err)
202 | 	}
203 | 
204 | 	// Build response
205 | 	response := &models.MCPResponse{
206 | 		Success:  true,
207 | 		Analysis: analysis,
208 | 		Message:  fmt.Sprintf("Successfully processed %s request in %v", request.Action, time.Since(startTime)),
209 | 	}
210 | 
211 | 	h.logger.Info("MCP request processed successfully",
212 | 		"action", request.Action,
213 | 		"duration", time.Since(startTime),
214 | 		"responseLength", len(analysis))
215 | 
216 | 	return response, nil
217 | }
218 | 
219 | // ProcessTroubleshootRequest processes a troubleshooting request with detected issues
220 | func (h *ProtocolHandler) ProcessTroubleshootRequest(ctx context.Context, request *models.MCPRequest, troubleshootResult *models.TroubleshootResult) (*models.MCPResponse, error) {
221 | 	startTime := time.Now()
222 | 	h.logger.Debug("Processing troubleshoot request")
223 | 
224 | 	// Extract issues and recommendations
225 | 	var issuesText string
226 | 	for i, issue := range troubleshootResult.Issues {
227 | 		issuesText += fmt.Sprintf("%d. %s (%s): %s\n",
228 | 			i+1,
229 | 			issue.Title,
230 | 			issue.Severity,
231 | 			issue.Description)
232 | 	}
233 | 
234 | 	var recommendationsText string
235 | 	for i, rec := range troubleshootResult.Recommendations {
236 | 		recommendationsText += fmt.Sprintf("%d. %s\n", i+1, rec)
237 | 	}
238 | 
239 | 	// Create a prompt for Claude with the troubleshooting results
240 | 	userPrompt := fmt.Sprintf(
241 | 		"I'm troubleshooting a Kubernetes %s named '%s' in namespace '%s'.\n\n"+
242 | 			"The following issues were detected:\n%s\n"+
243 | 			"General recommendations:\n%s\n\n"+
244 | 			"Based on these detected issues, please provide specific kubectl commands "+
245 | 			"that I can use to troubleshoot and fix the problems. %s",
246 | 		request.Resource,
247 | 		request.Name,
248 | 		request.Namespace,
249 | 		issuesText,
250 | 		recommendationsText,
251 | 		request.Query)
252 | 
253 | 	// Generate system prompt
254 | 	systemPrompt := h.promptGenerator.GenerateSystemPrompt()
255 | 
256 | 	// Get Claude's analysis
257 | 	h.logger.Debug("Sending troubleshoot request to Claude",
258 | 		"systemPromptLength", len(systemPrompt),
259 | 		"userPromptLength", len(userPrompt))
260 | 
261 | 	analysis, err := h.claudeProtocol.GetCompletion(ctx, systemPrompt, userPrompt)
262 | 	if err != nil {
263 | 		return nil, fmt.Errorf("failed to get completion for troubleshoot request: %w", err)
264 | 	}
265 | 
266 | 	// Create response
267 | 	response := &models.MCPResponse{
268 | 		Success:  true,
269 | 		Analysis: analysis,
270 | 		Message:  fmt.Sprintf("Successfully processed troubleshoot request in %v", time.Since(startTime)),
271 | 	}
272 | 
273 | 	h.logger.Info("Troubleshoot request processed successfully",
274 | 		"duration", time.Since(startTime),
275 | 		"responseLength", len(analysis))
276 | 
277 | 	return response, nil
278 | }
279 | 
280 | // WithCustomPrompt sets a custom base prompt template
281 | func (h *ProtocolHandler) WithCustomPrompt(template string) *ProtocolHandler {
282 | 	h.promptGenerator.WithBasePrompt(template)
283 | 	return h
284 | }
285 | 
286 | // WithMaxContextSize sets the maximum context size
287 | func (h *ProtocolHandler) WithMaxContextSize(size int) *ProtocolHandler {
288 | 	h.contextManager = NewContextManager(size, h.logger.Named("context"))
289 | 	return h
290 | }
291 | 
```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/mcp/namespace_analyzer.go:
--------------------------------------------------------------------------------

```go
  1 | package mcp
  2 | 
  3 | import (
  4 |     "context"
  5 |     "fmt"
  6 |     "strings"
  7 |     "time"
  8 | 
  9 |     "github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
 10 |     k8s "github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/k8s"
 11 | )
 12 | 
 13 | // NamespaceAnalysisResult contains the analysis of a namespace's resources
 14 | type NamespaceAnalysisResult struct {
 15 | 	Namespace           string                     `json:"namespace"`
 16 | 	ResourceCounts      map[string]int             `json:"resourceCounts"`
 17 | 	HealthStatus        map[string]map[string]int  `json:"healthStatus"`
 18 | 	ResourceRelationships []k8s.ResourceRelationship `json:"resourceRelationships"`
 19 | 	Issues              []models.Issue             `json:"issues"`
 20 | 	Recommendations     []string                   `json:"recommendations"`
 21 | 	Analysis            string                     `json:"analysis"`
 22 | }
 23 | 
 24 | // AnalyzeNamespace analyzes all resources in a namespace using Claude
 25 | func (h *ProtocolHandler) AnalyzeNamespace(ctx context.Context, namespace string) (*models.NamespaceAnalysisResult, error) {
 26 |     startTime := time.Now()
 27 |     h.logger.Info("Analyzing namespace", "namespace", namespace)
 28 |     
 29 |     // Get namespace topology
 30 |     topology, err := h.k8sClient.GetNamespaceTopology(ctx, namespace)
 31 |     if err != nil {
 32 |         return nil, fmt.Errorf("failed to get namespace topology: %w", err)
 33 |     }
 34 |     
 35 |     // Initialize result
 36 |     result := &models.NamespaceAnalysisResult{
 37 |         Namespace:      namespace,
 38 |         ResourceCounts: make(map[string]int),
 39 |         HealthStatus:   make(map[string]map[string]int),
 40 |         Issues:         []models.Issue{},
 41 |         Recommendations: []string{},
 42 |     }
 43 |     
 44 |     // Extract resource counts
 45 |     for kind, resources := range topology.Resources {
 46 |         result.ResourceCounts[kind] = len(resources)
 47 |     }
 48 |     
 49 |     // Extract health status
 50 |     for kind, statusMap := range topology.Health {
 51 |         healthCounts := make(map[string]int)
 52 |         for _, status := range statusMap {
 53 |             healthCounts[status]++
 54 |         }
 55 |         result.HealthStatus[kind] = healthCounts
 56 |     }
 57 |     
 58 |     // Add relationships - Convert from k8s.ResourceRelationship to models.ResourceRelationship
 59 |     for _, rel := range topology.Relationships {
 60 |         modelRel := models.ResourceRelationship{
 61 |             SourceKind:      rel.SourceKind,
 62 |             SourceName:      rel.SourceName,
 63 |             SourceNamespace: rel.SourceNamespace,
 64 |             TargetKind:      rel.TargetKind,
 65 |             TargetName:      rel.TargetName,
 66 |             TargetNamespace: rel.TargetNamespace,
 67 |             RelationType:    rel.RelationType,
 68 |         }
 69 |         result.ResourceRelationships = append(result.ResourceRelationships, modelRel)
 70 |     }
 71 | 	
 72 | 	// Get events for the namespace
 73 | 	events, err := h.k8sClient.GetNamespaceEvents(ctx, namespace)
 74 | 	if err != nil {
 75 | 		h.logger.Warn("Failed to get namespace events", "error", err)
 76 | 	}
 77 | 	
 78 | 	// Identify issues from events
 79 | 	for _, event := range events {
 80 | 		if event.Type == "Warning" {
 81 | 			issue := models.Issue{
 82 | 				Source:      "Kubernetes",
 83 | 				Severity:    "Warning",
 84 | 				Description: fmt.Sprintf("%s: %s", event.Reason, event.Message),
 85 | 			}
 86 | 			
 87 | 			// Categorize common issues
 88 | 			switch {
 89 | 			case strings.Contains(event.Reason, "Failed") && strings.Contains(event.Message, "ImagePull"):
 90 | 				issue.Category = "ImagePullError"
 91 | 				issue.Title = "Image Pull Failure"
 92 | 			
 93 | 			case strings.Contains(event.Reason, "Unhealthy"):
 94 | 				issue.Category = "HealthCheckFailure"
 95 | 				issue.Title = "Health Check Failure"
 96 | 			
 97 | 			case strings.Contains(event.Message, "memory"):
 98 | 				issue.Category = "ResourceIssue"
 99 | 				issue.Title = "Memory Resource Issue"
100 | 				
101 | 			case strings.Contains(event.Message, "cpu"):
102 | 				issue.Category = "ResourceIssue"
103 | 				issue.Title = "CPU Resource Issue"
104 | 				
105 | 			case strings.Contains(event.Reason, "BackOff"):
106 | 				issue.Category = "CrashLoopBackOff"
107 | 				issue.Title = "Container Crash Loop"
108 | 				
109 | 			default:
110 | 				issue.Category = "OtherWarning"
111 | 				issue.Title = "Kubernetes Warning"
112 | 			}
113 | 			
114 | 			result.Issues = append(result.Issues, issue)
115 | 		}
116 | 	}
117 | 	
118 | 	// Generate Claude analysis
119 | 	analysisPrompt := h.generateNamespaceAnalysisPrompt(namespace, topology, events)
120 | 	systemPrompt := h.promptGenerator.GenerateSystemPrompt()
121 | 	
122 | 	h.logger.Debug("Sending namespace analysis request to Claude",
123 | 		"namespace", namespace,
124 | 		"systemPromptLength", len(systemPrompt),
125 | 		"analysisPromptLength", len(analysisPrompt))
126 | 	
127 | 	analysis, err := h.claudeProtocol.GetCompletion(ctx, systemPrompt, analysisPrompt)
128 | 	if err != nil {
129 | 		return nil, fmt.Errorf("failed to get completion for namespace analysis: %w", err)
130 | 	}
131 | 	
132 | 	// Extract recommendations from analysis
133 | 	lines := strings.Split(analysis, "\n")
134 | 	inRecommendations := false
135 | 	
136 | 	for _, line := range lines {
137 | 		if strings.Contains(strings.ToLower(line), "recommendation") || 
138 | 		   strings.Contains(strings.ToLower(line), "recommendations") || 
139 | 		   strings.Contains(strings.ToLower(line), "suggest") {
140 | 			inRecommendations = true
141 | 			continue
142 | 		}
143 | 		
144 | 		if inRecommendations && strings.TrimSpace(line) != "" && !strings.HasPrefix(line, "#") {
145 | 			// Remove leading dash or number if it exists
146 | 			cleanLine := strings.TrimSpace(line)
147 | 			if strings.HasPrefix(cleanLine, "- ") {
148 | 				cleanLine = cleanLine[2:]
149 | 			} else if len(cleanLine) > 2 && strings.HasPrefix(cleanLine, "* ") {
150 | 				cleanLine = cleanLine[2:]
151 | 			} else if len(cleanLine) > 3 && 
152 | 			           ((cleanLine[0] >= '1' && cleanLine[0] <= '9') && 
153 | 			           (cleanLine[1] == '.' || cleanLine[1] == ')') && 
154 | 			           (cleanLine[2] == ' ')) {
155 | 				cleanLine = cleanLine[3:]
156 | 			}
157 | 			
158 | 			if cleanLine != "" && len(result.Recommendations) < 10 {
159 | 				result.Recommendations = append(result.Recommendations, cleanLine)
160 | 			}
161 | 		}
162 | 	}
163 | 	
164 | 	result.Analysis = analysis
165 | 	
166 | 	h.logger.Info("Namespace analysis completed", 
167 | 		"namespace", namespace,
168 | 		"duration", time.Since(startTime),
169 | 		"issueCount", len(result.Issues),
170 | 		"recommendationCount", len(result.Recommendations))
171 | 	
172 | 	return result, nil
173 | }
174 | 
175 | // generateNamespaceAnalysisPrompt creates a prompt for namespace analysis
176 | func (h *ProtocolHandler) generateNamespaceAnalysisPrompt(namespace string, topology *k8s.NamespaceTopology, events []models.K8sEvent) string {
177 | 	// Start with namespace overview
178 | 	prompt := fmt.Sprintf("# Namespace Analysis: %s\n\n", namespace)
179 | 	
180 | 	// Add resource summary
181 | 	prompt += "## Resource Summary\n\n"
182 | 	for kind, resources := range topology.Resources {
183 | 		prompt += fmt.Sprintf("- %s: %d resources\n", kind, len(resources))
184 | 	}
185 | 	prompt += "\n"
186 | 	
187 | 	// Add health status summary
188 | 	prompt += "## Health Status\n\n"
189 | 	for kind, statusMap := range topology.Health {
190 | 		prompt += fmt.Sprintf("### %s Health\n", kind)
191 | 		
192 | 		// Count the statuses
193 | 		healthCounts := make(map[string]int)
194 | 		for _, status := range statusMap {
195 | 			healthCounts[status]++
196 | 		}
197 | 		
198 | 		// List the counts
199 | 		for status, count := range healthCounts {
200 | 			prompt += fmt.Sprintf("- %s: %d resources\n", status, count)
201 | 		}
202 | 		
203 | 		// List unhealthy resources
204 | 		unhealthyResources := []string{}
205 | 		for name, status := range statusMap {
206 | 			if status == "unhealthy" {
207 | 				unhealthyResources = append(unhealthyResources, name)
208 | 			}
209 | 		}
210 | 		
211 | 		if len(unhealthyResources) > 0 {
212 | 			prompt += "\nUnhealthy resources:\n"
213 | 			for _, name := range unhealthyResources {
214 | 				prompt += fmt.Sprintf("- %s\n", name)
215 | 			}
216 | 		}
217 | 		
218 | 		prompt += "\n"
219 | 	}
220 | 	
221 | 	// Add relationship summary
222 | 	if len(topology.Relationships) > 0 {
223 | 		prompt += "## Resource Relationships\n\n"
224 | 		
225 | 		// Group by relationship type
226 | 		relationshipsByType := make(map[string][]string)
227 | 		for _, rel := range topology.Relationships {
228 | 			key := rel.RelationType
229 | 			relationshipsByType[key] = append(
230 | 				relationshipsByType[key], 
231 | 				fmt.Sprintf("%s/%s -> %s/%s", 
232 | 					rel.SourceKind, rel.SourceName,
233 | 					rel.TargetKind, rel.TargetName))
234 | 		}
235 | 		
236 | 		// List relationships by type
237 | 		for relType, relations := range relationshipsByType {
238 | 			prompt += fmt.Sprintf("### %s Relationships\n", strings.Title(relType))
239 | 			for _, rel := range relations {
240 | 				prompt += fmt.Sprintf("- %s\n", rel)
241 | 			}
242 | 			prompt += "\n"
243 | 		}
244 | 	}
245 | 	
246 | 	// Add recent events
247 | 	if len(events) > 0 {
248 | 		prompt += "## Recent Events\n\n"
249 | 		
250 | 		// Group events by type
251 | 		warningEvents := []models.K8sEvent{}
252 | 		normalEvents := []models.K8sEvent{}
253 | 		
254 | 		for _, event := range events {
255 | 			if event.Type == "Warning" {
256 | 				warningEvents = append(warningEvents, event)
257 | 			} else {
258 | 				normalEvents = append(normalEvents, event)
259 | 			}
260 | 		}
261 | 		
262 | 		// Add warning events first (limited to 10)
263 | 		if len(warningEvents) > 0 {
264 | 			prompt += "### Warning Events\n"
265 | 			count := 0
266 | 			for _, event := range warningEvents {
267 | 				if count >= 10 {
268 | 					break
269 | 				}
270 | 				prompt += fmt.Sprintf("- [%s] %s: %s (%s)\n", 
271 | 					event.LastTime.Format(time.RFC3339),
272 | 					event.Reason,
273 | 					event.Message,
274 | 					fmt.Sprintf("%s/%s", event.Object.Kind, event.Object.Name))
275 | 				count++
276 | 			}
277 | 			prompt += "\n"
278 | 		}
279 | 		
280 | 		// Add a few normal events (limited to 5)
281 | 		if len(normalEvents) > 0 {
282 | 			prompt += "### Normal Events\n"
283 | 			count := 0
284 | 			for _, event := range normalEvents {
285 | 				if count >= 5 {
286 | 					break
287 | 				}
288 | 				prompt += fmt.Sprintf("- [%s] %s: %s (%s)\n", 
289 | 					event.LastTime.Format(time.RFC3339),
290 | 					event.Reason,
291 | 					event.Message,
292 | 					fmt.Sprintf("%s/%s", event.Object.Kind, event.Object.Name))
293 | 				count++
294 | 			}
295 | 			prompt += "\n"
296 | 		}
297 | 	}
298 | 	
299 | 	// Add analysis request
300 | 	prompt += "## Analysis Request\n\n"
301 | 	prompt += "Based on the information above, please provide a comprehensive analysis of this Kubernetes namespace, including:\n\n"
302 | 	prompt += "1. Overall health assessment\n"
303 | 	prompt += "2. Identification of any issues or problems\n"
304 | 	prompt += "3. Analysis of resource relationships and dependencies\n"
305 | 	prompt += "4. Potential bottlenecks or misconfigurations\n"
306 | 	prompt += "5. Security concerns (if any can be identified)\n"
307 | 	prompt += "6. Specific recommendations for improvement\n\n"
308 | 	prompt += "Please format your analysis with clear sections and provide specific, actionable recommendations that would help improve the reliability, efficiency, and security of this namespace."
309 | 	
310 | 	return prompt
311 | }
312 | 
```

--------------------------------------------------------------------------------
/docs/src/content/docs/installation.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: Installation Guide
  3 | description: Comprehensive guide for installing and configuring the Kubernetes Claude MCP server in various environments.
  4 | date: 2025-03-01
  5 | order: 3
  6 | tags: ['installation', 'deployment']
  7 | ---
  8 | 
  9 | # Installation Guide
 10 | 
 11 | This guide provides detailed instructions for installing Kubernetes Claude MCP in different environments. Choose the method that best suits your needs.
 12 | 
 13 | ## Prerequisites
 14 | 
 15 | Before installing Kubernetes Claude MCP, ensure you have:
 16 | 
 17 | - Access to a Kubernetes cluster (v1.19+)
 18 | - kubectl configured to access your cluster
 19 | - Claude API key from Anthropic
 20 | - Optional: ArgoCD instance (for GitOps integration)
 21 | - Optional: GitLab access (for commit analysis)
 22 | 
 23 | ## Installation Methods
 24 | 
 25 | There are several ways to install Kubernetes Claude MCP:
 26 | 
 27 | 1. [Docker Compose](#docker-compose) (for development/testing)
 28 | 2. [Kubernetes Deployment](#kubernetes-deployment) (recommended for production)
 29 | 3. [Helm Chart](#helm-chart) (easiest for Kubernetes)
 30 | 4. [Manual Binary](#manual-binary) (for custom environments)
 31 | 
 32 | ## Docker Compose
 33 | 
 34 | Docker Compose is ideal for local development and testing.
 35 | 
 36 | ### Step 1: Clone the Repository
 37 | 
 38 | ```bash
 39 | git clone https://github.com/blankcut/kubernetes-mcp-server.git
 40 | cd kubernetes-mcp-server
 41 | ```
 42 | 
 43 | ### Step 2: Configure Environment Variables
 44 | 
 45 | Create a `.env` file with your credentials:
 46 | 
 47 | ```bash
 48 | CLAUDE_API_KEY=your_claude_api_key
 49 | ARGOCD_USERNAME=your_argocd_username
 50 | ARGOCD_PASSWORD=your_argocd_password
 51 | GITLAB_AUTH_TOKEN=your_gitlab_token
 52 | API_KEY=your_api_key_for_server_access
 53 | ```
 54 | 
 55 | ### Step 3: Configure the Server
 56 | 
 57 | Create or modify `config.yaml`:
 58 | 
 59 | ```yaml
 60 | server:
 61 |   address: ":8080"
 62 |   readTimeout: 30
 63 |   writeTimeout: 60
 64 |   auth:
 65 |     apiKey: "${API_KEY}"
 66 | 
 67 | kubernetes:
 68 |   kubeconfig: ""
 69 |   inCluster: false
 70 |   defaultContext: ""
 71 |   defaultNamespace: "default"
 72 | 
 73 | argocd:
 74 |   url: "${ARGOCD_URL}"
 75 |   authToken: "${ARGOCD_AUTH_TOKEN}"
 76 |   username: "${ARGOCD_USERNAME}"
 77 |   password: "${ARGOCD_PASSWORD}"
 78 |   insecure: true
 79 | 
 80 | gitlab:
 81 |   url: "${GITLAB_URL}"
 82 |   authToken: "${GITLAB_AUTH_TOKEN}"
 83 |   apiVersion: "v4"
 84 |   projectPath: "${PROJECT_PATH}"
 85 | 
 86 | claude:
 87 |   apiKey: "${CLAUDE_API_KEY}"
 88 |   baseURL: "https://api.anthropic.com"
 89 |   modelID: "claude-3-haiku-20240307"
 90 |   maxTokens: 4096
 91 |   temperature: 0.7
 92 | ```
 93 | 
 94 | ### Step 4: Start the Service
 95 | 
 96 | ```bash
 97 | docker-compose up -d
 98 | ```
 99 | 
100 | The server will be available at http://localhost:8080.
101 | 
102 | ## Kubernetes Deployment
103 | 
104 | For production environments, deploying to Kubernetes is recommended.
105 | 
106 | ### Step 1: Create a Namespace
107 | 
108 | ```bash
109 | kubectl create namespace mcp-system
110 | ```
111 | 
112 | ### Step 2: Create Secrets
113 | 
114 | ```bash
115 | kubectl create secret generic mcp-secrets \
116 |   --namespace mcp-system \
117 |   --from-literal=claude-api-key=your_claude_api_key \
118 |   --from-literal=argocd-username=your_argocd_username \
119 |   --from-literal=argocd-password=your_argocd_password \
120 |   --from-literal=gitlab-token=your_gitlab_token \
121 |   --from-literal=api-key=your_api_key_for_server_access
122 | ```
123 | 
124 | ### Step 3: Create ConfigMap
125 | 
126 | ```bash
127 | kubectl create configmap mcp-config \
128 |   --namespace mcp-system \
129 |   --from-file=config.yaml
130 | ```
131 | 
132 | ### Step 4: Apply Deployment Manifest
133 | 
134 | Create a file named `deployment.yaml`:
135 | 
136 | ```yaml
137 | apiVersion: apps/v1
138 | kind: Deployment
139 | metadata:
140 |   name: kubernetes-mcp-server
141 |   namespace: mcp-system
142 |   labels:
143 |     app: kubernetes-mcp-server
144 | spec:
145 |   replicas: 1
146 |   selector:
147 |     matchLabels:
148 |       app: kubernetes-mcp-server
149 |   template:
150 |     metadata:
151 |       labels:
152 |         app: kubernetes-mcp-server
153 |     spec:
154 |       serviceAccountName: mcp-service-account
155 |       containers:
156 |       - name: server
157 |         image: blankcut/kubernetes-mcp-server:latest
158 |         imagePullPolicy: Always
159 |         ports:
160 |         - containerPort: 8080
161 |         env:
162 |         - name: CLAUDE_API_KEY
163 |           valueFrom:
164 |             secretKeyRef:
165 |               name: mcp-secrets
166 |               key: claude-api-key
167 |         - name: ARGOCD_USERNAME
168 |           valueFrom:
169 |             secretKeyRef:
170 |               name: mcp-secrets
171 |               key: argocd-username
172 |               optional: true
173 |         - name: ARGOCD_PASSWORD
174 |           valueFrom:
175 |             secretKeyRef:
176 |               name: mcp-secrets
177 |               key: argocd-password
178 |               optional: true
179 |         - name: GITLAB_AUTH_TOKEN
180 |           valueFrom:
181 |             secretKeyRef:
182 |               name: mcp-secrets
183 |               key: gitlab-token
184 |               optional: true
185 |         - name: API_KEY
186 |           valueFrom:
187 |             secretKeyRef:
188 |               name: mcp-secrets
189 |               key: api-key
190 |         volumeMounts:
191 |         - name: config
192 |           mountPath: /app/config.yaml
193 |           subPath: config.yaml
194 |       volumes:
195 |       - name: config
196 |         configMap:
197 |           name: mcp-config
198 | ---
199 | apiVersion: v1
200 | kind: Service
201 | metadata:
202 |   name: kubernetes-mcp-server
203 |   namespace: mcp-system
204 | spec:
205 |   selector:
206 |     app: kubernetes-mcp-server
207 |   ports:
208 |   - port: 80
209 |     targetPort: 8080
210 |   type: ClusterIP
211 | ---
212 | apiVersion: v1
213 | kind: ServiceAccount
214 | metadata:
215 |   name: mcp-service-account
216 |   namespace: mcp-system
217 | ---
218 | apiVersion: rbac.authorization.k8s.io/v1
219 | kind: ClusterRole
220 | metadata:
221 |   name: mcp-cluster-role
222 | rules:
223 | - apiGroups: [""]
224 |   resources: ["pods", "services", "events", "configmaps", "secrets", "namespaces", "nodes"]
225 |   verbs: ["get", "list", "watch"]
226 | - apiGroups: ["apps"]
227 |   resources: ["deployments", "statefulsets", "daemonsets", "replicasets"]
228 |   verbs: ["get", "list", "watch"]
229 | - apiGroups: ["batch"]
230 |   resources: ["jobs", "cronjobs"]
231 |   verbs: ["get", "list", "watch"]
232 | - apiGroups: ["networking.k8s.io"]
233 |   resources: ["ingresses"]
234 |   verbs: ["get", "list", "watch"]
235 | ---
236 | apiVersion: rbac.authorization.k8s.io/v1
237 | kind: ClusterRoleBinding
238 | metadata:
239 |   name: mcp-role-binding
240 | subjects:
241 | - kind: ServiceAccount
242 |   name: mcp-service-account
243 |   namespace: mcp-system
244 | roleRef:
245 |   kind: ClusterRole
246 |   name: mcp-cluster-role
247 |   apiGroup: rbac.authorization.k8s.io
248 | ```
249 | 
250 | Apply the configuration:
251 | 
252 | ```bash
253 | kubectl apply -f deployment.yaml
254 | ```
255 | 
256 | ### Step 5: Access the Server
257 | 
258 | Create an Ingress or port-forward to access the server:
259 | 
260 | ```bash
261 | kubectl port-forward -n mcp-system svc/kubernetes-mcp-server 8080:80
262 | ```
263 | 
264 | ## Helm Chart
265 | 
266 | For Kubernetes users, the Helm chart provides the easiest installation method.
267 | 
268 | ### Step 1: Add the Helm Repository
269 | 
270 | ```bash
271 | helm repo add blankcut https://blankcut.github.io/helm-charts
272 | helm repo update
273 | ```
274 | 
275 | ### Step 2: Configure Values
276 | 
277 | Create a `values.yaml` file:
278 | 
279 | ```yaml
280 | image:
281 |   repository: blankcut/kubernetes-mcp-server
282 |   tag: latest
283 | 
284 | config:
285 |   server:
286 |     address: ":8080"
287 |   kubernetes:
288 |     inCluster: true
289 |     defaultNamespace: "default"
290 |   argocd:
291 |     url: "https://argocd.example.com"
292 |   gitlab:
293 |     url: "https://gitlab.com"
294 |   claude:
295 |     modelID: "claude-3-haiku-20240307"
296 | 
297 | secrets:
298 |   claude:
299 |     apiKey: "your_claude_api_key"
300 |   argocd:
301 |     username: "your_argocd_username"
302 |     password: "your_argocd_password"
303 |   gitlab:
304 |     authToken: "your_gitlab_token"
305 | 
306 | service:
307 |   type: ClusterIP
308 | 
309 | ingress:
310 |   enabled: false
311 |   # Uncomment to enable ingress
312 |   # hosts:
313 |   #   - host: mcp.example.com
314 |   #     paths:
315 |   #       - path: /
316 |   #         pathType: Prefix
317 | ```
318 | 
319 | ### Step 3: Install the Chart
320 | 
321 | ```bash
322 | helm install kubernetes-mcp-server blankcut/kubernetes-claude-mcp -f values.yaml -n mcp-system
323 | ```
324 | 
325 | ### Step 4: Verify the Installation
326 | 
327 | ```bash
328 | kubectl get pods -n mcp-system
329 | ```
330 | 
331 | ## Manual Binary
332 | 
333 | For environments where Docker or Kubernetes is not available, you can run the binary directly.
334 | 
335 | ### Step 1: Download the Latest Release
336 | 
337 | Visit the [Releases page](https://github.com/blankcut/kubernetes-mcp-server/releases) and download the appropriate binary for your platform.
338 | 
339 | ### Step 2: Make the Binary Executable
340 | 
341 | ```bash
342 | chmod +x mcp-server
343 | ```
344 | 
345 | ### Step 3: Create Configuration File
346 | 
347 | Create a `config.yaml` file in the same directory:
348 | 
349 | ```yaml
350 | server:
351 |   address: ":8080"
352 |   readTimeout: 30
353 |   writeTimeout: 60
354 |   auth:
355 |     apiKey: "your_api_key_for_server_access"
356 | 
357 | kubernetes:
358 |   kubeconfig: "/path/to/.kube/config"  # Path to your kubeconfig file
359 |   inCluster: false
360 |   defaultContext: ""
361 |   defaultNamespace: "default"
362 | 
363 | argocd:
364 |   url: "https://argocd.example.com"
365 |   username: "your_argocd_username"
366 |   password: "your_argocd_password"
367 |   insecure: true
368 | 
369 | gitlab:
370 |   url: "https://gitlab.com"
371 |   authToken: "your_gitlab_token"
372 |   apiVersion: "v4"
373 |   projectPath: ""
374 | 
375 | claude:
376 |   apiKey: "your_claude_api_key"
377 |   baseURL: "https://api.anthropic.com"
378 |   modelID: "claude-3-haiku-20240307"
379 |   maxTokens: 4096
380 |   temperature: 0.7
381 | ```
382 | 
383 | ### Step 4: Run the Server
384 | 
385 | ```bash
386 | export CLAUDE_API_KEY=your_claude_api_key
387 | export API_KEY=your_api_key_for_server
388 | ./mcp-server --config config.yaml
389 | ```
390 | 
391 | ## Verifying the Installation
392 | 
393 | To verify your installation is working correctly:
394 | 
395 | 1. Check the health endpoint:
396 | 
397 | ```bash
398 | curl http://localhost:8080/api/v1/health
399 | ```
400 | 
401 | 2. List Kubernetes namespaces:
402 | 
403 | ```bash
404 | curl -H "X-API-Key: your_api_key" http://localhost:8080/api/v1/namespaces
405 | ```
406 | 
407 | 3. Test a resource query:
408 | 
409 | ```bash
410 | curl -X POST \
411 |   -H "Content-Type: application/json" \
412 |   -H "X-API-Key: your_api_key" \
413 |   -d '{
414 |     "action": "queryResource",
415 |     "resource": "pod",
416 |     "name": "example-pod",
417 |     "namespace": "default",
418 |     "query": "Is this pod healthy?"
419 |   }' \
420 |   http://localhost:8080/api/v1/mcp/resource
421 | ```
422 | 
423 | ## Security Considerations
424 | 
425 | When deploying Kubernetes Claude MCP, consider the following security best practices:
426 | 
427 | 1. **API Access**: Use a strong API key and restrict access to the server.
428 | 2. **Kubernetes Permissions**: Use a service account with the minimum required permissions.
429 | 3. **Secrets Management**: Store credentials in Kubernetes Secrets or a secure vault.
430 | 4. **Network Isolation**: Consider network policies to limit access to the server.
431 | 5. **TLS**: Use TLS to encrypt connections to the server.
432 | 
433 | For more security recommendations, see the [Security Best Practices](/docs/security-best-practices) guide.
434 | 
435 | ## Troubleshooting
436 | 
437 | If you encounter issues during installation, check:
438 | 
439 | 1. **Logs**: View server logs for error messages
440 |    ```bash
441 |    # For Docker Compose
442 |    docker-compose logs
443 |    
444 |    # For Kubernetes
445 |    kubectl logs -n mcp-system deployment/kubernetes-mcp-server
446 |    ```
447 | 
448 | 2. **Configuration**: Verify your `config.yaml` has the correct settings
449 | 3. **Connectivity**: Ensure the server can connect to Kubernetes, ArgoCD, and GitLab
450 | 4. **API Key**: Verify you're using the correct API key in requests
451 | 
452 | For more troubleshooting tips, see the [Troubleshooting](/docs/troubleshooting-resources) guide.
453 | 
454 | ## Next Steps
455 | 
456 | After successful installation, continue with:
457 | 
458 | - [Configuration Guide](/docs/configuration) - Configure the server for your environment
459 | - [API Reference](/docs/api-overview) - Explore the API endpoints
460 | - [Examples](/docs/examples/basic-usage) - See examples of common use cases
```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/auth/credentials.go:
--------------------------------------------------------------------------------

```go
  1 | package auth
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"fmt"
  6 | 	"os"
  7 | 	"sync"
  8 | 	"time"
  9 | 
 10 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/config"
 11 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
 12 | )
 13 | 
 14 | // ServiceType represents the type of service requiring credentials
 15 | type ServiceType string
 16 | 
 17 | const (
 18 | 	ServiceKubernetes ServiceType = "kubernetes"
 19 | 	ServiceArgoCD     ServiceType = "argocd"
 20 | 	ServiceGitLab     ServiceType = "gitlab"
 21 | 	ServiceClaude     ServiceType = "claude"
 22 | )
 23 | 
 24 | // Credentials stores authentication information for various services
 25 | type Credentials struct {
 26 | 	// API tokens, oauth tokens, etc.
 27 | 	Token       string
 28 | 	APIKey      string
 29 | 	Username    string
 30 | 	Password    string
 31 | 	Certificate []byte
 32 | 	PrivateKey  []byte
 33 | 	ExpiresAt time.Time
 34 | }
 35 | 
 36 | // IsExpired checks if the credentials are expired
 37 | func (c *Credentials) IsExpired() bool {
 38 | 	// If no expiration time is set, we'll assume credentials don't expire
 39 | 	if c.ExpiresAt.IsZero() {
 40 | 		return false
 41 | 	}
 42 | 	
 43 | 	// Check if current time is past the expiration time
 44 | 	return time.Now().After(c.ExpiresAt)
 45 | }
 46 | 
 47 | // CredentialProvider manages credentials for various services
 48 | type CredentialProvider struct {
 49 | 	mu          sync.RWMutex
 50 | 	credentials map[ServiceType]*Credentials
 51 | 	config      *config.Config
 52 | 	logger      *logging.Logger
 53 | 	secretsManager *SecretsManager
 54 | 	vaultManager   *VaultManager
 55 | }
 56 | 
 57 | // NewCredentialProvider creates a new credential provider
 58 | func NewCredentialProvider(cfg *config.Config) *CredentialProvider {
 59 | 	logger := logging.NewLogger().Named("auth")
 60 | 	
 61 | 	return &CredentialProvider{
 62 | 		credentials: make(map[ServiceType]*Credentials),
 63 | 		config:      cfg,
 64 | 		logger:      logger,
 65 | 		secretsManager: NewSecretsManager(logger),
 66 | 		vaultManager: NewVaultManager(logger),
 67 | 	}
 68 | }
 69 | 
 70 | // LoadCredentials loads all service credentials based on configuration
 71 | func (p *CredentialProvider) LoadCredentials(ctx context.Context) error {
 72 | 	// Load credentials for each service type based on config
 73 | 	if err := p.loadKubernetesCredentials(ctx); err != nil {
 74 | 		return fmt.Errorf("failed to load Kubernetes credentials: %w", err)
 75 | 	}
 76 | 
 77 | 	if err := p.loadArgoCDCredentials(ctx); err != nil {
 78 | 		return fmt.Errorf("failed to load ArgoCD credentials: %w", err)
 79 | 	}
 80 | 
 81 | 	if err := p.loadGitLabCredentials(ctx); err != nil {
 82 | 		return fmt.Errorf("failed to load GitLab credentials: %w", err)
 83 | 	}
 84 | 
 85 | 	if err := p.loadClaudeCredentials(ctx); err != nil {
 86 | 		return fmt.Errorf("failed to load Claude credentials: %w", err)
 87 | 	}
 88 | 
 89 | 	return nil
 90 | }
 91 | 
 92 | // GetCredentials returns credentials for the specified service
 93 | func (p *CredentialProvider) GetCredentials(serviceType ServiceType) (*Credentials, error) {
 94 | 	p.mu.RLock()
 95 | 	defer p.mu.RUnlock()
 96 | 
 97 | 	creds, ok := p.credentials[serviceType]
 98 | 	if !ok {
 99 | 		return nil, fmt.Errorf("credentials not found for service: %s", serviceType)
100 | 	}
101 | 	
102 | 	// Check if credentials are expired and need refresh
103 | 	if creds.IsExpired() {
104 | 		p.mu.RUnlock() // Release read lock
105 | 		
106 | 		// Acquire write lock for refresh
107 | 		p.mu.Lock()
108 | 		defer p.mu.Unlock()
109 | 		
110 | 		// Check again in case another goroutine refreshed while we were waiting
111 | 		if creds.IsExpired() {
112 | 			p.logger.Info("Refreshing expired credentials", "serviceType", serviceType)
113 | 			if err := p.RefreshCredentials(context.Background(), serviceType); err != nil {
114 | 				return nil, fmt.Errorf("failed to refresh expired credentials: %w", err)
115 | 			}
116 | 			creds = p.credentials[serviceType]
117 | 		}
118 | 	}
119 | 
120 | 	return creds, nil
121 | }
122 | 
123 | // loadKubernetesCredentials loads Kubernetes authentication credentials
124 | func (p *CredentialProvider) loadKubernetesCredentials(ctx context.Context) error {
125 | 	p.mu.Lock()
126 | 	defer p.mu.Unlock()
127 | 
128 | 	// For Kubernetes, we primarily rely on kubeconfig or in-cluster config
129 | 	// We won't need to store explicit credentials
130 | 	p.credentials[ServiceKubernetes] = &Credentials{}
131 | 	return nil
132 | }
133 | 
134 | // loadArgoCDCredentials loads ArgoCD authentication credentials
135 | func (p *CredentialProvider) loadArgoCDCredentials(ctx context.Context) error {
136 | 	p.mu.Lock()
137 | 	defer p.mu.Unlock()
138 | 
139 | 	// Try to load from secrets manager if available
140 | 	if p.secretsManager != nil && p.secretsManager.IsAvailable() {
141 | 		creds, err := p.secretsManager.GetCredentials(ctx, "argocd")
142 | 		if err == nil && creds != nil {
143 | 			p.credentials[ServiceArgoCD] = creds
144 | 			p.logger.Info("Loaded ArgoCD credentials from secrets manager")
145 | 			return nil
146 | 		}
147 | 	}
148 | 	
149 | 	// Try to load from vault if available
150 | 	if p.vaultManager != nil && p.vaultManager.IsAvailable() {
151 | 		creds, err := p.vaultManager.GetCredentials(ctx, "argocd")
152 | 		if err == nil && creds != nil {
153 | 			p.credentials[ServiceArgoCD] = creds
154 | 			p.logger.Info("Loaded ArgoCD credentials from vault")
155 | 			return nil
156 | 		}
157 | 	}
158 | 
159 | 	// Primary source: Environment variables
160 | 	token := os.Getenv("ARGOCD_AUTH_TOKEN")
161 | 	if token != "" {
162 | 		p.credentials[ServiceArgoCD] = &Credentials{
163 | 			Token: token,
164 | 		}
165 | 		p.logger.Info("Loaded ArgoCD credentials from environment")
166 | 		return nil
167 | 	}
168 | 
169 | 	// Secondary source: Config file
170 | 	if p.config.ArgoCD.AuthToken != "" {
171 | 		p.credentials[ServiceArgoCD] = &Credentials{
172 | 			Token: p.config.ArgoCD.AuthToken,
173 | 		}
174 | 		p.logger.Info("Loaded ArgoCD credentials from config file")
175 | 		return nil
176 | 	}
177 | 
178 | 	// Tertiary source: Username/password...
179 | 	username := os.Getenv("ARGOCD_USERNAME")
180 | 	password := os.Getenv("ARGOCD_PASSWORD")
181 | 	if username != "" && password != "" {
182 | 		p.credentials[ServiceArgoCD] = &Credentials{
183 | 			Username: username,
184 | 			Password: password,
185 | 		}
186 | 		p.logger.Info("Loaded ArgoCD username/password from environment")
187 | 		return nil
188 | 	}
189 | 
190 | 	// Final fallback to config
191 | 	if p.config.ArgoCD.Username != "" && p.config.ArgoCD.Password != "" {
192 | 		p.credentials[ServiceArgoCD] = &Credentials{
193 | 			Username: p.config.ArgoCD.Username,
194 | 			Password: p.config.ArgoCD.Password,
195 | 		}
196 | 		p.logger.Info("Loaded ArgoCD username/password from config file")
197 | 		return nil
198 | 	}
199 | 
200 | 	p.logger.Warn("No ArgoCD credentials found, continuing without them")
201 | 	// We don't want to fail if ArgoCD credentials are not found
202 | 	// since ArgoCD integration is optional
203 | 	p.credentials[ServiceArgoCD] = &Credentials{}
204 | 	return nil
205 | }
206 | 
207 | // loadGitLabCredentials loads GitLab authentication credentials
208 | func (p *CredentialProvider) loadGitLabCredentials(ctx context.Context) error {
209 | 	p.mu.Lock()
210 | 	defer p.mu.Unlock()
211 | 
212 | 	// Try to load from secrets manager if available
213 | 	if p.secretsManager != nil && p.secretsManager.IsAvailable() {
214 | 		creds, err := p.secretsManager.GetCredentials(ctx, "gitlab")
215 | 		if err == nil && creds != nil {
216 | 			p.credentials[ServiceGitLab] = creds
217 | 			p.logger.Info("Loaded GitLab credentials from secrets manager")
218 | 			return nil
219 | 		}
220 | 	}
221 | 	
222 | 	// Try to load from vault if available
223 | 	if p.vaultManager != nil && p.vaultManager.IsAvailable() {
224 | 		creds, err := p.vaultManager.GetCredentials(ctx, "gitlab")
225 | 		if err == nil && creds != nil {
226 | 			p.credentials[ServiceGitLab] = creds
227 | 			p.logger.Info("Loaded GitLab credentials from vault")
228 | 			return nil
229 | 		}
230 | 	}
231 | 
232 | 	// Primary source: Environment variables
233 | 	token := os.Getenv("GITLAB_AUTH_TOKEN")
234 | 	if token != "" {
235 | 		p.credentials[ServiceGitLab] = &Credentials{
236 | 			Token: token,
237 | 		}
238 | 		p.logger.Info("Loaded GitLab credentials from environment")
239 | 		return nil
240 | 	}
241 | 
242 | 	// Secondary source: Config file
243 | 	if p.config.GitLab.AuthToken != "" {
244 | 		p.credentials[ServiceGitLab] = &Credentials{
245 | 			Token: p.config.GitLab.AuthToken,
246 | 		}
247 | 		p.logger.Info("Loaded GitLab credentials from config file")
248 | 		return nil
249 | 	}
250 | 
251 | 	p.logger.Warn("No GitLab credentials found, continuing without them")
252 | 	// We don't want to fail if GitLab credentials are not found
253 | 	// since GitLab integration is optional
254 | 	p.credentials[ServiceGitLab] = &Credentials{}
255 | 	return nil
256 | }
257 | 
258 | // loadClaudeCredentials loads Claude API credentials
259 | func (p *CredentialProvider) loadClaudeCredentials(ctx context.Context) error {
260 | 	p.mu.Lock()
261 | 	defer p.mu.Unlock()
262 | 
263 | 	// Try to load from secrets manager if available
264 | 	if p.secretsManager != nil && p.secretsManager.IsAvailable() {
265 | 		creds, err := p.secretsManager.GetCredentials(ctx, "claude")
266 | 		if err == nil && creds != nil {
267 | 			p.credentials[ServiceClaude] = creds
268 | 			p.logger.Info("Loaded Claude credentials from secrets manager")
269 | 			return nil
270 | 		}
271 | 	}
272 | 	
273 | 	// Try to load from vault if available
274 | 	if p.vaultManager != nil && p.vaultManager.IsAvailable() {
275 | 		creds, err := p.vaultManager.GetCredentials(ctx, "claude")
276 | 		if err == nil && creds != nil {
277 | 			p.credentials[ServiceClaude] = creds
278 | 			p.logger.Info("Loaded Claude credentials from vault")
279 | 			return nil
280 | 		}
281 | 	}
282 | 
283 | 	// Primary source: Environment variables
284 | 	apiKey := os.Getenv("CLAUDE_API_KEY")
285 | 	if apiKey != "" {
286 | 		p.credentials[ServiceClaude] = &Credentials{
287 | 			APIKey: apiKey,
288 | 		}
289 | 		p.logger.Info("Loaded Claude credentials from environment")
290 | 		return nil
291 | 	}
292 | 
293 | 	// Secondary source: Config file
294 | 	if p.config.Claude.APIKey != "" {
295 | 		p.credentials[ServiceClaude] = &Credentials{
296 | 			APIKey: p.config.Claude.APIKey,
297 | 		}
298 | 		p.logger.Info("Loaded Claude credentials from config file")
299 | 		return nil
300 | 	}
301 | 
302 | 	p.logger.Warn("No Claude API key found")
303 | 	return fmt.Errorf("no Claude API key found")
304 | }
305 | 
306 | // RefreshCredentials refreshes credentials for a specific service (for tokens that expire)
307 | func (p *CredentialProvider) RefreshCredentials(ctx context.Context, serviceType ServiceType) error {
308 | 	// Implement credential refresh logic based on service type
309 | 	switch serviceType {
310 | 	case ServiceArgoCD:
311 | 		return p.refreshArgoCDToken(ctx)
312 | 	default:
313 | 		p.logger.Debug("No refresh needed for service", "serviceType", serviceType)
314 | 		return nil // No refresh needed for other services
315 | 	}
316 | }
317 | 
318 | // refreshArgoCDToken refreshes the ArgoCD token if using username/password auth
319 | func (p *CredentialProvider) refreshArgoCDToken(ctx context.Context) error {
320 | 	p.mu.Lock()
321 | 	defer p.mu.Unlock()
322 | 
323 | 	creds, ok := p.credentials[ServiceArgoCD]
324 | 	if !ok {
325 | 		return fmt.Errorf("ArgoCD credentials not found")
326 | 	}
327 | 
328 | 	// If using token authentication and it's not expired, no refresh needed
329 | 	if creds.Token != "" && !creds.IsExpired() {
330 | 		return nil
331 | 	}
332 | 
333 | 	// If using username/password, we would implement logic to get a new token
334 | 	if creds.Username != "" && creds.Password != "" {
335 | 		p.logger.Info("Refreshing ArgoCD token using username/password")		
336 | 		p.logger.Info("Successfully refreshed ArgoCD token")
337 | 		return nil
338 | 	}
339 | 
340 | 	return fmt.Errorf("unable to refresh ArgoCD token: invalid credential type")
341 | }
342 | 
343 | // UpdateArgoToken updates the ArgoCD token
344 | func (p *CredentialProvider) UpdateArgoToken(ctx context.Context, token string) {
345 |     p.mu.Lock()
346 |     defer p.mu.Unlock()
347 |     
348 |     if creds, ok := p.credentials[ServiceArgoCD]; ok {
349 |         creds.Token = token
350 |         creds.ExpiresAt = time.Now().Add(24 * time.Hour)
351 |         p.logger.Info("Updated ArgoCD token")
352 |     } else {
353 |         p.credentials[ServiceArgoCD] = &Credentials{
354 |             Token: token,
355 |             ExpiresAt: time.Now().Add(24 * time.Hour),
356 |         }
357 |         p.logger.Info("Created new ArgoCD token")
358 |     }
359 | }
```

--------------------------------------------------------------------------------
/docs/src/content/docs/configuration.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: Configuration Guide
  3 | description: Learn how to configure and customize the Kubernetes Claude MCP server to suit your needs and environment.
  4 | date: 2025-03-01
  5 | order: 4
  6 | tags: ['configuration', 'setup']
  7 | ---
  8 | 
  9 | # Configuration Guide
 10 | 
 11 | This guide explains how to configure Kubernetes Claude MCP to work optimally in your environment. The server is highly configurable, allowing you to customize its behavior and integrations.
 12 | 
 13 | ## Configuration File
 14 | 
 15 | Kubernetes Claude MCP is primarily configured using a YAML file (`config.yaml`). This file contains settings for the server, Kubernetes connection, ArgoCD integration, GitLab integration, and Claude AI.
 16 | 
 17 | Here's a complete example of the configuration file with explanations:
 18 | 
 19 | ```yaml
 20 | # Server configuration
 21 | server:
 22 |   # Address to bind the server on (host:port)
 23 |   address: ":8080"
 24 |   # Read timeout in seconds
 25 |   readTimeout: 30
 26 |   # Write timeout in seconds
 27 |   writeTimeout: 60
 28 |   # Authentication settings
 29 |   auth:
 30 |     # API key for authenticating requests
 31 |     apiKey: "your_api_key_here"
 32 | 
 33 | # Kubernetes connection settings
 34 | kubernetes:
 35 |   # Path to kubeconfig file (leave empty for in-cluster)
 36 |   kubeconfig: ""
 37 |   # Whether to use in-cluster config
 38 |   inCluster: false
 39 |   # Default Kubernetes context (leave empty for current)
 40 |   defaultContext: ""
 41 |   # Default namespace
 42 |   defaultNamespace: "default"
 43 | 
 44 | # ArgoCD integration settings
 45 | argocd:
 46 |   # ArgoCD server URL
 47 |   url: "https://argocd.example.com"
 48 |   # ArgoCD auth token (optional if using username/password)
 49 |   authToken: ""
 50 |   # ArgoCD username (optional if using token)
 51 |   username: "admin"
 52 |   # ArgoCD password (optional if using token)
 53 |   password: "password"
 54 |   # Whether to allow insecure connections
 55 |   insecure: false
 56 | 
 57 | # GitLab integration settings
 58 | gitlab:
 59 |   # GitLab server URL
 60 |   url: "https://gitlab.com"
 61 |   # GitLab personal access token
 62 |   authToken: "your_gitlab_token"
 63 |   # GitLab API version
 64 |   apiVersion: "v4"
 65 |   # Default project path
 66 |   projectPath: "namespace/project"
 67 | 
 68 | # Claude AI settings
 69 | claude:
 70 |   # Claude API key
 71 |   apiKey: "your_claude_api_key"
 72 |   # Claude API base URL
 73 |   baseURL: "https://api.anthropic.com"
 74 |   # Claude model ID
 75 |   modelID: "claude-3-haiku-20240307"
 76 |   # Maximum tokens for Claude responses
 77 |   maxTokens: 4096
 78 |   # Temperature for Claude responses (0.0-1.0)
 79 |   temperature: 0.7
 80 | ```
 81 | 
 82 | ## Configuration Options
 83 | 
 84 | ### Server Configuration
 85 | 
 86 | | Option | Description | Default |
 87 | |--------|-------------|---------|
 88 | | `address` | Host and port to bind the server (":8080" means all interfaces, port 8080) | ":8080" |
 89 | | `readTimeout` | HTTP read timeout in seconds | 30 |
 90 | | `writeTimeout` | HTTP write timeout in seconds | 60 |
 91 | | `auth.apiKey` | API key for authenticating requests | - |
 92 | 
 93 | ### Kubernetes Configuration
 94 | 
 95 | | Option | Description | Default |
 96 | |--------|-------------|---------|
 97 | | `kubeconfig` | Path to kubeconfig file | "" (auto-detect) |
 98 | | `inCluster` | Whether to use in-cluster configuration | false |
 99 | | `defaultContext` | Default Kubernetes context | "" (current context) |
100 | | `defaultNamespace` | Default namespace for operations | "default" |
101 | 
102 | ### ArgoCD Configuration
103 | 
104 | | Option | Description | Default |
105 | |--------|-------------|---------|
106 | | `url` | ArgoCD server URL | - |
107 | | `authToken` | ArgoCD auth token | "" |
108 | | `username` | ArgoCD username | "" |
109 | | `password` | ArgoCD password | "" |
110 | | `insecure` | Allow insecure connections to ArgoCD | false |
111 | 
112 | ### GitLab Configuration
113 | 
114 | | Option | Description | Default |
115 | |--------|-------------|---------|
116 | | `url` | GitLab server URL | "https://gitlab.com" |
117 | | `authToken` | GitLab personal access token | - |
118 | | `apiVersion` | GitLab API version | "v4" |
119 | | `projectPath` | Default project path | "" |
120 | 
121 | ### Claude Configuration
122 | 
123 | | Option | Description | Default |
124 | |--------|-------------|---------|
125 | | `apiKey` | Claude API key | - |
126 | | `baseURL` | Claude API base URL | "https://api.anthropic.com" |
127 | | `modelID` | Claude model ID | "claude-3-haiku-20240307" |
128 | | `maxTokens` | Maximum tokens for response | 4096 |
129 | | `temperature` | Temperature for responses (0.0-1.0) | 0.7 |
130 | 
131 | ## Environment Variables
132 | 
133 | In addition to the configuration file, you can use environment variables to override any configuration option. This is especially useful for secrets and credentials.
134 | 
135 | Environment variables follow this pattern:
136 | 
137 | - For server options: `SERVER_OPTION_NAME`
138 | - For Kubernetes options: `KUBERNETES_OPTION_NAME`
139 | - For ArgoCD options: `ARGOCD_OPTION_NAME`
140 | - For GitLab options: `GITLAB_OPTION_NAME`
141 | - For Claude options: `CLAUDE_OPTION_NAME`
142 | 
143 | Common examples:
144 | 
145 | ```bash
146 | # API keys
147 | export CLAUDE_API_KEY=your_claude_api_key
148 | export API_KEY=your_api_key_for_server
149 | 
150 | # ArgoCD credentials
151 | export ARGOCD_USERNAME=your_argocd_username
152 | export ARGOCD_PASSWORD=your_argocd_password
153 | 
154 | # GitLab credentials
155 | export GITLAB_AUTH_TOKEN=your_gitlab_token
156 | ```
157 | 
158 | ## Variable Interpolation
159 | 
160 | The configuration file supports variable interpolation, allowing you to reference environment variables in your config. This is useful for injecting secrets:
161 | 
162 | ```yaml
163 | server:
164 |   auth:
165 |     apiKey: "${API_KEY}"
166 | 
167 | claude:
168 |   apiKey: "${CLAUDE_API_KEY}"
169 | ```
170 | 
171 | ## Configuration Hierarchy
172 | 
173 | The server reads configuration in the following order (later overrides earlier):
174 | 
175 | 1. Default values
176 | 2. Configuration file
177 | 3. Environment variables
178 | 
179 | This allows you to have a base configuration file and override specific settings with environment variables.
180 | 
181 | ## ArgoCD Integration
182 | 
183 | ### Authentication Methods
184 | 
185 | There are two ways to authenticate with ArgoCD:
186 | 
187 | 1. **Token-based authentication**: Provide an auth token in `argocd.authToken`.
188 | 2. **Username/password authentication**: Provide username and password in `argocd.username` and `argocd.password`.
189 | 
190 | For production environments, token-based authentication is recommended for security.
191 | 
192 | ### Insecure Mode
193 | 
194 | If you're using a self-signed certificate for ArgoCD, you can set `argocd.insecure` to `true` to skip certificate validation. However, this is not recommended for production environments.
195 | 
196 | ## GitLab Integration
197 | 
198 | ### Personal Access Token
199 | 
200 | To integrate with GitLab, you need a personal access token with the following scopes:
201 | 
202 | - `read_api` - For accessing repository information
203 | - `read_repository` - For accessing repository content
204 | - `read_registry` - For accessing container registry (if needed)
205 | 
206 | ### Self-hosted GitLab
207 | 
208 | If you're using a self-hosted GitLab instance, set the `gitlab.url` to your GitLab URL:
209 | 
210 | ```yaml
211 | gitlab:
212 |   url: "https://gitlab.your-company.com"
213 |   # Other GitLab settings...
214 | ```
215 | 
216 | ## Claude AI Configuration
217 | 
218 | ### Model Selection
219 | 
220 | Kubernetes Claude MCP supports different Claude model variants. The default is `claude-3-haiku-20240307`, but you can choose others based on your needs:
221 | 
222 | - `claude-3-opus-20240229` - Most capable model, best for complex analysis
223 | - `claude-3-sonnet-20240229` - Balanced performance and speed
224 | - `claude-3-haiku-20240307` - Fastest model, suitable for most use cases
225 | 
226 | ### Response Parameters
227 | 
228 | You can adjust two parameters that affect Claude's responses:
229 | 
230 | 1. `maxTokens` - Maximum number of tokens in the response (1-4096)
231 | 2. `temperature` - Controls randomness in responses (0.0-1.0)
232 |    - Lower values (e.g., 0.3) make responses more deterministic
233 |    - Higher values (e.g., 0.7) make responses more creative
234 | 
235 | For troubleshooting and analysis, a temperature of 0.3-0.5 is recommended.
236 | 
237 | ## Advanced Configuration
238 | 
239 | ### Running Behind a Proxy
240 | 
241 | If the server needs to connect to external services through a proxy, set the standard HTTP proxy environment variables:
242 | 
243 | ```bash
244 | export HTTP_PROXY=http://proxy.example.com:8080
245 | export HTTPS_PROXY=http://proxy.example.com:8080
246 | export NO_PROXY=localhost,127.0.0.1,.cluster.local
247 | ```
248 | 
249 | ### TLS Configuration
250 | 
251 | For production deployments, it's recommended to use TLS. This is typically handled by your ingress controller, load balancer, or API gateway.
252 | 
253 | If you need to terminate TLS at the server (not recommended for production), you can use a reverse proxy like Nginx or Traefik.
254 | 
255 | ### Logging Configuration
256 | 
257 | The logging level can be controlled with the `LOG_LEVEL` environment variable:
258 | 
259 | ```bash
260 | export LOG_LEVEL=debug  # debug, info, warn, error
261 | ```
262 | 
263 | For production, `info` is recommended. Use `debug` only for troubleshooting.
264 | 
265 | ## Configuration Examples
266 | 
267 | ### Minimal Configuration
268 | 
269 | ```yaml
270 | server:
271 |   address: ":8080"
272 |   auth:
273 |     apiKey: "your_api_key_here"
274 | 
275 | kubernetes:
276 |   inCluster: false
277 | 
278 | claude:
279 |   apiKey: "your_claude_api_key"
280 |   modelID: "claude-3-haiku-20240307"
281 | ```
282 | 
283 | ### Production Kubernetes Configuration
284 | 
285 | ```yaml
286 | server:
287 |   address: ":8080"
288 |   readTimeout: 60
289 |   writeTimeout: 120
290 |   auth:
291 |     apiKey: "${API_KEY}"
292 | 
293 | kubernetes:
294 |   inCluster: true
295 |   defaultNamespace: "default"
296 | 
297 | argocd:
298 |   url: "https://argocd.example.com"
299 |   authToken: "${ARGOCD_AUTH_TOKEN}"
300 |   insecure: false
301 | 
302 | gitlab:
303 |   url: "https://gitlab.example.com"
304 |   authToken: "${GITLAB_AUTH_TOKEN}"
305 |   apiVersion: "v4"
306 | 
307 | claude:
308 |   apiKey: "${CLAUDE_API_KEY}"
309 |   baseURL: "https://api.anthropic.com"
310 |   modelID: "claude-3-haiku-20240307"
311 |   maxTokens: 4096
312 |   temperature: 0.5
313 | ```
314 | 
315 | ## Troubleshooting Configuration
316 | 
317 | If you encounter issues with your configuration:
318 | 
319 | 1. Check that all required fields are set correctly
320 | 2. Verify that environment variables are correctly set and accessible to the server
321 | 3. Test connectivity to external services (Kubernetes, ArgoCD, GitLab)
322 | 4. Check the server logs for error messages
323 | 5. Ensure your Claude API key is valid and has sufficient quota
324 | 
325 | ### Common Issues
326 | 
327 | #### "Failed to create Kubernetes client"
328 | 
329 | This usually indicates an issue with the Kubernetes configuration:
330 | 
331 | - Check if the kubeconfig file exists and is accessible
332 | - Verify the permissions of the kubeconfig file
333 | - For in-cluster config, ensure the pod has the proper service account
334 | 
335 | #### "Failed to connect to ArgoCD"
336 | 
337 | ArgoCD connectivity issues are typically related to:
338 | 
339 | - Incorrect URL or credentials
340 | - Network connectivity issues
341 | - Certificate validation (if `insecure: false`)
342 | 
343 | Try using the `--log-level=debug` flag to get more details:
344 | 
345 | ```bash
346 | LOG_LEVEL=debug ./mcp-server --config config.yaml
347 | ```
348 | 
349 | #### "Failed to connect to GitLab"
350 | 
351 | GitLab connectivity issues may be due to:
352 | 
353 | - Invalid personal access token
354 | - Insufficient permissions for the token
355 | - Network connectivity issues
356 | 
357 | #### "Claude API error"
358 | 
359 | Claude API errors usually indicate:
360 | 
361 | - Invalid API key
362 | - Rate limiting or quota issues
363 | - Incorrect model ID
364 | 
365 | ## Updating Configuration
366 | 
367 | You can update the configuration without restarting the server by sending a SIGHUP signal:
368 | 
369 | ```bash
370 | # Find the process ID
371 | ps aux | grep mcp-server
372 | 
373 | # Send SIGHUP signal
374 | kill -HUP <process_id>
375 | ```
376 | 
377 | For containerized deployments, you'll need to restart the container to apply configuration changes.
378 | 
379 | ## Next Steps
380 | 
381 | Now that you've configured Kubernetes Claude MCP, you can:
382 | 
383 | - [Explore the API](/docs/api-overview) to learn how to interact with the server
384 | - [Try some examples](/docs/examples/basic-usage) to see common use cases
385 | - [Learn about troubleshooting](/docs/troubleshooting-resources) to diagnose issues in your cluster
```

--------------------------------------------------------------------------------
/docs/src/content/docs/troubleshooting-resources.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: Troubleshooting Resources
  3 | description: Learn how to use Kubernetes Claude MCP to diagnose and solve problems with your Kubernetes resources and applications.
  4 | date: 2025-03-01
  5 | order: 6
  6 | tags: ['troubleshooting', 'guides']
  7 | ---
  8 | 
  9 | # Troubleshooting Resources
 10 | 
 11 | Kubernetes Claude MCP is a powerful tool for diagnosing and resolving issues in your Kubernetes environment. This guide will walk you through common troubleshooting scenarios and how to use the MCP server to address them.
 12 | 
 13 | ## Getting Started with Troubleshooting
 14 | 
 15 | The `/api/v1/mcp/troubleshoot` endpoint is specifically designed for troubleshooting. It automatically:
 16 | 
 17 | 1. Collects all relevant information about a resource
 18 | 2. Detects common issues and their severity
 19 | 3. Correlates information across systems (Kubernetes, ArgoCD, GitLab)
 20 | 4. Generates recommendations for fixing the issues
 21 | 5. Provides Claude AI-powered analysis of the problems
 22 | 
 23 | ## Troubleshooting Common Resource Types
 24 | 
 25 | ### Troubleshooting Pods
 26 | 
 27 | Pods are often the first place to look when troubleshooting application issues.
 28 | 
 29 | **Example Request:**
 30 | 
 31 | ```bash
 32 | curl -X POST \
 33 |   -H "Content-Type: application/json" \
 34 |   -H "X-API-Key: your_api_key" \
 35 |   -d '{
 36 |     "resource": "pod",
 37 |     "name": "my-app-pod",
 38 |     "namespace": "default",
 39 |     "query": "Why is this pod not starting?"
 40 |   }' \
 41 |   http://localhost:8080/api/v1/mcp/troubleshoot
 42 | ```
 43 | 
 44 | **What MCP Detects:**
 45 | 
 46 | - Pod status issues (Pending, CrashLoopBackOff, ImagePullBackOff, etc.)
 47 | - Container status and restart counts
 48 | - Resource constraints (CPU/memory limits)
 49 | - Volume mounting issues
 50 | - Init container failures
 51 | - Image pull errors
 52 | - Scheduling problems
 53 | - Events related to the pod
 54 | 
 55 | **Example Troubleshooting Output:**
 56 | 
 57 | ```json
 58 | {
 59 |   "success": true,
 60 |   "analysis": "The pod 'my-app-pod' is failing to start due to an ImagePullBackOff error. The container runtime is unable to pull the image 'myregistry.com/my-app:v1.2.3' because of authentication issues with the private registry. Looking at the events, there was an 'ErrImagePull' error with the message 'unauthorized: authentication required'...",
 61 |   "troubleshootResult": {
 62 |     "issues": [
 63 |       {
 64 |         "title": "Image Pull Error",
 65 |         "category": "ImagePullError",
 66 |         "severity": "Error",
 67 |         "source": "Kubernetes",
 68 |         "description": "Failed to pull image 'myregistry.com/my-app:v1.2.3': unauthorized: authentication required"
 69 |       }
 70 |     ],
 71 |     "recommendations": [
 72 |       "Create or update the ImagePullSecret for the private registry",
 73 |       "Verify the image name and tag are correct",
 74 |       "Check that the ServiceAccount has access to the ImagePullSecret"
 75 |     ]
 76 |   }
 77 | }
 78 | ```
 79 | 
 80 | ### Troubleshooting Deployments
 81 | 
 82 | Deployments manage replica sets and pods, so issues can occur at multiple levels.
 83 | 
 84 | **Example Request:**
 85 | 
 86 | ```bash
 87 | curl -X POST \
 88 |   -H "Content-Type: application/json" \
 89 |   -H "X-API-Key: your_api_key" \
 90 |   -d '{
 91 |     "resource": "deployment",
 92 |     "name": "my-app",
 93 |     "namespace": "default",
 94 |     "query": "Why are pods not scaling up?"
 95 |   }' \
 96 |   http://localhost:8080/api/v1/mcp/troubleshoot
 97 | ```
 98 | 
 99 | **What MCP Detects:**
100 | 
101 | - ReplicaSet creation issues
102 | - Pod scaling issues
103 | - Resource quotas preventing scaling
104 | - Node capacity issues
105 | - Pod disruption budgets
106 | - Deployment strategy issues
107 | - Resource constraints on pods
108 | - Health check configuration issues
109 | 
110 | **Example Troubleshooting Output:**
111 | 
112 | ```json
113 | {
114 |   "success": true,
115 |   "analysis": "The deployment 'my-app' is unable to scale up because the pods are requesting more CPU resources than are available in the cluster. The deployment is configured to request 2 CPU cores per pod, but the nodes in your cluster only have 1.8 cores available per node...",
116 |   "troubleshootResult": {
117 |     "issues": [
118 |       {
119 |         "title": "Insufficient CPU Resources",
120 |         "category": "ResourceConstraint",
121 |         "severity": "Warning",
122 |         "source": "Kubernetes",
123 |         "description": "Insufficient CPU resources available to schedule pods (requested: 2, available: 1.8)"
124 |       }
125 |     ],
126 |     "recommendations": [
127 |       "Reduce the CPU request in the deployment specification",
128 |       "Add more nodes to the cluster or use nodes with more CPU capacity",
129 |       "Check if there are any resource quotas preventing the scaling"
130 |     ]
131 |   }
132 | }
133 | ```
134 | 
135 | ### Troubleshooting Services
136 | 
137 | Services provide network connectivity between components, and issues often relate to selector mismatches or port configurations.
138 | 
139 | **Example Request:**
140 | 
141 | ```bash
142 | curl -X POST \
143 |   -H "Content-Type: application/json" \
144 |   -H "X-API-Key: your_api_key" \
145 |   -d '{
146 |     "resource": "service",
147 |     "name": "my-app-service",
148 |     "namespace": "default",
149 |     "query": "Why can't I connect to this service?"
150 |   }' \
151 |   http://localhost:8080/api/v1/mcp/troubleshoot
152 | ```
153 | 
154 | **What MCP Detects:**
155 | 
156 | - Selector mismatches between service and pods
157 | - Port configuration issues
158 | - Endpoint availability
159 | - Pod readiness issues
160 | - Network policy restrictions
161 | - Service type misconfigurations
162 | - External name resolution issues (for ExternalName services)
163 | 
164 | **Example Troubleshooting Output:**
165 | 
166 | ```json
167 | {
168 |   "success": true,
169 |   "analysis": "The service 'my-app-service' is not working correctly because there are no endpoints being selected. The service uses the selector 'app=my-app,tier=frontend', but examining the pods in the namespace, I can see that the pods have the labels 'app=my-app,tier=web'. The mismatch in the 'tier' label (frontend vs web) is preventing the service from selecting any pods...",
170 |   "troubleshootResult": {
171 |     "issues": [
172 |       {
173 |         "title": "Selector Mismatch",
174 |         "category": "ServiceSelectorIssue",
175 |         "severity": "Error",
176 |         "source": "Kubernetes",
177 |         "description": "Service selector 'app=my-app,tier=frontend' doesn't match any pods (pods have 'app=my-app,tier=web')"
178 |       }
179 |     ],
180 |     "recommendations": [
181 |       "Update the service selector to match the actual pod labels: 'app=my-app,tier=web'",
182 |       "Alternatively, update the pod labels to match the service selector",
183 |       "Verify that pods are in the 'Running' state and passing readiness probes"
184 |     ]
185 |   }
186 | }
187 | ```
188 | 
189 | ### Troubleshooting Ingresses
190 | 
191 | Ingress resources configure external access to services, and issues often relate to hostname mismatches or TLS configuration.
192 | 
193 | **Example Request:**
194 | 
195 | ```bash
196 | curl -X POST \
197 |   -H "Content-Type: application/json" \
198 |   -H "X-API-Key: your_api_key" \
199 |   -d '{
200 |     "resource": "ingress",
201 |     "name": "my-app-ingress",
202 |     "namespace": "default",
203 |     "query": "Why is this ingress returning 404 errors?"
204 |   }' \
205 |   http://localhost:8080/api/v1/mcp/troubleshoot
206 | ```
207 | 
208 | **What MCP Detects:**
209 | 
210 | - Backend service existence and configuration
211 | - Path routing rules
212 | - TLS certificate issues
213 | - Ingress controller availability
214 | - Host name configurations
215 | - Annotation misconfigurations
216 | - Service port mappings
217 | 
218 | ## Troubleshooting GitOps Resources
219 | 
220 | Kubernetes Claude MCP excels at diagnosing issues in GitOps workflows by correlating information between Kubernetes, ArgoCD, and GitLab.
221 | 
222 | ### Troubleshooting ArgoCD Applications
223 | 
224 | **Example Request:**
225 | 
226 | ```bash
227 | curl -X POST \
228 |   -H "Content-Type: application/json" \
229 |   -H "X-API-Key: your_api_key" \
230 |   -d '{
231 |     "resource": "application",
232 |     "name": "my-argocd-app",
233 |     "namespace": "argocd",
234 |     "query": "Why is this application out of sync?"
235 |   }' \
236 |   http://localhost:8080/api/v1/mcp/troubleshoot
237 | ```
238 | 
239 | **What MCP Detects:**
240 | 
241 | - Sync status issues
242 | - Sync history and recent failures
243 | - Git repository connectivity issues
244 | - Manifest validation errors
245 | - Resource differences between desired and actual state
246 | - Health status issues
247 | - Related Kubernetes resources
248 | 
249 | **Example Troubleshooting Output:**
250 | 
251 | ```json
252 | {
253 |   "success": true,
254 |   "analysis": "The ArgoCD application 'my-argocd-app' is out of sync because there are local changes to the Deployment resource that differ from the version in Git. Specifically, someone has manually scaled the deployment from 3 replicas (as defined in Git) to 5 replicas using kubectl...",
255 |   "troubleshootResult": {
256 |     "issues": [
257 |       {
258 |         "title": "Manual Modification",
259 |         "category": "SyncIssue",
260 |         "severity": "Warning",
261 |         "source": "ArgoCD",
262 |         "description": "Deployment 'my-app' was manually modified: replicas changed from 3 to 5"
263 |       }
264 |     ],
265 |     "recommendations": [
266 |       "Use 'argocd app sync my-argocd-app' to revert to the state defined in Git",
267 |       "Update the Git repository to reflect the desired replica count",
268 |       "Enable self-healing in the ArgoCD application to prevent manual modifications"
269 |     ]
270 |   }
271 | }
272 | ```
273 | 
274 | ### Investigating Commit Impact
275 | 
276 | When a deployment fails after a GitLab commit, you can analyze the commit's impact:
277 | 
278 | **Example Request:**
279 | 
280 | ```bash
281 | curl -X POST \
282 |   -H "Content-Type: application/json" \
283 |   -H "X-API-Key: your_api_key" \
284 |   -d '{
285 |     "projectId": "mygroup/myproject",
286 |     "commitSha": "abcdef1234567890",
287 |     "query": "How has this commit affected Kubernetes resources and what issues has it caused?"
288 |   }' \
289 |   http://localhost:8080/api/v1/mcp/commit
290 | ```
291 | 
292 | **What MCP Analyzes:**
293 | 
294 | - Files changed in the commit
295 | - Connected ArgoCD applications
296 | - Affected Kubernetes resources
297 | - Subsequent pipeline results
298 | - Changes in resource configurations
299 | - Introduction of new errors or warnings
300 | 
301 | ## Advanced Troubleshooting Scenarios
302 | 
303 | ### Multi-Resource Analysis
304 | 
305 | You can troubleshoot complex issues by instructing Claude to correlate multiple resources:
306 | 
307 | **Example Request:**
308 | 
309 | ```bash
310 | curl -X POST \
311 |   -H "Content-Type: application/json" \
312 |   -H "X-API-Key: your_api_key" \
313 |   -d '{
314 |     "query": "Analyze the connectivity issue between the frontend deployment and the backend service in the 'myapp' namespace. Check both the deployment and the service configurations."
315 |   }' \
316 |   http://localhost:8080/api/v1/mcp
317 | ```
318 | 
319 | ### Diagram Generation
320 | 
321 | For complex troubleshooting scenarios, you can request diagram generation to visualize relationships:
322 | 
323 | **Example Request:**
324 | 
325 | ```bash
326 | curl -X POST \
327 |   -H "Content-Type: application/json" \
328 |   -H "X-API-Key: your_api_key" \
329 |   -d '{
330 |     "resource": "deployment",
331 |     "name": "my-app",
332 |     "namespace": "default",
333 |     "query": "Create a diagram showing this deployment's relationship to all associated resources, including services, ingresses, configmaps, and secrets."
334 |   }' \
335 |   http://localhost:8080/api/v1/mcp/resource
336 | ```
337 | 
338 | Claude can generate Mermaid diagrams within its response to visualize the relationships.
339 | 
340 | ## Troubleshooting Best Practices
341 | 
342 | When using Kubernetes Claude MCP for troubleshooting:
343 | 
344 | 1. **Start specific**: Begin with the resource that's showing symptoms
345 | 2. **Go broad**: If needed, expand to related resources
346 | 3. **Use specific queries**: The more specific your query, the better Claude can help
347 | 4. **Include context**: Mention what you've already tried or specific symptoms
348 | 5. **Follow recommendations**: Try the recommended fixes one at a time
349 | 6. **Iterate**: Use follow-up queries to dive deeper
350 | 
351 | ## Real-Time Troubleshooting
352 | 
353 | For ongoing issues, you can set up continuous monitoring:
354 | 
355 | ```bash
356 | # Watch a resource and get alerts when issues are detected
357 | watch -n 30 'curl -s -X POST \
358 |   -H "Content-Type: application/json" \
359 |   -H "X-API-Key: your_api_key" \
360 |   -d "{\"resource\":\"deployment\",\"name\":\"my-app\",\"namespace\":\"default\",\"query\":\"Report any new issues\"}" \
361 |   http://localhost:8080/api/v1/mcp/troubleshoot | jq .troubleshootResult.issues'
362 | ```
363 | 
364 | ## Troubleshooting Reference
365 | 
366 | Here's a quick reference of what to check for common Kubernetes issues:
367 | 
368 | | Symptom | Resource to Check | Common Issues |
369 | |---------|-------------------|---------------|
370 | | Application not starting | Pod | Image pull errors, resource constraints, configuration issues |
371 | | Cannot connect to app | Service | Selector mismatch, port configuration, pod health |
372 | | External access failing | Ingress | Path configuration, backend service, TLS issues |
373 | | Scaling issues | Deployment | Resource constraints, pod disruption budgets, affinity rules |
374 | | Configuration issues | ConfigMap/Secret | Missing keys, invalid format, mounting issues |
375 | | Persistent storage issues | PVC | Storage class, capacity issues, access modes |
376 | | GitOps sync failures | ArgoCD Application | Git repo issues, manifest errors, resource conflicts |
```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/mcp/context.go:
--------------------------------------------------------------------------------

```go
  1 | package mcp
  2 | 
  3 | import (
  4 |     "context"
  5 |     "fmt"
  6 |     "strings"
  7 |     "time"
  8 | 
  9 |     "github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
 10 |     "github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
 11 |     "github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/utils"
 12 | )
 13 | 
 14 | // ContextManager handles the creation and management of context for Claude
 15 | type ContextManager struct {
 16 | 	maxContextSize int
 17 | 	logger         *logging.Logger
 18 | }
 19 | 
 20 | // NewContextManager creates a new context manager
 21 | func NewContextManager(maxContextSize int, logger *logging.Logger) *ContextManager {
 22 | 	if maxContextSize <= 0 {
 23 | 		maxContextSize = 100000
 24 | 	}
 25 | 
 26 | 	if logger == nil {
 27 | 		logger = logging.NewLogger().Named("context")
 28 | 	}
 29 | 
 30 | 	return &ContextManager{
 31 | 		maxContextSize: maxContextSize,
 32 | 		logger:         logger,
 33 | 	}
 34 | }
 35 | 
 36 | // FormatResourceContext formats a resource context for Claude
 37 | func (cm *ContextManager) FormatResourceContext(rc models.ResourceContext) (string, error) {
 38 |     cm.logger.Debug("Formatting resource context", 
 39 |         "kind", rc.Kind, 
 40 |         "name", rc.Name, 
 41 |         "namespace", rc.Namespace)
 42 |     
 43 |     var formattedContext string
 44 | 
 45 |     // Format the basic resource information
 46 |     formattedContext += fmt.Sprintf("# Kubernetes Resource: %s/%s\n", rc.Kind, rc.Name)
 47 |     if rc.Namespace != "" {
 48 |         formattedContext += fmt.Sprintf("Namespace: %s\n", rc.Namespace)
 49 |     }
 50 |     formattedContext += fmt.Sprintf("API Version: %s\n\n", rc.APIVersion)
 51 | 
 52 | 	// Add the full resource data if available
 53 | 	if rc.ResourceData != "" {
 54 | 		formattedContext += "## Resource Details\n```json\n"
 55 | 		formattedContext += rc.ResourceData
 56 | 		formattedContext += "\n```\n\n"
 57 | 	}
 58 | 
 59 | 	// Add resource-specific metadata if available
 60 | 	if rc.Metadata != nil {
 61 | 		// Add deployment-specific information
 62 | 		if strings.EqualFold(rc.Kind, "deployment") {
 63 | 			formattedContext += "## Deployment Status\n"
 64 | 			
 65 | 			// Add replica information
 66 | 			if desiredReplicas, ok := rc.Metadata["desiredReplicas"].(int64); ok {
 67 | 				formattedContext += fmt.Sprintf("Desired Replicas: %d\n", desiredReplicas)
 68 | 			}
 69 | 			
 70 | 			if currentReplicas, ok := rc.Metadata["currentReplicas"].(int64); ok {
 71 | 				formattedContext += fmt.Sprintf("Current Replicas: %d\n", currentReplicas)
 72 | 			}
 73 | 			
 74 | 			if readyReplicas, ok := rc.Metadata["readyReplicas"].(int64); ok {
 75 | 				formattedContext += fmt.Sprintf("Ready Replicas: %d\n", readyReplicas)
 76 | 			}
 77 | 			
 78 | 			if availableReplicas, ok := rc.Metadata["availableReplicas"].(int64); ok {
 79 | 				formattedContext += fmt.Sprintf("Available Replicas: %d\n", availableReplicas)
 80 | 			}
 81 | 			
 82 | 			// Add container information
 83 | 			if containers, ok := rc.Metadata["containers"].([]map[string]interface{}); ok && len(containers) > 0 {
 84 | 				formattedContext += "\n### Containers\n"
 85 | 				for i, container := range containers {
 86 | 					formattedContext += fmt.Sprintf("%d. Name: %s\n", i+1, container["name"])
 87 | 					
 88 | 					if image, ok := container["image"].(string); ok {
 89 | 						formattedContext += fmt.Sprintf("   Image: %s\n", image)
 90 | 					}
 91 | 					
 92 | 					if resources, ok := container["resources"].(map[string]interface{}); ok {
 93 | 						formattedContext += "   Resources:\n"
 94 | 						
 95 | 						if requests, ok := resources["requests"].(map[string]interface{}); ok {
 96 | 							formattedContext += "     Requests:\n"
 97 | 							for k, v := range requests {
 98 | 								formattedContext += fmt.Sprintf("       %s: %v\n", k, v)
 99 | 							}
100 | 						}
101 | 						
102 | 						if limits, ok := resources["limits"].(map[string]interface{}); ok {
103 | 							formattedContext += "     Limits:\n"
104 | 							for k, v := range limits {
105 | 								formattedContext += fmt.Sprintf("       %s: %v\n", k, v)
106 | 							}
107 | 						}
108 | 					}
109 | 				}
110 | 			}
111 | 			
112 | 			formattedContext += "\n"
113 | 		}
114 | 	}
115 | 
116 |     // If this is a namespace, add namespace-specific information
117 |     if strings.EqualFold(rc.Kind, "namespace") {
118 |         // Add resource metadata if available
119 |         if rc.Metadata != nil {
120 |             if resourceCounts, ok := rc.Metadata["resourceCounts"].(map[string][]string); ok {
121 |                 formattedContext += "## Resources in Namespace\n"
122 |                 for kind, resources := range resourceCounts {
123 |                     formattedContext += fmt.Sprintf("- %s: %d resources\n", kind, len(resources))
124 |                     
125 |                     // List up to 5 resources of each kind
126 |                     if len(resources) > 0 {
127 |                         formattedContext += "  - "
128 |                         for i, name := range resources {
129 |                             if i > 4 {
130 |                                 formattedContext += fmt.Sprintf("and %d more...", len(resources)-5)
131 |                                 break
132 |                             }
133 |                             if i > 0 {
134 |                                 formattedContext += ", "
135 |                             }
136 |                             formattedContext += name
137 |                         }
138 |                         formattedContext += "\n"
139 |                     }
140 |                 }
141 |                 formattedContext += "\n"
142 |             }
143 |             
144 |             if health, ok := rc.Metadata["health"].(map[string]map[string]string); ok {
145 |                 formattedContext += "## Health Status\n"
146 |                 for kind, statuses := range health {
147 |                     healthy := 0
148 |                     unhealthy := 0
149 |                     progressing := 0
150 |                     unknown := 0
151 |                     
152 |                     for _, status := range statuses {
153 |                         switch status {
154 |                         case "healthy":
155 |                             healthy++
156 |                         case "unhealthy":
157 |                             unhealthy++
158 |                         case "progressing":
159 |                             progressing++
160 |                         default:
161 |                             unknown++
162 |                         }
163 |                     }
164 |                     
165 |                     formattedContext += fmt.Sprintf("- %s: %d healthy, %d unhealthy, %d progressing, %d unknown\n", 
166 |                         kind, healthy, unhealthy, progressing, unknown)
167 |                     
168 |                     // List unhealthy resources
169 |                     unhealthyResources := []string{}
170 |                     for name, status := range statuses {
171 |                         if status == "unhealthy" {
172 |                             unhealthyResources = append(unhealthyResources, name)
173 |                         }
174 |                     }
175 |                     
176 |                     if len(unhealthyResources) > 0 {
177 |                         formattedContext += "  Unhealthy: "
178 |                         for i, name := range unhealthyResources {
179 |                             if i > 4 {
180 |                                 formattedContext += fmt.Sprintf("and %d more...", len(unhealthyResources)-5)
181 |                                 break
182 |                             }
183 |                             if i > 0 {
184 |                                 formattedContext += ", "
185 |                             }
186 |                             formattedContext += name
187 |                         }
188 |                         formattedContext += "\n"
189 |                     }
190 |                 }
191 |                 formattedContext += "\n"
192 |             }
193 |         }
194 |     }
195 | 
196 | 	// Format ArgoCD information if available
197 | 	if rc.ArgoApplication != nil {
198 | 		formattedContext += "## ArgoCD Application\n"
199 | 		formattedContext += fmt.Sprintf("Name: %s\n", rc.ArgoApplication.Name)
200 | 		formattedContext += fmt.Sprintf("Sync Status: %s\n", rc.ArgoSyncStatus)
201 | 		formattedContext += fmt.Sprintf("Health Status: %s\n", rc.ArgoHealthStatus)
202 | 		
203 | 		if rc.ArgoApplication.Spec.Source.RepoURL != "" {
204 | 			formattedContext += fmt.Sprintf("Source: %s\n", rc.ArgoApplication.Spec.Source.RepoURL)
205 | 			formattedContext += fmt.Sprintf("Path: %s\n", rc.ArgoApplication.Spec.Source.Path)
206 | 			formattedContext += fmt.Sprintf("Target Revision: %s\n", rc.ArgoApplication.Spec.Source.TargetRevision)
207 | 		}
208 | 		
209 | 		formattedContext += "\n"
210 | 		
211 | 		// Add recent sync history
212 | 		if len(rc.ArgoSyncHistory) > 0 {
213 | 			formattedContext += "### Recent Sync History\n"
214 | 			for i, history := range rc.ArgoSyncHistory {
215 | 				formattedContext += fmt.Sprintf("%d. [%s] Revision: %s, Status: %s\n", 
216 | 					i+1, 
217 | 					history.DeployedAt.Format(time.RFC3339), 
218 | 					history.Revision, 
219 | 					history.Status)
220 | 			}
221 | 			formattedContext += "\n"
222 | 		}
223 | 	}
224 | 
225 | 	// Format GitLab information if available
226 | 	if rc.GitLabProject != nil {
227 | 		formattedContext += "## GitLab Project\n"
228 | 		formattedContext += fmt.Sprintf("Name: %s\n", rc.GitLabProject.PathWithNamespace)
229 | 		formattedContext += fmt.Sprintf("URL: %s\n\n", rc.GitLabProject.WebURL)
230 | 		
231 | 		// Add last pipeline information
232 | 		if rc.LastPipeline != nil {
233 | 			formattedContext += "### Last Pipeline\n"
234 | 			
235 | 			// Handle pipeline CreatedAt timestamp
236 | 			var pipelineTimestamp string
237 | 			switch createdAt := rc.LastPipeline.CreatedAt.(type) {
238 | 			case int64:
239 | 				pipelineTimestamp = time.Unix(createdAt, 0).Format(time.RFC3339)
240 | 			case float64:
241 | 				pipelineTimestamp = time.Unix(int64(createdAt), 0).Format(time.RFC3339)
242 | 			case string:
243 | 				// Try to parse the string timestamp
244 | 				parsed, err := time.Parse(time.RFC3339, createdAt)
245 | 				if err != nil {
246 | 					// Try alternative format
247 | 					parsed, err = time.Parse("2006-01-02T15:04:05.000Z", createdAt)
248 | 					if err != nil {
249 | 						// Use raw string if parsing fails
250 | 						pipelineTimestamp = createdAt
251 | 					} else {
252 | 						pipelineTimestamp = parsed.Format(time.RFC3339)
253 | 					}
254 | 				} else {
255 | 					pipelineTimestamp = parsed.Format(time.RFC3339)
256 | 				}
257 | 			default:
258 | 				pipelineTimestamp = "unknown timestamp"
259 | 			}
260 | 			
261 | 			formattedContext += fmt.Sprintf("Status: %s\n", rc.LastPipeline.Status)
262 | 			formattedContext += fmt.Sprintf("Ref: %s\n", rc.LastPipeline.Ref)
263 | 			formattedContext += fmt.Sprintf("SHA: %s\n", rc.LastPipeline.SHA)
264 | 			formattedContext += fmt.Sprintf("Created At: %s\n\n", pipelineTimestamp)
265 | 		}
266 | 		
267 | 		// Add last deployment information
268 | 		if rc.LastDeployment != nil {
269 | 			formattedContext += "### Last Deployment\n"
270 | 			
271 | 			// Handle deployment CreatedAt timestamp
272 | 			var deploymentTimestamp string
273 | 			switch createdAt := rc.LastDeployment.CreatedAt.(type) {
274 | 			case int64:
275 | 				deploymentTimestamp = time.Unix(createdAt, 0).Format(time.RFC3339)
276 | 			case float64:
277 | 				deploymentTimestamp = time.Unix(int64(createdAt), 0).Format(time.RFC3339)
278 | 			case string:
279 | 				// Try to parse the string timestamp
280 | 				parsed, err := time.Parse(time.RFC3339, createdAt)
281 | 				if err != nil {
282 | 					// Try alternative format
283 | 					parsed, err = time.Parse("2006-01-02T15:04:05.000Z", createdAt)
284 | 					if err != nil {
285 | 						// Use raw string if parsing fails
286 | 						deploymentTimestamp = createdAt
287 | 					} else {
288 | 						deploymentTimestamp = parsed.Format(time.RFC3339)
289 | 					}
290 | 				} else {
291 | 					deploymentTimestamp = parsed.Format(time.RFC3339)
292 | 				}
293 | 			default:
294 | 				deploymentTimestamp = "unknown timestamp"
295 | 			}
296 | 			
297 | 			formattedContext += fmt.Sprintf("Status: %s\n", rc.LastDeployment.Status)
298 | 			formattedContext += fmt.Sprintf("Environment: %s\n", rc.LastDeployment.Environment.Name)
299 | 			formattedContext += fmt.Sprintf("Created At: %s\n\n", deploymentTimestamp)
300 | 		}
301 | 		
302 | 		// Add recent commits
303 | 		if len(rc.RecentCommits) > 0 {
304 | 			formattedContext += "### Recent Commits\n"
305 | 			for i, commit := range rc.RecentCommits {
306 | 				// Handle commit CreatedAt timestamp
307 | 				var commitTimestamp string
308 | 				switch createdAt := commit.CreatedAt.(type) {
309 | 				case int64:
310 | 					commitTimestamp = time.Unix(createdAt, 0).Format(time.RFC3339)
311 | 				case float64:
312 | 					commitTimestamp = time.Unix(int64(createdAt), 0).Format(time.RFC3339)
313 | 				case string:
314 | 					// Try to parse the string timestamp
315 | 					parsed, err := time.Parse(time.RFC3339, createdAt)
316 | 					if err != nil {
317 | 						// Try alternative format
318 | 						parsed, err = time.Parse("2006-01-02T15:04:05.000Z", createdAt)
319 | 						if err != nil {
320 | 							// Use raw string if parsing fails
321 | 							commitTimestamp = createdAt
322 | 						} else {
323 | 							commitTimestamp = parsed.Format(time.RFC3339)
324 | 						}
325 | 					} else {
326 | 						commitTimestamp = parsed.Format(time.RFC3339)
327 | 					}
328 | 				default:
329 | 					commitTimestamp = "unknown timestamp"
330 | 				}
331 | 				
332 | 				formattedContext += fmt.Sprintf("%d. [%s] %s by %s: %s\n", 
333 | 					i+1, 
334 | 					commitTimestamp, 
335 | 					commit.ShortID, 
336 | 					commit.AuthorName, 
337 | 					commit.Title)
338 | 			}
339 | 			formattedContext += "\n"
340 | 		}
341 | 	}
342 | 
343 | 	// Format Kubernetes events
344 | 	if len(rc.Events) > 0 {
345 | 		formattedContext += "## Recent Kubernetes Events\n"
346 | 		for i, event := range rc.Events {
347 | 			formattedContext += fmt.Sprintf("%d. [%s] %s: %s\n", 
348 | 				i+1, 
349 | 				event.Type, 
350 | 				event.Reason, 
351 | 				event.Message)
352 | 		}
353 | 		formattedContext += "\n"
354 | 	}
355 | 
356 | 	if len(rc.RelatedResources) > 0 {
357 |         formattedContext += "## Related Resources\n"
358 |         // Group by resource kind
359 |         resourcesByKind := make(map[string][]string)
360 |         for _, resource := range rc.RelatedResources {
361 |             parts := strings.Split(resource, "/")
362 |             if len(parts) == 2 {
363 |                 kind := parts[0]
364 |                 name := parts[1]
365 |                 resourcesByKind[kind] = append(resourcesByKind[kind], name)
366 |             } else {
367 |                 // If format is unexpected, just add as is
368 |                 formattedContext += fmt.Sprintf("- %s\n", resource)
369 |             }
370 |         }
371 |         
372 |         // Format resources by kind
373 |         for kind, names := range resourcesByKind {
374 |             formattedContext += fmt.Sprintf("- %s (%d):\n", kind, len(names))
375 |             // Show up to 10 resources per kind
376 |             maxToShow := 10
377 |             if len(names) > maxToShow {
378 |                 for i := 0; i < maxToShow; i++ {
379 |                     formattedContext += fmt.Sprintf("  - %s\n", names[i])
380 |                 }
381 |                 formattedContext += fmt.Sprintf("  - ... and %d more\n", len(names)-maxToShow)
382 |             } else {
383 |                 for _, name := range names {
384 |                     formattedContext += fmt.Sprintf("  - %s\n", name)
385 |                 }
386 |             }
387 |         }
388 |         formattedContext += "\n"
389 |     }
390 | 
391 | 	// Add errors if any
392 | 	if len(rc.Errors) > 0 {
393 | 		formattedContext += "## Errors in Data Collection\n"
394 | 		for _, err := range rc.Errors {
395 | 			formattedContext += fmt.Sprintf("- %s\n", err)
396 | 		}
397 | 		formattedContext += "\n"
398 | 	}
399 | 
400 | 	// Ensure context doesn't exceed max size
401 | 	if len(formattedContext) > cm.maxContextSize {
402 |         cm.logger.Debug("Context exceeds maximum size, truncating", 
403 |             "originalSize", len(formattedContext), 
404 |             "maxSize", cm.maxContextSize)
405 |         formattedContext = utils.TruncateContextSmartly(formattedContext, cm.maxContextSize)
406 |     }
407 | 
408 | 	cm.logger.Debug("Formatted resource context", 
409 |         "kind", rc.Kind, 
410 |         "name", rc.Name, 
411 |         "contextSize", len(formattedContext))
412 |     return formattedContext, nil
413 | }
414 | 
415 | // CombineContexts combines multiple resource contexts into a single context
416 | func (cm *ContextManager) CombineContexts(ctx context.Context, resourceContexts []models.ResourceContext) (string, error) {
417 | 	cm.logger.Debug("Combining resource contexts", "count", len(resourceContexts))
418 | 	
419 | 	var combinedContext string
420 | 	
421 | 	combinedContext += fmt.Sprintf("# Kubernetes GitOps Context (%d resources)\n\n", len(resourceContexts))
422 | 	
423 | 	// Add context for each resource
424 | 	for i, rc := range resourceContexts {
425 | 		resourceContext, err := cm.FormatResourceContext(rc)
426 | 		if err != nil {
427 | 			return "", fmt.Errorf("failed to format resource context #%d: %w", i+1, err)
428 | 		}
429 | 		
430 | 		combinedContext += fmt.Sprintf("--- RESOURCE %d/%d ---\n", i+1, len(resourceContexts))
431 | 		combinedContext += resourceContext
432 | 		combinedContext += "------------------------\n\n"
433 | 	}
434 | 	
435 | 	// Ensure combined context doesn't exceed max size
436 | 	if len(combinedContext) > cm.maxContextSize {
437 | 		cm.logger.Debug("Combined context exceeds maximum size, truncating", 
438 | 			"originalSize", len(combinedContext), 
439 | 			"maxSize", cm.maxContextSize)
440 | 		combinedContext = utils.TruncateContextSmartly(combinedContext, cm.maxContextSize)
441 | 	}
442 | 	
443 | 	cm.logger.Debug("Combined resource contexts", 
444 | 		"resourceCount", len(resourceContexts), 
445 | 		"contextSize", len(combinedContext))
446 | 	return combinedContext, nil
447 | }
```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/api/routes.go:
--------------------------------------------------------------------------------

```go
  1 | package api
  2 | 
  3 | import (
  4 | 	"encoding/json"
  5 | 	"fmt"
  6 | 	"net/http"
  7 | 	"strings"
  8 | 
  9 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
 10 | 	"github.com/gorilla/mux"
 11 | )
 12 | 
 13 | // setupRoutes configures the API routes
 14 | func (s *Server) setupRoutes() {
 15 | 
 16 | 	// Apply CORS middleware to all routes
 17 | 	s.router.Use(s.corsMiddleware)
 18 | 
 19 | 	// API version prefix
 20 | 	apiV1 := s.router.PathPrefix("/api/v1").Subrouter()
 21 | 
 22 | 	// Health check endpoint (no auth required)
 23 | 	apiV1.HandleFunc("/health", s.handleHealth).Methods("GET")
 24 | 
 25 | 	// Add authentication middleware to all other routes
 26 | 	apiSecure := apiV1.NewRoute().Subrouter()
 27 | 	apiSecure.Use(s.authMiddleware)
 28 | 
 29 | 	// MCP endpoints
 30 | 	apiSecure.HandleFunc("/mcp", s.handleMCPRequest).Methods("POST")
 31 | 	apiSecure.HandleFunc("/mcp/resource", s.handleResourceQuery).Methods("POST")
 32 | 	apiSecure.HandleFunc("/mcp/commit", s.handleCommitQuery).Methods("POST")
 33 | 	apiSecure.HandleFunc("/mcp/troubleshoot", s.handleTroubleshoot).Methods("POST")
 34 | 
 35 | 	// Kubernetes resource endpoints
 36 | 	apiSecure.HandleFunc("/namespaces", s.handleListNamespaces).Methods("GET")
 37 | 	apiSecure.HandleFunc("/resources/{resource}", s.handleListResources).Methods("GET")
 38 | 	apiSecure.HandleFunc("/resources/{resource}/{name}", s.handleGetResource).Methods("GET")
 39 | 	apiSecure.HandleFunc("/events", s.handleGetEvents).Methods("GET")
 40 | 
 41 | 	// ArgoCD endpoints
 42 | 	apiSecure.HandleFunc("/argocd/applications", s.handleListArgoApplications).Methods("GET")
 43 | 	apiSecure.HandleFunc("/argocd/applications/{name}", s.handleGetArgoApplication).Methods("GET")
 44 | 
 45 | 	// GitLab endpoints
 46 | 	apiSecure.HandleFunc("/gitlab/projects", s.handleListGitLabProjects).Methods("GET")
 47 | 	apiSecure.HandleFunc("/gitlab/projects/{projectId}/pipelines", s.handleListGitLabPipelines).Methods("GET")
 48 | 
 49 | 	// Merge Request endpoints
 50 | 	apiSecure.HandleFunc("/mcp/mergeRequest", s.handleMergeRequestQuery).Methods("POST")
 51 | }
 52 | 
 53 | // handleMergeRequestQuery handles MCP requests for analyzing merge requests
 54 | func (s *Server) handleMergeRequestQuery(w http.ResponseWriter, r *http.Request) {
 55 | 	var request models.MCPRequest
 56 | 
 57 | 	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
 58 | 		s.respondWithError(w, http.StatusBadRequest, "Invalid request format", err)
 59 | 		return
 60 | 	}
 61 | 
 62 | 	// Force action to be queryMergeRequest
 63 | 	request.Action = "queryMergeRequest"
 64 | 
 65 | 	// Validate merge request parameters
 66 | 	if request.ProjectID == "" || request.MergeRequestIID <= 0 {
 67 | 		s.respondWithError(w, http.StatusBadRequest, "Project ID and merge request IID are required", nil)
 68 | 		return
 69 | 	}
 70 | 
 71 | 	s.logger.Info("Received merge request query",
 72 | 		"projectId", request.ProjectID,
 73 | 		"mergeRequestIID", request.MergeRequestIID)
 74 | 
 75 | 	// Process the request
 76 | 	response, err := s.mcpHandler.ProcessRequest(r.Context(), &request)
 77 | 	if err != nil {
 78 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to process request", err)
 79 | 		return
 80 | 	}
 81 | 
 82 | 	s.respondWithJSON(w, http.StatusOK, response)
 83 | }
 84 | 
 85 | // handleHealth handles health check requests
 86 | func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
 87 | 	type healthResponse struct {
 88 | 		Status   string            `json:"status"`
 89 | 		Services map[string]string `json:"services"`
 90 | 	}
 91 | 
 92 | 	// Check each service
 93 | 	services := map[string]string{
 94 | 		"kubernetes": "unknown",
 95 | 		"argocd":     "unknown",
 96 | 		"gitlab":     "unknown",
 97 | 		"claude":     "unknown",
 98 | 	}
 99 | 
100 | 	ctx := r.Context()
101 | 
102 | 	// Check Kubernetes connectivity
103 | 	if err := s.k8sClient.CheckConnectivity(ctx); err != nil {
104 | 		services["kubernetes"] = "unavailable"
105 | 		s.logger.Warn("Kubernetes health check failed", "error", err)
106 | 	} else {
107 | 		services["kubernetes"] = "available"
108 | 	}
109 | 
110 | 	// Check ArgoCD connectivity
111 | 	if err := s.argoClient.CheckConnectivity(ctx); err != nil {
112 | 		services["argocd"] = "unavailable"
113 | 		s.logger.Warn("ArgoCD health check failed", "error", err)
114 | 	} else {
115 | 		services["argocd"] = "available"
116 | 	}
117 | 
118 | 	// Check GitLab connectivity
119 | 	if err := s.gitlabClient.CheckConnectivity(ctx); err != nil {
120 | 		services["gitlab"] = "unavailable"
121 | 		s.logger.Warn("GitLab health check failed", "error", err)
122 | 	} else {
123 | 		services["gitlab"] = "available"
124 | 	}
125 | 
126 | 	// For Claude, we just assume it's available since we don't want to make an API call
127 | 	// in a health check endpoint
128 | 	services["claude"] = "assumed available"
129 | 
130 | 	// Determine overall status
131 | 	status := "ok"
132 | 	if services["kubernetes"] != "available" {
133 | 		status = "degraded"
134 | 	}
135 | 
136 | 	response := healthResponse{
137 | 		Status:   status,
138 | 		Services: services,
139 | 	}
140 | 
141 | 	w.Header().Set("Content-Type", "application/json")
142 | 	w.WriteHeader(http.StatusOK)
143 | 	json.NewEncoder(w).Encode(response)
144 | }
145 | 
146 | // handleMCPRequest handles generic MCP requests
147 | func (s *Server) handleMCPRequest(w http.ResponseWriter, r *http.Request) {
148 | 	var request models.MCPRequest
149 | 
150 | 	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
151 | 		s.respondWithError(w, http.StatusBadRequest, "Invalid request format", err)
152 | 		return
153 | 	}
154 | 
155 | 	s.logger.Info("Received MCP request", "action", request.Action)
156 | 
157 | 	// Process the request
158 | 	response, err := s.mcpHandler.ProcessRequest(r.Context(), &request)
159 | 	if err != nil {
160 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to process request", err)
161 | 		return
162 | 	}
163 | 
164 | 	s.respondWithJSON(w, http.StatusOK, response)
165 | }
166 | 
167 | // handleResourceQuery handles MCP requests for querying resources
168 | func (s *Server) handleResourceQuery(w http.ResponseWriter, r *http.Request) {
169 | 	var request models.MCPRequest
170 | 
171 | 	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
172 | 		s.respondWithError(w, http.StatusBadRequest, "Invalid request format", err)
173 | 		return
174 | 	}
175 | 
176 | 	// Force action to be queryResource
177 | 	request.Action = "queryResource"
178 | 
179 | 	// Validate resource parameters
180 | 	if request.Resource == "" || request.Name == "" {
181 | 		s.respondWithError(w, http.StatusBadRequest, "Resource and name are required", nil)
182 | 		return
183 | 	}
184 | 
185 | 	s.logger.Info("Received resource query",
186 | 		"resource", request.Resource,
187 | 		"name", request.Name,
188 | 		"namespace", request.Namespace)
189 | 
190 | 	// Special handling for namespace resources to provide comprehensive data
191 | 	if strings.ToLower(request.Resource) == "namespace" {
192 | 		// Get namespace topology
193 | 		topology, err := s.k8sClient.GetNamespaceTopology(r.Context(), request.Name)
194 | 		if err != nil {
195 | 			s.respondWithError(w, http.StatusInternalServerError, "Failed to get namespace topology", err)
196 | 			return
197 | 		}
198 | 
199 | 		// Get all resources in the namespace
200 | 		resources, err := s.k8sClient.GetAllNamespaceResources(r.Context(), request.Name)
201 | 		if err != nil {
202 | 			s.respondWithError(w, http.StatusInternalServerError, "Failed to get namespace resources", err)
203 | 			return
204 | 		}
205 | 
206 | 		// Get namespace analysis
207 | 		analysis, err := s.mcpHandler.AnalyzeNamespace(r.Context(), request.Name)
208 | 		if err != nil {
209 | 			s.respondWithError(w, http.StatusInternalServerError, "Failed to analyze namespace", err)
210 | 			return
211 | 		}
212 | 
213 | 		// Create an enhanced request with the gathered data
214 | 		enhancedRequest := request
215 | 		enhancedRequest.Context = fmt.Sprintf("# Namespace Analysis: %s\n\n", request.Name)
216 | 		enhancedRequest.Context += fmt.Sprintf("## Resource Counts\n")
217 | 		for kind, count := range resources.Stats {
218 | 			enhancedRequest.Context += fmt.Sprintf("- %s: %d\n", kind, count)
219 | 		}
220 | 		enhancedRequest.Context += "\n## Resource Relationships\n"
221 | 		for _, rel := range topology.Relationships {
222 | 			enhancedRequest.Context += fmt.Sprintf("- %s/%s → %s/%s (%s)\n",
223 | 				rel.SourceKind, rel.SourceName, rel.TargetKind, rel.TargetName, rel.RelationType)
224 | 		}
225 | 		enhancedRequest.Context += "\n## Health Status\n"
226 | 		for kind, statuses := range topology.Health {
227 | 			healthy := 0
228 | 			unhealthy := 0
229 | 			progressing := 0
230 | 			unknown := 0
231 | 
232 | 			for _, status := range statuses {
233 | 				switch status {
234 | 				case "healthy":
235 | 					healthy++
236 | 				case "unhealthy":
237 | 					unhealthy++
238 | 				case "progressing":
239 | 					progressing++
240 | 				default:
241 | 					unknown++
242 | 				}
243 | 			}
244 | 
245 | 			enhancedRequest.Context += fmt.Sprintf("- %s: %d healthy, %d unhealthy, %d progressing, %d unknown\n",
246 | 				kind, healthy, unhealthy, progressing, unknown)
247 | 		}
248 | 
249 | 		// Get events for the namespace
250 | 		events, err := s.k8sClient.GetNamespaceEvents(r.Context(), request.Name)
251 | 		if err == nil && len(events) > 0 {
252 | 			enhancedRequest.Context += "\n## Recent Events\n"
253 | 			for i, event := range events {
254 | 				if i >= 10 {
255 | 					break // Limit to 10 events
256 | 				}
257 | 				enhancedRequest.Context += fmt.Sprintf("- [%s] %s: %s\n",
258 | 					event.Type, event.Reason, event.Message)
259 | 			}
260 | 		}
261 | 
262 | 		// Process the enhanced request
263 | 		response, err := s.mcpHandler.ProcessRequest(r.Context(), &enhancedRequest)
264 | 		if err != nil {
265 | 			s.respondWithError(w, http.StatusInternalServerError, "Failed to process request", err)
266 | 			return
267 | 		}
268 | 
269 | 		// Add analysis insights to the response
270 | 		if analysis != nil {
271 | 			response.NamespaceAnalysis = analysis
272 | 		}
273 | 
274 | 		s.respondWithJSON(w, http.StatusOK, response)
275 | 		return
276 | 	}
277 | 
278 | 	// Process regular resource query
279 | 	response, err := s.mcpHandler.ProcessRequest(r.Context(), &request)
280 | 	if err != nil {
281 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to process request", err)
282 | 		return
283 | 	}
284 | 
285 | 	s.respondWithJSON(w, http.StatusOK, response)
286 | }
287 | 
288 | // handleCommitQuery handles MCP requests for analyzing commits
289 | func (s *Server) handleCommitQuery(w http.ResponseWriter, r *http.Request) {
290 | 	var request models.MCPRequest
291 | 
292 | 	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
293 | 		s.respondWithError(w, http.StatusBadRequest, "Invalid request format", err)
294 | 		return
295 | 	}
296 | 
297 | 	// Force action to be queryCommit
298 | 	request.Action = "queryCommit"
299 | 
300 | 	// Validate commit parameters
301 | 	if request.ProjectID == "" || request.CommitSHA == "" {
302 | 		s.respondWithError(w, http.StatusBadRequest, "Project ID and commit SHA are required", nil)
303 | 		return
304 | 	}
305 | 
306 | 	s.logger.Info("Received commit query",
307 | 		"projectId", request.ProjectID,
308 | 		"commitSha", request.CommitSHA)
309 | 
310 | 	// Process the request
311 | 	response, err := s.mcpHandler.ProcessRequest(r.Context(), &request)
312 | 	if err != nil {
313 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to process request", err)
314 | 		return
315 | 	}
316 | 
317 | 	s.respondWithJSON(w, http.StatusOK, response)
318 | }
319 | 
320 | // handleTroubleshoot handles troubleshooting requests
321 | func (s *Server) handleTroubleshoot(w http.ResponseWriter, r *http.Request) {
322 | 	var request struct {
323 | 		Resource  string `json:"resource"`
324 | 		Name      string `json:"name"`
325 | 		Namespace string `json:"namespace"`
326 | 		Query     string `json:"query,omitempty"`
327 | 	}
328 | 
329 | 	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
330 | 		s.respondWithError(w, http.StatusBadRequest, "Invalid request format", err)
331 | 		return
332 | 	}
333 | 
334 | 	// Validate parameters
335 | 	if request.Resource == "" || request.Name == "" {
336 | 		s.respondWithError(w, http.StatusBadRequest, "Resource and name are required", nil)
337 | 		return
338 | 	}
339 | 
340 | 	s.logger.Info("Received troubleshoot request",
341 | 		"resource", request.Resource,
342 | 		"name", request.Name,
343 | 		"namespace", request.Namespace)
344 | 
345 | 	// Process the troubleshooting request
346 | 	result, err := s.troubleshootCorrelator.TroubleshootResource(
347 | 		r.Context(),
348 | 		request.Namespace,
349 | 		request.Resource,
350 | 		request.Name,
351 | 	)
352 | 	if err != nil {
353 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to troubleshoot resource", err)
354 | 		return
355 | 	}
356 | 
357 | 	// If there's a query, use Claude to analyze the results
358 | 	if request.Query != "" {
359 | 		mcpRequest := &models.MCPRequest{
360 | 			Resource:  request.Resource,
361 | 			Name:      request.Name,
362 | 			Namespace: request.Namespace,
363 | 			Query:     request.Query,
364 | 		}
365 | 
366 | 		response, err := s.mcpHandler.ProcessTroubleshootRequest(r.Context(), mcpRequest, result)
367 | 		if err != nil {
368 | 			s.respondWithError(w, http.StatusInternalServerError, "Failed to process troubleshoot analysis", err)
369 | 			return
370 | 		}
371 | 
372 | 		// Add the troubleshoot result to the response
373 | 		responseWithResult := struct {
374 | 			*models.MCPResponse
375 | 			TroubleshootResult *models.TroubleshootResult `json:"troubleshootResult"`
376 | 		}{
377 | 			MCPResponse:        response,
378 | 			TroubleshootResult: result,
379 | 		}
380 | 
381 | 		s.respondWithJSON(w, http.StatusOK, responseWithResult)
382 | 		return
383 | 	}
384 | 
385 | 	// If no query, just return the troubleshoot result
386 | 	s.respondWithJSON(w, http.StatusOK, result)
387 | }
388 | 
389 | // handleListNamespaces handles requests to list namespaces
390 | func (s *Server) handleListNamespaces(w http.ResponseWriter, r *http.Request) {
391 | 	namespaces, err := s.k8sClient.GetNamespaces(r.Context())
392 | 	if err != nil {
393 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to list namespaces", err)
394 | 		return
395 | 	}
396 | 
397 | 	s.respondWithJSON(w, http.StatusOK, map[string][]string{"namespaces": namespaces})
398 | }
399 | 
400 | // handleListResources handles requests to list resources of a specific type
401 | func (s *Server) handleListResources(w http.ResponseWriter, r *http.Request) {
402 | 	vars := mux.Vars(r)
403 | 	resourceType := vars["resource"]
404 | 	namespace := r.URL.Query().Get("namespace")
405 | 
406 | 	resources, err := s.k8sClient.ListResources(r.Context(), resourceType, namespace)
407 | 	if err != nil {
408 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to list resources", err)
409 | 		return
410 | 	}
411 | 
412 | 	s.respondWithJSON(w, http.StatusOK, map[string]interface{}{"resources": resources})
413 | }
414 | 
415 | // handleGetResource handles requests to get a specific resource
416 | func (s *Server) handleGetResource(w http.ResponseWriter, r *http.Request) {
417 | 	vars := mux.Vars(r)
418 | 	resourceType := vars["resource"]
419 | 	name := vars["name"]
420 | 	namespace := r.URL.Query().Get("namespace")
421 | 
422 | 	resource, err := s.k8sClient.GetResource(r.Context(), resourceType, namespace, name)
423 | 	if err != nil {
424 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to get resource", err)
425 | 		return
426 | 	}
427 | 
428 | 	s.respondWithJSON(w, http.StatusOK, resource)
429 | }
430 | 
431 | // handleGetEvents handles requests to get events
432 | func (s *Server) handleGetEvents(w http.ResponseWriter, r *http.Request) {
433 | 	namespace := r.URL.Query().Get("namespace")
434 | 	resourceType := r.URL.Query().Get("resource")
435 | 	name := r.URL.Query().Get("name")
436 | 
437 | 	events, err := s.k8sClient.GetResourceEvents(r.Context(), namespace, resourceType, name)
438 | 	if err != nil {
439 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to get events", err)
440 | 		return
441 | 	}
442 | 
443 | 	s.respondWithJSON(w, http.StatusOK, map[string]interface{}{"events": events})
444 | }
445 | 
446 | // handleListArgoApplications handles requests to list ArgoCD applications
447 | func (s *Server) handleListArgoApplications(w http.ResponseWriter, r *http.Request) {
448 | 	applications, err := s.argoClient.ListApplications(r.Context())
449 | 	if err != nil {
450 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to list ArgoCD applications", err)
451 | 		return
452 | 	}
453 | 
454 | 	s.respondWithJSON(w, http.StatusOK, map[string]interface{}{"applications": applications})
455 | }
456 | 
457 | // handleGetArgoApplication handles requests to get a specific ArgoCD application
458 | func (s *Server) handleGetArgoApplication(w http.ResponseWriter, r *http.Request) {
459 | 	vars := mux.Vars(r)
460 | 	name := vars["name"]
461 | 
462 | 	application, err := s.argoClient.GetApplication(r.Context(), name)
463 | 	if err != nil {
464 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to get ArgoCD application", err)
465 | 		return
466 | 	}
467 | 
468 | 	s.respondWithJSON(w, http.StatusOK, application)
469 | }
470 | 
471 | // handleListGitLabProjects handles requests to list GitLab projects
472 | func (s *Server) handleListGitLabProjects(w http.ResponseWriter, r *http.Request) {
473 | 	// This would typically include pagination parameters
474 | 	projects, err := s.gitlabClient.ListProjects(r.Context())
475 | 	if err != nil {
476 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to list GitLab projects", err)
477 | 		return
478 | 	}
479 | 
480 | 	s.respondWithJSON(w, http.StatusOK, map[string]interface{}{"projects": projects})
481 | }
482 | 
483 | // handleListGitLabPipelines handles requests to list GitLab pipelines
484 | func (s *Server) handleListGitLabPipelines(w http.ResponseWriter, r *http.Request) {
485 | 	vars := mux.Vars(r)
486 | 	projectId := vars["projectId"]
487 | 
488 | 	pipelines, err := s.gitlabClient.ListPipelines(r.Context(), projectId)
489 | 	if err != nil {
490 | 		s.respondWithError(w, http.StatusInternalServerError, "Failed to list GitLab pipelines", err)
491 | 		return
492 | 	}
493 | 
494 | 	s.respondWithJSON(w, http.StatusOK, map[string]interface{}{"pipelines": pipelines})
495 | }
496 | 
497 | // Helper methods
498 | 
499 | // respondWithError sends an error response to the client
500 | func (s *Server) respondWithError(w http.ResponseWriter, code int, message string, err error) {
501 | 	errorResponse := map[string]string{
502 | 		"error": message,
503 | 	}
504 | 
505 | 	if err != nil {
506 | 		errorResponse["details"] = err.Error()
507 | 		s.logger.Error(message, "error", err, "code", code)
508 | 	} else {
509 | 		s.logger.Warn(message, "code", code)
510 | 	}
511 | 
512 | 	w.Header().Set("Content-Type", "application/json")
513 | 	w.WriteHeader(code)
514 | 	json.NewEncoder(w).Encode(errorResponse)
515 | }
516 | 
517 | // respondWithJSON sends a JSON response to the client
518 | func (s *Server) respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
519 | 	w.Header().Set("Content-Type", "application/json")
520 | 	w.WriteHeader(code)
521 | 	json.NewEncoder(w).Encode(payload)
522 | }
523 | 
```
Page 3/5FirstPrevNextLast