#
tokens: 48192/50000 26/82 files (page 2/5)
lines: off (toggle) GitHub
raw markdown copy
This is page 2 of 5. Use http://codebase.md/blankcut/kubernetes-mcp-server?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

--------------------------------------------------------------------------------
/docs/src/components/Header.astro:
--------------------------------------------------------------------------------

```
---
import Search from './Search.astro';
---

<header class="sticky top-0 z-40 w-full backdrop-blur bg-white/90 dark:bg-slate-900/90 border-b border-slate-200 dark:border-slate-700">
  <div class="container mx-auto px-4 py-3 flex justify-between items-center">
    <a href="/" class="flex items-center space-x-2">
      <img src="/images/logo.svg" alt="Kubernetes Claude MCP" class="h-8 w-8" />
      <span class="font-bold text-xl">Kubernetes Claude MCP</span>
    </a>
    <div class="hidden md:flex items-center space-x-6">
      <Search />
      <nav class="flex space-x-6">
        <a href="/docs/introduction" class="text-slate-700 hover:text-primary-600 dark:text-slate-300 dark:hover:text-primary-400">Documentation</a>
        <a href="/examples" class="text-slate-700 hover:text-primary-600 dark:text-slate-300 dark:hover:text-primary-400">Examples</a>
      </nav>
      <a href="https://github.com/blankcut/kubernetes-mcp-server" target="_blank" rel="noopener noreferrer" class="text-slate-700 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white">
        <span class="sr-only">GitHub</span>
        <svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
          <path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"></path>
        </svg>
      </a>
    </div>
    <button id="mobile-menu-toggle" class="md:hidden p-2">
      <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
        <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"></path>
      </svg>
    </button>
  </div>
  <div id="mobile-menu" class="md:hidden hidden">
    <div class="px-4 py-3 space-y-4">
      <Search />
      <nav class="flex flex-col space-y-3">
        <a href="/docs/introduction" class="text-slate-700 hover:text-primary-600 dark:text-slate-300 dark:hover:text-primary-400">Documentation</a>
        <a href="/examples" class="text-slate-700 hover:text-primary-600 dark:text-slate-300 dark:hover:text-primary-400">Examples</a>
        <a href="https://github.com/blankcut/kubernetes-mcp-server" target="_blank" rel="noopener noreferrer" class="text-slate-700 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white flex items-center">
          <svg class="h-5 w-5 mr-2" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
            <path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"></path>
          </svg>
          GitHub
        </a>
      </nav>
    </div>
  </div>
</header>

<script>
  const mobileMenuToggle = document.getElementById('mobile-menu-toggle');
  const mobileMenu = document.getElementById('mobile-menu');

  if (mobileMenuToggle && mobileMenu) {
    mobileMenuToggle.addEventListener('click', () => {
      mobileMenu.classList.toggle('hidden');
    });
  }
</script>
```

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

```go
package claude

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
)

// Client handles communication with the Claude API
type Client struct {
	apiKey      string
	baseURL     string
	modelID     string
	maxTokens   int
	temperature float64
	httpClient  *http.Client
	logger      *logging.Logger
}

// Message represents a message in the Claude conversation
type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

// CompletionRequest represents a request to the Claude API
type CompletionRequest struct {
	Model       string    `json:"model"`
	System      string    `json:"system,omitempty"`
	Messages    []Message `json:"messages"`
	MaxTokens   int       `json:"max_tokens,omitempty"`
	Temperature float64   `json:"temperature,omitempty"`
}

// ContentItem represents an item in the content array of a response
type ContentItem struct {
	Type string `json:"type"`
	Text string `json:"text"`
}

// CompletionResponse represents a response from the Claude API
type CompletionResponse struct {
	ID      string        `json:"id"`
	Type    string        `json:"type"`
	Model   string        `json:"model"`
	Content []ContentItem `json:"content"`
	Usage   Usage         `json:"usage"`
}

// Usage represents token usage information
type Usage struct {
	InputTokens  int `json:"input_tokens"`
	OutputTokens int `json:"output_tokens"`
}

// NewClient creates a new Claude API client
func NewClient(cfg ClaudeConfig, logger *logging.Logger) *Client {
	if logger == nil {
		logger = logging.NewLogger().Named("claude")
	}
	
	return &Client{
		apiKey:      cfg.APIKey,
		baseURL:     cfg.BaseURL,
		modelID:     cfg.ModelID,
		maxTokens:   cfg.MaxTokens,
		temperature: cfg.Temperature,
		httpClient: &http.Client{
			Timeout: 120 * time.Second,
		},
		logger: logger,
	}
}

// ClaudeConfig holds configuration for the Claude API client
type ClaudeConfig struct {
	APIKey      string  `yaml:"apiKey"`
	BaseURL     string  `yaml:"baseURL"`
	ModelID     string  `yaml:"modelID"`
	MaxTokens   int     `yaml:"maxTokens"`
	Temperature float64 `yaml:"temperature"`
}

// Complete sends a completion request to the Claude API
func (c *Client) Complete(ctx context.Context, messages []Message) (string, error) {
	c.logger.Debug("Sending completion request", 
		"model", c.modelID, 
		"messageCount", len(messages))
	
	// Extract system message if present
	var systemPrompt string
	var userMessages []Message
	
	for _, msg := range messages {
		if msg.Role == "system" {
			systemPrompt = msg.Content
		} else {
			userMessages = append(userMessages, msg)
		}
	}
	
	reqBody := CompletionRequest{
		Model:       c.modelID,
		System:      systemPrompt,
		Messages:    userMessages,
		MaxTokens:   c.maxTokens,
		Temperature: c.temperature,
	}

	reqJSON, err := json.Marshal(reqBody)
	if err != nil {
		return "", fmt.Errorf("failed to marshal request: %w", err)
	}

	req, err := http.NewRequestWithContext(
		ctx,
		http.MethodPost,
		c.baseURL+"/v1/messages",
		bytes.NewBuffer(reqJSON),
	)
	if err != nil {
		return "", fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("x-api-key", c.apiKey)
	req.Header.Set("anthropic-version", "2023-06-01")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return "", fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("failed to read response body: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, body)
	}

	var completionResponse CompletionResponse
	if err := json.Unmarshal(body, &completionResponse); err != nil {
		return "", fmt.Errorf("failed to unmarshal response: %w", err)
	}

	// Extract text from content array
	var responseText string
	for _, content := range completionResponse.Content {
		if content.Type == "text" {
			responseText += content.Text
		}
	}

	c.logger.Debug("Received completion response", 
		"model", completionResponse.Model, 
		"inputTokens", completionResponse.Usage.InputTokens,
		"outputTokens", completionResponse.Usage.OutputTokens)
	
	return responseText, nil
}
```

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

```go
package gitlab

import (
	"io"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
)

// ListPipelines returns a list of pipelines for a project
func (c *Client) ListPipelines(ctx context.Context, projectID string) ([]models.GitLabPipeline, error) {
	c.logger.Debug("Listing pipelines", "projectID", projectID)
	
	endpoint := fmt.Sprintf("projects/%s/pipelines", url.PathEscape(projectID))
	
	// Add query parameters for pagination
	u, err := url.Parse(endpoint)
	if err != nil {
		return nil, fmt.Errorf("invalid endpoint: %w", err)
	}
	
	q := u.Query()
	q.Set("per_page", "20")
	q.Set("order_by", "id")
	q.Set("sort", "desc")
	u.RawQuery = q.Encode()
	
	resp, err := c.doRequest(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var pipelines []models.GitLabPipeline
	if err := json.NewDecoder(resp.Body).Decode(&pipelines); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	c.logger.Debug("Listed pipelines", "projectID", projectID, "count", len(pipelines))
	return pipelines, nil
}

// GetPipeline returns details about a specific pipeline
func (c *Client) GetPipeline(ctx context.Context, projectID string, pipelineID int) (*models.GitLabPipeline, error) {
	c.logger.Debug("Getting pipeline", "projectID", projectID, "pipelineID", pipelineID)
	
	endpoint := fmt.Sprintf("projects/%s/pipelines/%d", url.PathEscape(projectID), pipelineID)
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var pipeline models.GitLabPipeline
	if err := json.NewDecoder(resp.Body).Decode(&pipeline); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &pipeline, nil
}

// GetPipelineJobs returns jobs for a specific pipeline
func (c *Client) GetPipelineJobs(ctx context.Context, projectID string, pipelineID int) ([]models.GitLabJob, error) {
	c.logger.Debug("Getting pipeline jobs", "projectID", projectID, "pipelineID", pipelineID)
	
	endpoint := fmt.Sprintf("projects/%s/pipelines/%d/jobs", url.PathEscape(projectID), pipelineID)
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var jobs []models.GitLabJob
	if err := json.NewDecoder(resp.Body).Decode(&jobs); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	c.logger.Debug("Got pipeline jobs", "projectID", projectID, "pipelineID", pipelineID, "count", len(jobs))
	return jobs, nil
}

// FindRecentDeployments finds recent deployments to a specific environment
func (c *Client) FindRecentDeployments(ctx context.Context, projectID, environment string) ([]models.GitLabDeployment, error) {
	c.logger.Debug("Finding recent deployments", 
		"projectID", projectID, 
		"environment", environment)
	
	// Create endpoint with query parameters
	endpoint := fmt.Sprintf("projects/%s/deployments", url.PathEscape(projectID))
	
	u, err := url.Parse(endpoint)
	if err != nil {
		return nil, fmt.Errorf("invalid endpoint: %w", err)
	}
	
	q := u.Query()
	q.Set("environment", environment)
	q.Set("order_by", "created_at")
	q.Set("sort", "desc")
	q.Set("per_page", "10")
	u.RawQuery = q.Encode()
	
	resp, err := c.doRequest(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var deployments []models.GitLabDeployment
	if err := json.NewDecoder(resp.Body).Decode(&deployments); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	c.logger.Debug("Found deployments", 
		"projectID", projectID, 
		"environment", environment, 
		"count", len(deployments))
	return deployments, nil
}

// GetJobLogs retrieves logs for a specific job
func (c *Client) GetJobLogs(ctx context.Context, projectID string, jobID int) (string, error) {
	c.logger.Debug("Getting job logs", "projectID", projectID, "jobID", jobID)
	
	endpoint := fmt.Sprintf("projects/%s/jobs/%d/trace", url.PathEscape(projectID), jobID)
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	logs, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("failed to read logs: %w", err)
	}

	return string(logs), nil
}
```

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

```go
package models

// ResourceContext combines information about a Kubernetes resource with GitOps context
type ResourceContext struct {
	// Basic resource information
	Kind         string                 `json:"kind"`
	Name         string                 `json:"name"`
	Namespace    string                 `json:"namespace"`
	APIVersion   string                 `json:"apiVersion"`
	Metadata     map[string]interface{} `json:"metadata,omitempty"`
	ResourceData string                 `json:"resourceData,omitempty"`

	// Related ArgoCD information
	ArgoApplication  *ArgoApplication         `json:"argoApplication,omitempty"`
	ArgoSyncStatus   string                   `json:"argoSyncStatus,omitempty"`
	ArgoHealthStatus string                   `json:"argoHealthStatus,omitempty"`
	ArgoSyncHistory  []ArgoApplicationHistory `json:"argoSyncHistory,omitempty"`

	// Related GitLab information
	GitLabProject  *GitLabProject    `json:"gitlabProject,omitempty"`
	LastPipeline   *GitLabPipeline   `json:"lastPipeline,omitempty"`
	LastDeployment *GitLabDeployment `json:"lastDeployment,omitempty"`
	RecentCommits  []GitLabCommit    `json:"recentCommits,omitempty"`

	// Additional context
	Events           []K8sEvent `json:"events,omitempty"`
	RelatedResources []string   `json:"relatedResources,omitempty"`
	Errors           []string   `json:"errors,omitempty"`
}

// Issue represents a discovered issue or potential problem
type Issue struct {
	Title       string `json:"title"`
	Category    string `json:"category"`
	Severity    string `json:"severity"`
	Source      string `json:"source"`
	Description string `json:"description"`
}

// TroubleshootResult contains troubleshooting findings and recommendations
type TroubleshootResult struct {
	ResourceContext ResourceContext `json:"resourceContext"`
	Issues          []Issue         `json:"issues"`
	Recommendations []string        `json:"recommendations"`
}

// MCPRequest represents a request to the MCP server
type MCPRequest struct {
	Action          string                 `json:"action"`
	Resource        string                 `json:"resource,omitempty"`
	Namespace       string                 `json:"namespace,omitempty"`
	Name            string                 `json:"name,omitempty"`
	Query           string                 `json:"query,omitempty"`
	CommitSHA       string                 `json:"commitSha,omitempty"`
	ProjectID       string                 `json:"projectId,omitempty"`
	MergeRequestIID int                    `json:"mergeRequestIid,omitempty"`
	ResourceSpecs   map[string]interface{} `json:"resourceSpecs,omitempty"`
	Context         string                 `json:"context,omitempty"`
}

// ResourceRelationship represents a relationship between two resources
type ResourceRelationship struct {
	SourceKind      string `json:"sourceKind"`
	SourceName      string `json:"sourceName"`
	SourceNamespace string `json:"sourceNamespace"`
	TargetKind      string `json:"targetKind"`
	TargetName      string `json:"targetName"`
	TargetNamespace string `json:"targetNamespace"`
	RelationType    string `json:"relationType"`
}

// NamespaceAnalysisResult contains the analysis of a namespace's resources
type NamespaceAnalysisResult struct {
	Namespace             string                    `json:"namespace"`
	ResourceCounts        map[string]int            `json:"resourceCounts"`
	HealthStatus          map[string]map[string]int `json:"healthStatus"`
	ResourceRelationships []ResourceRelationship    `json:"resourceRelationships"`
	Issues                []Issue                   `json:"issues"`
	Recommendations       []string                  `json:"recommendations"`
	Analysis              string                    `json:"analysis"`
}

// MCPResponse represents a response from the MCP server
type MCPResponse struct {
	Success            bool                     `json:"success"`
	Message            string                   `json:"message,omitempty"`
	Analysis           string                   `json:"analysis,omitempty"`
	Context            ResourceContext          `json:"context,omitempty"`
	Actions            []string                 `json:"actions,omitempty"`
	ErrorDetails       string                   `json:"errorDetails,omitempty"`
	TroubleshootResult *TroubleshootResult      `json:"troubleshootResult,omitempty"`
	NamespaceAnalysis  *NamespaceAnalysisResult `json:"namespaceAnalysis,omitempty"`
}

```

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

```go
package auth

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
)

// SecretsManager handles access to secrets stored in various backends
type SecretsManager struct {
	logger *logging.Logger
	// Directory where secrets files are stored
	secretsDir string
	// Flag to indicate if secrets manager is available
	available bool
}

// NewSecretsManager creates a new secrets manager
func NewSecretsManager(logger *logging.Logger) *SecretsManager {
	if logger == nil {
		logger = logging.NewLogger().Named("secrets")
	}
	
	// Default secrets directory is ./secrets
	secretsDir := os.Getenv("SECRETS_DIR")
	if secretsDir == "" {
		secretsDir = "./secrets"
	}
	
	// Check if secrets directory exists
	_, err := os.Stat(secretsDir)
	available := err == nil
	
	if !available {
		logger.Warn("Secrets directory not available", "directory", secretsDir)
	}
	
	return &SecretsManager{
		logger:     logger,
		secretsDir: secretsDir,
		available:  available,
	}
}

// IsAvailable returns true if the secrets manager is available
func (sm *SecretsManager) IsAvailable() bool {
	return sm.available
}

// GetCredentials retrieves credentials for a service from the secrets manager
func (sm *SecretsManager) GetCredentials(ctx context.Context, service string) (*Credentials, error) {
	if !sm.available {
		return nil, fmt.Errorf("secrets manager not available")
	}
	
	// Build paths to potential secret files
	tokenPath := filepath.Join(sm.secretsDir, service, "token")
	apiKeyPath := filepath.Join(sm.secretsDir, service, "apikey")
	usernamePath := filepath.Join(sm.secretsDir, service, "username")
	passwordPath := filepath.Join(sm.secretsDir, service, "password")
	
	// Initialize credentials
	creds := &Credentials{}
	
	// Try to read token
	tokenBytes, err := os.ReadFile(tokenPath)
	if err == nil {
		creds.Token = string(tokenBytes)
		sm.logger.Debug("Loaded token from file", "service", service)
	}
	
	// Try to read API key
	apiKeyBytes, err := os.ReadFile(apiKeyPath)
	if err == nil {
		creds.APIKey = string(apiKeyBytes)
		sm.logger.Debug("Loaded API key from file", "service", service)
	}
	
	// Try to read username
	usernameBytes, err := os.ReadFile(usernamePath)
	if err == nil {
		creds.Username = string(usernameBytes)
		sm.logger.Debug("Loaded username from file", "service", service)
	}
	
	// Try to read password
	passwordBytes, err := os.ReadFile(passwordPath)
	if err == nil {
		creds.Password = string(passwordBytes)
		sm.logger.Debug("Loaded password from file", "service", service)
	}
	
	// Check if we loaded any credentials
	if creds.Token == "" && creds.APIKey == "" && creds.Username == "" && creds.Password == "" {
		return nil, fmt.Errorf("no credentials found for service: %s", service)
	}
	
	return creds, nil
}

// SaveCredentials saves credentials for a service to the secrets manager
func (sm *SecretsManager) SaveCredentials(ctx context.Context, service string, creds *Credentials) error {
	if !sm.available {
		return fmt.Errorf("secrets manager not available")
	}
	
	// Create service directory if it doesn't exist
	serviceDir := filepath.Join(sm.secretsDir, service)
	if err := os.MkdirAll(serviceDir, 0700); err != nil {
		return fmt.Errorf("failed to create service directory: %w", err)
	}
	
	// Save token if provided
	if creds.Token != "" {
		tokenPath := filepath.Join(serviceDir, "token")
		if err := os.WriteFile(tokenPath, []byte(creds.Token), 0600); err != nil {
			return fmt.Errorf("failed to save token: %w", err)
		}
		sm.logger.Debug("Saved token to file", "service", service)
	}
	
	// Save API key if provided
	if creds.APIKey != "" {
		apiKeyPath := filepath.Join(serviceDir, "apikey")
		if err := os.WriteFile(apiKeyPath, []byte(creds.APIKey), 0600); err != nil {
			return fmt.Errorf("failed to save API key: %w", err)
		}
		sm.logger.Debug("Saved API key to file", "service", service)
	}
	
	// Save username if provided
	if creds.Username != "" {
		usernamePath := filepath.Join(serviceDir, "username")
		if err := os.WriteFile(usernamePath, []byte(creds.Username), 0600); err != nil {
			return fmt.Errorf("failed to save username: %w", err)
		}
		sm.logger.Debug("Saved username to file", "service", service)
	}
	
	// Save password if provided
	if creds.Password != "" {
		passwordPath := filepath.Join(serviceDir, "password")
		if err := os.WriteFile(passwordPath, []byte(creds.Password), 0600); err != nil {
			return fmt.Errorf("failed to save password: %w", err)
		}
		sm.logger.Debug("Saved password to file", "service", service)
	}
	
	return nil
}
```

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

```go
package argocd

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
)

// ListApplications returns a list of all ArgoCD applications
func (c *Client) ListApplications(ctx context.Context) ([]models.ArgoApplication, error) {
	c.logger.Debug("Listing ArgoCD applications")
	
	// Try the v1 API path
	endpoint := "/api/v1/applications"
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var result struct {
		Items []models.ArgoApplication `json:"items"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	c.logger.Debug("Listed ArgoCD applications", "count", len(result.Items))
	return result.Items, nil
}

// GetApplication returns details about a specific ArgoCD application
func (c *Client) GetApplication(ctx context.Context, name string) (*models.ArgoApplication, error) {
	c.logger.Debug("Getting ArgoCD application", "name", name)
	
	endpoint := fmt.Sprintf("/api/v1/applications/%s", url.PathEscape(name))
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var app models.ArgoApplication
	if err := json.NewDecoder(resp.Body).Decode(&app); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &app, nil
}

// GetResourceTree returns the resource hierarchy for an application
func (c *Client) GetResourceTree(ctx context.Context, name string) (*models.ArgoResourceTree, error) {
	c.logger.Debug("Getting resource tree for application", "name", name)
	
	endpoint := fmt.Sprintf("/api/v1/applications/%s/resource-tree", url.PathEscape(name))
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var tree models.ArgoResourceTree
	if err := json.NewDecoder(resp.Body).Decode(&tree); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	c.logger.Debug("Retrieved resource tree", "name", name, "nodeCount", len(tree.Nodes))
	return &tree, nil
}

// FindApplicationsByResource finds all ArgoCD applications that manage a specific Kubernetes resource
func (c *Client) FindApplicationsByResource(ctx context.Context, kind, name, namespace string) ([]models.ArgoApplication, error) {
	c.logger.Debug("Finding applications by resource", 
		"kind", kind, 
		"name", name, 
		"namespace", namespace)
	
	// First try to use the resource API endpoint if available
	endpoint := fmt.Sprintf("/api/v1/applications/resource/%s/%s/%s/%s/%s",
		url.PathEscape(""),
		url.PathEscape(kind),
		url.PathEscape(namespace),
		url.PathEscape(name),
		url.PathEscape(""),
	)
	
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err == nil {
		defer resp.Body.Close()
		
		var appRefs []struct {
			Name string `json:"name"`
		}
		
		if err := json.NewDecoder(resp.Body).Decode(&appRefs); err != nil {
			c.logger.Warn("Failed to decode application references", "error", err)
		} else if len(appRefs) > 0 {
			// Get full application details for each reference
			var apps []models.ArgoApplication
			for _, ref := range appRefs {
				app, err := c.GetApplication(ctx, ref.Name)
				if err != nil {
					c.logger.Warn("Failed to get application details", 
						"name", ref.Name, 
						"error", err)
					continue
				}
				apps = append(apps, *app)
			}
			
			c.logger.Debug("Found applications by resource API", 
				"resourceKind", kind, 
				"resourceName", name, 
				"count", len(apps))
			return apps, nil
		}
	}
	
	// Fallback: Get all applications and check their resource trees
	c.logger.Debug("Resource API failed, falling back to application scanning")
	apps, err := c.ListApplications(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to list applications: %w", err)
	}

	var matchingApps []models.ArgoApplication

	// For each application, check if it manages the specified resource
	for _, app := range apps {
		tree, err := c.GetResourceTree(ctx, app.Name)
		if err != nil {
			c.logger.Warn("Failed to get resource tree", 
				"application", app.Name, 
				"error", err)
			continue // Skip this app if we can't get its resource tree
		}

		for _, node := range tree.Nodes {
			// Match against the specified resource
			if strings.EqualFold(node.Kind, kind) && 
			   node.Name == name && 
			   (namespace == "" || node.Namespace == namespace) {
				matchingApps = append(matchingApps, app)
				break // Found a match in this app, move to the next app
			}
		}
	}

	c.logger.Debug("Found applications managing resource by scanning", 
		"resourceKind", kind, 
		"resourceName", name, 
		"count", len(matchingApps))
	return matchingApps, nil
}
```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/cmd/server/main.go:
--------------------------------------------------------------------------------

```go
package main

import (
	"context"
	"flag"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/api"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/argocd"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/auth"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/claude"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/correlator"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/gitlab"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/k8s"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/mcp"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/config"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
)

func main() {

	// Parse command line flags
	configPath := flag.String("config", "config.yaml", "path to config file")
	logLevel := flag.String("log-level", "info", "logging level (debug, info, warn, error)")
	flag.Parse()

	// Initialize logger
	os.Setenv("LOG_LEVEL", *logLevel)
	logger := logging.NewLogger()
	logger.Info("Starting Kubernetes Claude MCP server")

	// Load configuration
	logger.Info("Loading configuration", "path", *configPath)
	cfg, err := config.Load(*configPath)
	if err != nil {
		logger.Fatal("Failed to load configuration", "error", err)
	}

	// Validate configuration
	if err := cfg.Validate(); err != nil {
		logger.Fatal("Invalid configuration", "error", err)
	}

	// Set up context with cancellation
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Initialize credential provider
	logger.Info("Initializing credential provider")
	credProvider := auth.NewCredentialProvider(cfg)
	if err := credProvider.LoadCredentials(ctx); err != nil {
		logger.Fatal("Failed to load credentials", "error", err)
	}

	// Initialize Kubernetes client
	logger.Info("Initializing Kubernetes client")
	k8sClient, err := k8s.NewClient(cfg.Kubernetes, logger.Named("k8s"))
	if err != nil {
		logger.Fatal("Failed to create Kubernetes client", "error", err)
	}

	// Check Kubernetes connectivity
	if err := k8sClient.CheckConnectivity(ctx); err != nil {
		logger.Warn("Kubernetes connectivity check failed", "error", err)
	} else {
		logger.Info("Kubernetes connectivity confirmed")
	}

	// Initialize ArgoCD client
	logger.Info("Initializing ArgoCD client")
	argoClient := argocd.NewClient(&cfg.ArgoCD, credProvider, logger.Named("argocd"))

	// Check ArgoCD connectivity (don't fail if unavailable)
	if err := argoClient.CheckConnectivity(ctx); err != nil {
		logger.Warn("ArgoCD connectivity check failed", "error", err)
	} else {
		logger.Info("ArgoCD connectivity confirmed")
	}

	// Initialize GitLab client
	logger.Info("Initializing GitLab client")
	gitlabClient := gitlab.NewClient(&cfg.GitLab, credProvider, logger.Named("gitlab"))

	// Check GitLab connectivity (don't fail if unavailable)
	if err := gitlabClient.CheckConnectivity(ctx); err != nil {
		logger.Warn("GitLab connectivity check failed", "error", err)
	} else {
		logger.Info("GitLab connectivity confirmed")
	}

	// Initialize Claude client
	logger.Info("Initializing Claude client")
	claudeConfig := claude.ClaudeConfig{
		APIKey:      cfg.Claude.APIKey,
		BaseURL:     cfg.Claude.BaseURL,
		ModelID:     cfg.Claude.ModelID,
		MaxTokens:   cfg.Claude.MaxTokens,
		Temperature: cfg.Claude.Temperature,
	}
	claudeClient := claude.NewClient(claudeConfig, logger.Named("claude"))

	// Initialize GitOps correlator
	logger.Info("Initializing GitOps correlator")
	gitOpsCorrelator := correlator.NewGitOpsCorrelator(
		k8sClient, 
		argoClient, 
		gitlabClient, 
		logger.Named("correlator"),
	)

	// Initialize troubleshoot correlator
	troubleshootCorrelator := correlator.NewTroubleshootCorrelator(
		gitOpsCorrelator, 
		k8sClient,
		logger.Named("troubleshoot"),
	)

	// Initialize MCP protocol handler
	logger.Info("Initializing MCP protocol handler")
	mcpHandler := mcp.NewProtocolHandler(
		claudeClient, 
		gitOpsCorrelator,
		k8sClient,
		logger.Named("mcp"),
	)

	// Initialize API server
	logger.Info("Initializing API server")
	server := api.NewServer(
		cfg.Server, 
		k8sClient, 
		argoClient, 
		gitlabClient, 
		mcpHandler,
		troubleshootCorrelator,
		logger.Named("api"),
	)

	// Handle graceful shutdown
	go func() {
		sigCh := make(chan os.Signal, 1)
		signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
		sig := <-sigCh
		logger.Info("Received shutdown signal", "signal", sig)
		
		// Create a timeout context for shutdown
		shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
		defer shutdownCancel()
		
		logger.Info("Shutting down server...")
		cancel() // Cancel the main context
		
		// Wait for server to shut down or timeout
		<-shutdownCtx.Done()
	}()

	// Start server
	logger.Info("Starting MCP server", "address", cfg.Server.Address)
	if err := server.Start(ctx); err != nil {
		logger.Fatal("Server error", "error", err)
	}

	logger.Info("Server shutdown complete")
}
```

--------------------------------------------------------------------------------
/docs/.astro/content.d.ts:
--------------------------------------------------------------------------------

```typescript
declare module 'astro:content' {
	interface Render {
		'.mdx': Promise<{
			Content: import('astro').MarkdownInstance<{}>['Content'];
			headings: import('astro').MarkdownHeading[];
			remarkPluginFrontmatter: Record<string, any>;
			components: import('astro').MDXInstance<{}>['components'];
		}>;
	}
}

declare module 'astro:content' {
	export interface RenderResult {
		Content: import('astro/runtime/server/index.js').AstroComponentFactory;
		headings: import('astro').MarkdownHeading[];
		remarkPluginFrontmatter: Record<string, any>;
	}
	interface Render {
		'.md': Promise<RenderResult>;
	}

	export interface RenderedContent {
		html: string;
		metadata?: {
			imagePaths: Array<string>;
			[key: string]: unknown;
		};
	}
}

declare module 'astro:content' {
	type Flatten<T> = T extends { [K: string]: infer U } ? U : never;

	export type CollectionKey = keyof AnyEntryMap;
	export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;

	export type ContentCollectionKey = keyof ContentEntryMap;
	export type DataCollectionKey = keyof DataEntryMap;

	type AllValuesOf<T> = T extends any ? T[keyof T] : never;
	type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
		ContentEntryMap[C]
	>['slug'];

	export type ReferenceDataEntry<
		C extends CollectionKey,
		E extends keyof DataEntryMap[C] = string,
	> = {
		collection: C;
		id: E;
	};
	export type ReferenceContentEntry<
		C extends keyof ContentEntryMap,
		E extends ValidContentEntrySlug<C> | (string & {}) = string,
	> = {
		collection: C;
		slug: E;
	};

	/** @deprecated Use `getEntry` instead. */
	export function getEntryBySlug<
		C extends keyof ContentEntryMap,
		E extends ValidContentEntrySlug<C> | (string & {}),
	>(
		collection: C,
		// Note that this has to accept a regular string too, for SSR
		entrySlug: E,
	): E extends ValidContentEntrySlug<C>
		? Promise<CollectionEntry<C>>
		: Promise<CollectionEntry<C> | undefined>;

	/** @deprecated Use `getEntry` instead. */
	export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(
		collection: C,
		entryId: E,
	): Promise<CollectionEntry<C>>;

	export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(
		collection: C,
		filter?: (entry: CollectionEntry<C>) => entry is E,
	): Promise<E[]>;
	export function getCollection<C extends keyof AnyEntryMap>(
		collection: C,
		filter?: (entry: CollectionEntry<C>) => unknown,
	): Promise<CollectionEntry<C>[]>;

	export function getEntry<
		C extends keyof ContentEntryMap,
		E extends ValidContentEntrySlug<C> | (string & {}),
	>(
		entry: ReferenceContentEntry<C, E>,
	): E extends ValidContentEntrySlug<C>
		? Promise<CollectionEntry<C>>
		: Promise<CollectionEntry<C> | undefined>;
	export function getEntry<
		C extends keyof DataEntryMap,
		E extends keyof DataEntryMap[C] | (string & {}),
	>(
		entry: ReferenceDataEntry<C, E>,
	): E extends keyof DataEntryMap[C]
		? Promise<DataEntryMap[C][E]>
		: Promise<CollectionEntry<C> | undefined>;
	export function getEntry<
		C extends keyof ContentEntryMap,
		E extends ValidContentEntrySlug<C> | (string & {}),
	>(
		collection: C,
		slug: E,
	): E extends ValidContentEntrySlug<C>
		? Promise<CollectionEntry<C>>
		: Promise<CollectionEntry<C> | undefined>;
	export function getEntry<
		C extends keyof DataEntryMap,
		E extends keyof DataEntryMap[C] | (string & {}),
	>(
		collection: C,
		id: E,
	): E extends keyof DataEntryMap[C]
		? string extends keyof DataEntryMap[C]
			? Promise<DataEntryMap[C][E]> | undefined
			: Promise<DataEntryMap[C][E]>
		: Promise<CollectionEntry<C> | undefined>;

	/** Resolve an array of entry references from the same collection */
	export function getEntries<C extends keyof ContentEntryMap>(
		entries: ReferenceContentEntry<C, ValidContentEntrySlug<C>>[],
	): Promise<CollectionEntry<C>[]>;
	export function getEntries<C extends keyof DataEntryMap>(
		entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
	): Promise<CollectionEntry<C>[]>;

	export function render<C extends keyof AnyEntryMap>(
		entry: AnyEntryMap[C][string],
	): Promise<RenderResult>;

	export function reference<C extends keyof AnyEntryMap>(
		collection: C,
	): import('astro/zod').ZodEffects<
		import('astro/zod').ZodString,
		C extends keyof ContentEntryMap
			? ReferenceContentEntry<C, ValidContentEntrySlug<C>>
			: ReferenceDataEntry<C, keyof DataEntryMap[C]>
	>;
	// Allow generic `string` to avoid excessive type errors in the config
	// if `dev` is not running to update as you edit.
	// Invalid collection names will be caught at build time.
	export function reference<C extends string>(
		collection: C,
	): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;

	type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
	type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<
		ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
	>;

	type ContentEntryMap = {
		
	};

	type DataEntryMap = {
		"docs": Record<string, {
  id: string;
  render(): Render[".md"];
  slug: string;
  body: string;
  collection: "docs";
  data: InferEntrySchema<"docs">;
  rendered?: RenderedContent;
  filePath?: string;
}>;

	};

	type AnyEntryMap = ContentEntryMap & DataEntryMap;

	export type ContentConfig = typeof import("../src/content/config.js");
}

```

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

```go
package k8s

import (
	"context"
	"fmt"
	"path/filepath"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/discovery"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/config"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
)

// Client wraps the Kubernetes clientset and provides additional functionality
type Client struct {
	clientset       *kubernetes.Clientset
	dynamicClient   dynamic.Interface
	discoveryClient *discovery.DiscoveryClient
	restConfig      *rest.Config
	defaultNS       string
	logger          *logging.Logger
	ResourceMapper  *ResourceMapper
}

// NewClient creates a new Kubernetes client based on the provided configuration
func NewClient(cfg config.KubernetesConfig, logger *logging.Logger) (*Client, error) {
	if logger == nil {
		logger = logging.NewLogger().Named("k8s")
	}

	var restConfig *rest.Config
	var err error

	logger.Debug("Initializing Kubernetes client",
		"inCluster", cfg.InCluster,
		"kubeconfig", cfg.KubeConfig,
		"defaultNamespace", cfg.DefaultNamespace)

	if cfg.InCluster {
		// Use in-cluster config when deployed inside Kubernetes
		restConfig, err = rest.InClusterConfig()
		if err != nil {
			return nil, fmt.Errorf("failed to create in-cluster config: %w", err)
		}
		logger.Debug("Using in-cluster configuration")
	} else {
		// Use kubeconfig file
		kubeconfigPath := cfg.KubeConfig
		if kubeconfigPath == "" {
			// Try to use default location if not specified
			if home := homedir.HomeDir(); home != "" {
				kubeconfigPath = filepath.Join(home, ".kube", "config")
				logger.Debug("Using default kubeconfig path", "path", kubeconfigPath)
			} else {
				return nil, fmt.Errorf("kubeconfig not specified and home directory not found")
			}
		}

		// Build config from kubeconfig file
		configLoadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath}
		configOverrides := &clientcmd.ConfigOverrides{}

		if cfg.DefaultContext != "" {
			configOverrides.CurrentContext = cfg.DefaultContext
			logger.Debug("Using specified context", "context", cfg.DefaultContext)
		}

		kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
			configLoadingRules,
			configOverrides,
		)

		restConfig, err = kubeConfig.ClientConfig()
		if err != nil {
			return nil, fmt.Errorf("failed to build kubeconfig: %w", err)
		}
	}

	// Increase QPS and Burst for better performance in busy environments
	restConfig.QPS = 100
	restConfig.Burst = 100

	// Create clientset
	clientset, err := kubernetes.NewForConfig(restConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to create Kubernetes clientset: %w", err)
	}

	// Create dynamic client
	dynamicClient, err := dynamic.NewForConfig(restConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to create dynamic client: %w", err)
	}

	// Create discovery client
	discoveryClient, err := discovery.NewDiscoveryClientForConfig(restConfig)
	if err != nil {
		return nil, fmt.Errorf("failed to create discovery client: %w", err)
	}

	defaultNamespace := cfg.DefaultNamespace
	if defaultNamespace == "" {
		defaultNamespace = "default"
	}

	logger.Info("Kubernetes client initialized",
		"defaultNamespace", defaultNamespace)

	// Create the client instance
	client := &Client{
		clientset:       clientset,
		dynamicClient:   dynamicClient,
		discoveryClient: discoveryClient,
		restConfig:      restConfig,
		defaultNS:       defaultNamespace,
		logger:          logger,
	}

	// Initialize the ResourceMapper (ensure NewResourceMapper is defined in your package)
	client.ResourceMapper = NewResourceMapper(client)

	return client, nil
}

// CheckConnectivity verifies connectivity to the Kubernetes API
func (c *Client) CheckConnectivity(ctx context.Context) error {
	c.logger.Debug("Checking Kubernetes connectivity")

	// Try to get server version as a basic connectivity test
	_, err := c.clientset.Discovery().ServerVersion()
	if err != nil {
		c.logger.Warn("Kubernetes connectivity check failed", "error", err)
		return fmt.Errorf("failed to connect to Kubernetes API: %w", err)
	}

	c.logger.Debug("Kubernetes connectivity check successful")
	return nil
}

// GetNamespaces returns a list of all namespaces in the cluster
func (c *Client) GetNamespaces(ctx context.Context) ([]string, error) {
	c.logger.Debug("Getting namespaces")

	namespaceList, err := c.clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to list namespaces: %w", err)
	}

	var namespaces []string
	for _, ns := range namespaceList.Items {
		namespaces = append(namespaces, ns.Name)
	}

	c.logger.Debug("Got namespaces", "count", len(namespaces))
	return namespaces, nil
}

// GetDefaultNamespace returns the default namespace for operations
func (c *Client) GetDefaultNamespace() string {
	return c.defaultNS
}

// GetRestConfig returns the Kubernetes REST configuration
func (c *Client) GetRestConfig() *rest.Config {
	return c.restConfig
}

// GetClientset returns the Kubernetes clientset
func (c *Client) GetClientset() *kubernetes.Clientset {
	return c.clientset
}

// GetDynamicClient returns the dynamic client
func (c *Client) GetDynamicClient() dynamic.Interface {
	return c.dynamicClient
}

// GetDiscoveryClient returns the discovery client
func (c *Client) GetDiscoveryClient() *discovery.DiscoveryClient {
	return c.discoveryClient
}

// GetNamespaceTopology returns the topology for a specific namespace
func (c *Client) GetNamespaceTopology(ctx context.Context, namespace string) (*NamespaceTopology, error) {
	return c.ResourceMapper.GetNamespaceTopology(ctx, namespace)
}

```

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

```go
package api

import (
	"context"
	"net/http"
	"strings"
	"time"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/argocd"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/correlator"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/gitlab"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/k8s"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/mcp"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/config"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
	"github.com/gorilla/mux"
)

// Server represents the API server
type Server struct {
	router                 *mux.Router
	server                 *http.Server
	k8sClient              *k8s.Client
	argoClient             *argocd.Client
	gitlabClient           *gitlab.Client
	mcpHandler             *mcp.ProtocolHandler
	troubleshootCorrelator *correlator.TroubleshootCorrelator
	resourceMapper         *k8s.ResourceMapper
	config                 config.ServerConfig
	logger                 *logging.Logger
}

// NewServer creates a new API server
func NewServer(
	cfg config.ServerConfig,
	k8sClient *k8s.Client,
	argoClient *argocd.Client,
	gitlabClient *gitlab.Client,
	mcpHandler *mcp.ProtocolHandler,
	troubleshootCorrelator *correlator.TroubleshootCorrelator,
	logger *logging.Logger,
) *Server {
	if logger == nil {
		logger = logging.NewLogger().Named("api")
	}

	server := &Server{
		router:                 mux.NewRouter(),
		k8sClient:              k8sClient,
		argoClient:             argoClient,
		gitlabClient:           gitlabClient,
		mcpHandler:             mcpHandler,
		troubleshootCorrelator: troubleshootCorrelator,
		config:                 cfg,
		logger:                 logger,
	}

	// Initialize resource mapper
	server.resourceMapper = server.k8sClient.ResourceMapper

	// Set up routes
	server.setupRoutes()
	server.setupNamespaceRoutes()

	return server
}

// Start starts the HTTP server
func (s *Server) Start(ctx context.Context) error {
	s.server = &http.Server{
		Addr:         s.config.Address,
		Handler:      s.loggingMiddleware(s.router),
		ReadTimeout:  time.Duration(s.config.ReadTimeout) * time.Second,
		WriteTimeout: time.Duration(s.config.WriteTimeout) * time.Second,
	}

	// Channel for server errors
	errCh := make(chan error, 1)

	// Start server in a goroutine
	go func() {
		s.logger.Info("Starting HTTP server", "address", s.config.Address)
		if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			errCh <- err
		}
	}()

	// Wait for context cancellation or server error
	select {
	case <-ctx.Done():
		s.logger.Info("Context cancelled, shutting down server")
		return s.Shutdown(context.Background())
	case err := <-errCh:
		return err
	}
}

// Shutdown gracefully shuts down the server
func (s *Server) Shutdown(ctx context.Context) error {
	s.logger.Info("Shutting down HTTP server")
	return s.server.Shutdown(ctx)
}

// Middleware functions

// loggingMiddleware logs information about each request
func (s *Server) loggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		// Create a response writer that captures status code
		rw := &responseWriter{w, http.StatusOK}

		// Call the next handler
		next.ServeHTTP(rw, r)

		// Log the request
		s.logger.Info("HTTP request",
			"method", r.Method,
			"path", r.URL.Path,
			"status", rw.statusCode,
			"duration", time.Since(start),
			"remote_addr", r.RemoteAddr,
			"user_agent", r.UserAgent(),
		)
	})
}

// authMiddleware checks for valid authentication
func (s *Server) authMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Get API key from header
		apiKey := r.Header.Get("X-API-Key")

		// Check for bearer token if API key is not provided
		if apiKey == "" {
			authHeader := r.Header.Get("Authorization")
			if authHeader == "" {
				s.respondWithError(w, http.StatusUnauthorized, "Authentication required", nil)
				return
			}

			// Extract token
			parts := strings.Split(authHeader, " ")
			if len(parts) != 2 || parts[0] != "Bearer" {
				s.respondWithError(w, http.StatusUnauthorized, "Invalid authorization format", nil)
				return
			}

			apiKey = parts[1]
		}

		// Validate the API key against the configured key
		if apiKey != s.config.Auth.APIKey {
			s.respondWithError(w, http.StatusUnauthorized, "Invalid API key", nil)
			return
		}

		// Call the next handler
		next.ServeHTTP(w, r)
	})
}

// Custom response writer to capture status code
type responseWriter struct {
	http.ResponseWriter
	statusCode int
}

// WriteHeader captures the status code
func (rw *responseWriter) WriteHeader(code int) {
	rw.statusCode = code
	rw.ResponseWriter.WriteHeader(code)
}

// Initialize the resourceMapper in NewServer
func (s *Server) initResourceMapper() {
	if s.k8sClient != nil {
		s.resourceMapper = k8s.NewResourceMapper(s.k8sClient)
		s.logger.Info("Resource mapper initialized")
	} else {
		s.logger.Warn("Cannot initialize resource mapper - K8s client is nil")
	}
}

func (s *Server) corsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Set CORS headers
		w.Header().Set("Access-Control-Allow-Origin", "*") // Allow all origins in development
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")

		// If this is a preflight request, respond with 200 OK
		if r.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
			return
		}

		// Call the next handler
		next.ServeHTTP(w, r)
	})
}

```

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

```go
package gitlab

import (
    "io"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
)

// ListProjects returns a list of GitLab projects
func (c *Client) ListProjects(ctx context.Context) ([]models.GitLabProject, error) {
	c.logger.Debug("Listing projects")
	
	// Create endpoint with query parameters
	endpoint := "projects"
	
	u, err := url.Parse(endpoint)
	if err != nil {
		return nil, fmt.Errorf("invalid endpoint: %w", err)
	}
	
	q := u.Query()
	q.Set("membership", "true")
	q.Set("order_by", "updated_at")
	q.Set("sort", "desc")
	q.Set("per_page", "100")
	u.RawQuery = q.Encode()
	
	resp, err := c.doRequest(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var projects []models.GitLabProject
	if err := json.NewDecoder(resp.Body).Decode(&projects); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	c.logger.Debug("Listed projects", "count", len(projects))
	return projects, nil
}

// GetProject returns details about a specific GitLab project
func (c *Client) GetProject(ctx context.Context, projectID string) (*models.GitLabProject, error) {
	c.logger.Debug("Getting project", "projectID", projectID)
	
	endpoint := fmt.Sprintf("projects/%s", url.PathEscape(projectID))
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var project models.GitLabProject
	if err := json.NewDecoder(resp.Body).Decode(&project); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &project, nil
}

// GetProjectByPath returns a project by its path (namespace/project-name)
func (c *Client) GetProjectByPath(ctx context.Context, path string) (*models.GitLabProject, error) {
	c.logger.Debug("Getting project by path", "path", path)
	
	// GitLab API requires path to be URL encoded
	encodedPath := url.QueryEscape(path)
	endpoint := fmt.Sprintf("projects/%s", encodedPath)
	
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var project models.GitLabProject
	if err := json.NewDecoder(resp.Body).Decode(&project); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &project, nil
}

// GetCommit returns details about a specific commit
func (c *Client) GetCommit(ctx context.Context, projectID, sha string) (*models.GitLabCommit, error) {
	c.logger.Debug("Getting commit", "projectID", projectID, "sha", sha)
	
	endpoint := fmt.Sprintf("projects/%s/repository/commits/%s", url.PathEscape(projectID), url.PathEscape(sha))
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var commit models.GitLabCommit
	if err := json.NewDecoder(resp.Body).Decode(&commit); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &commit, nil
}

// GetCommitDiff returns the changes in a specific commit
func (c *Client) GetCommitDiff(ctx context.Context, projectID, sha string) ([]models.GitLabDiff, error) {
	c.logger.Debug("Getting commit diff", "projectID", projectID, "sha", sha)
	
	endpoint := fmt.Sprintf("projects/%s/repository/commits/%s/diff", url.PathEscape(projectID), url.PathEscape(sha))
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var diffs []models.GitLabDiff
	if err := json.NewDecoder(resp.Body).Decode(&diffs); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	c.logger.Debug("Got commit diff", "projectID", projectID, "sha", sha, "count", len(diffs))
	return diffs, nil
}

// GetFileContent returns the content of a file at a specific commit
func (c *Client) GetFileContent(ctx context.Context, projectID, filePath, ref string) (string, error) {
	c.logger.Debug("Getting file content", 
		"projectID", projectID, 
		"filePath", filePath, 
		"ref", ref)
	
	encodedFilePath := url.PathEscape(filePath)
	endpoint := fmt.Sprintf("projects/%s/repository/files/%s/raw", 
		url.PathEscape(projectID),
		encodedFilePath)
	
	// Add ref parameter if provided
	if ref != "" {
		u, err := url.Parse(endpoint)
		if err != nil {
			return "", fmt.Errorf("invalid endpoint: %w", err)
		}
		
		q := u.Query()
		q.Set("ref", ref)
		u.RawQuery = q.Encode()
		endpoint = u.String()
	}
	
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	content, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("failed to read file content: %w", err)
	}

	return string(content), nil
}

// FindRecentChanges finds recent changes (commits) for a project
func (c *Client) FindRecentChanges(ctx context.Context, projectID string, since time.Time) ([]models.GitLabCommit, error) {
	c.logger.Debug("Finding recent changes", 
		"projectID", projectID, 
		"since", since.Format(time.RFC3339))
	
	// Format time as ISO 8601
	sinceStr := since.Format(time.RFC3339)
	
	// Create endpoint with query parameters
	endpoint := fmt.Sprintf("projects/%s/repository/commits", url.PathEscape(projectID))
	
	u, err := url.Parse(endpoint)
	if err != nil {
		return nil, fmt.Errorf("invalid endpoint: %w", err)
	}
	
	q := u.Query()
	q.Set("since", sinceStr)
	q.Set("per_page", "20")
	u.RawQuery = q.Encode()
	
	resp, err := c.doRequest(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var commits []models.GitLabCommit
	if err := json.NewDecoder(resp.Body).Decode(&commits); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	c.logger.Debug("Found recent changes", 
		"projectID", projectID, 
		"count", len(commits))
	return commits, nil
}
```

--------------------------------------------------------------------------------
/docs/src/components/Footer.astro:
--------------------------------------------------------------------------------

```
---
const currentYear = new Date().getFullYear();
---

<footer class="bg-slate-100 dark:bg-slate-900 border-t border-slate-200 dark:border-slate-800 py-12">
  <div class="container mx-auto px-4">
    <div class="grid grid-cols-1 md:grid-cols-4 gap-8">
      <div>
        <h3 class="font-semibold text-lg mb-4">Kubernetes Claude MCP</h3>
        <p class="text-slate-600 dark:text-slate-400">
          An advanced Model Context Protocol server for Kubernetes, integrating Claude AI with GitOps workflows.
        </p>
      </div>
      
      <div>
        <h3 class="font-semibold text-lg mb-4">Documentation</h3>
        <ul class="space-y-2">
          <li><a href="/docs/introduction" class="text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400">Introduction</a></li>
          <li><a href="/docs/quick-start" class="text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400">Quick Start</a></li>
          <li><a href="/docs/installation" class="text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400">Installation</a></li>
          <li><a href="/docs/api-overview" class="text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400">API Reference</a></li>
        </ul>
      </div>
      
      <div>
        <h3 class="font-semibold text-lg mb-4">Community</h3>
        <ul class="space-y-2">
          <li><a href="https://github.com/blankcut/kubernetes-mcp-server" target="_blank" rel="noopener noreferrer" class="text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400">GitHub</a></li>
          <li><a href="https://github.com/blankcut/kubernetes-mcp-server/issues" target="_blank" rel="noopener noreferrer" class="text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400">Issues</a></li>
          <li><a href="https://github.com/blankcut/kubernetes-mcp-server/discussions" target="_blank" rel="noopener noreferrer" class="text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400">Discussions</a></li>
          <li><a href="https://github.com/blankcut/kubernetes-mcp-server/releases" target="_blank" rel="noopener noreferrer" class="text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400">Releases</a></li>
        </ul>
      </div>
      
      <div>
        <h3 class="font-semibold text-lg mb-4">Legal</h3>
        <ul class="space-y-2">
          <li><a href="/license" class="text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400">License</a></li>
          <li><a href="/privacy" class="text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400">Privacy Policy</a></li>
          <li><a href="/terms" class="text-slate-600 hover:text-primary-600 dark:text-slate-400 dark:hover:text-primary-400">Terms of Service</a></li>
        </ul>
      </div>
    </div>
    
    <div class="mt-12 pt-8 border-t border-slate-200 dark:border-slate-800 flex flex-col sm:flex-row justify-between items-center">
      <p class="text-slate-600 dark:text-slate-400 text-sm mb-4 sm:mb-0">
        &copy; {currentYear} Blank Cut Inc. All rights reserved.
      </p>
      <div class="flex space-x-4">
        <a href="https://github.com/blankcut" target="_blank" rel="noopener noreferrer" class="text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white">
          <span class="sr-only">GitHub</span>
          <svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
            <path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"></path>
          </svg>
        </a>
        <a href="https://twitter.com/blankcut" target="_blank" rel="noopener noreferrer" class="text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white">
          <span class="sr-only">Twitter</span>
          <svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
            <path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84"></path>
          </svg>
        </a>
        <a href="https://www.linkedin.com/company/blankcut" target="_blank" rel="noopener noreferrer" class="text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white">
          <span class="sr-only">LinkedIn</span>
          <svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
            <path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"></path>
          </svg>
        </a>
      </div>
    </div>
  </div>
</footer>
```

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

```go
package models

// GitLabProject represents a GitLab project
type GitLabProject struct {
	ID                int    `json:"id"`
	Name              string `json:"name"`
	Path              string `json:"path"`
	PathWithNamespace string `json:"path_with_namespace"`
	WebURL            string `json:"web_url"`
	DefaultBranch     string `json:"default_branch"`
	Visibility        string `json:"visibility"`
}

// GitLabPipeline represents a GitLab CI/CD pipeline
type GitLabPipeline struct {
	ID        int         `json:"id"`
	Status    string      `json:"status"`
	Ref       string      `json:"ref"`
	SHA       string      `json:"sha"`
	WebURL    string      `json:"web_url"`
	CreatedAt interface{} `json:"created_at"`
	UpdatedAt interface{} `json:"updated_at"`
}

// GitLabJob represents a job in a GitLab CI/CD pipeline
type GitLabJob struct {
	ID         int    `json:"id"`
	Status     string `json:"status"`
	Stage      string `json:"stage"`
	Name       string `json:"name"`
	Ref        string `json:"ref"`
	CreatedAt  int64  `json:"created_at"`
	StartedAt  int64  `json:"started_at"`
	FinishedAt int64  `json:"finished_at"`
	Pipeline   struct {
		ID int `json:"id"`
	} `json:"pipeline"`
}

// GitLabCommit represents a Git commit in GitLab
type GitLabCommit struct {
	ID             string      `json:"id"`
	ShortID        string      `json:"short_id"`
	Title          string      `json:"title"`
	Message        string      `json:"message"`
	AuthorName     string      `json:"author_name"`
	AuthorEmail    string      `json:"author_email"`
	CommitterName  string      `json:"committer_name"`
	CommitterEmail string      `json:"committer_email"`
	CreatedAt      interface{} `json:"created_at"`
	ParentIDs      []string    `json:"parent_ids"`
	WebURL         string      `json:"web_url"`
}

// GitLabDiff represents a file diff in a commit
type GitLabDiff struct {
	OldPath     string `json:"old_path"`
	NewPath     string `json:"new_path"`
	Diff        string `json:"diff"`
	NewFile     bool   `json:"new_file"`
	RenamedFile bool   `json:"renamed_file"`
	DeletedFile bool   `json:"deleted_file"`
}

// GitLabDeployment represents a deployment in GitLab
type GitLabDeployment struct {
	ID          int         `json:"id"`
	Status      string      `json:"status"`
	CreatedAt   interface{} `json:"created_at"`
	UpdatedAt   interface{} `json:"updated_at"`
	Environment struct {
		ID    int    `json:"id"`
		Name  string `json:"name"`
		Slug  string `json:"slug"`
		State string `json:"state"`
	} `json:"environment"`
	Deployable struct {
		ID       int    `json:"id"`
		Status   string `json:"status"`
		Stage    string `json:"stage"`
		Name     string `json:"name"`
		Ref      string `json:"ref"`
		Tag      bool   `json:"tag"`
		Pipeline struct {
			ID     int    `json:"id"`
			Status string `json:"status"`
		} `json:"pipeline"`
	} `json:"deployable"`
	Commit GitLabCommit `json:"commit"`
}

// GitLabRelease represents a release in GitLab
type GitLabRelease struct {
	TagName     string `json:"tag_name"`
	Description string `json:"description"`
	CreatedAt   int64  `json:"created_at"`
	Assets      struct {
		Links []struct {
			Name string `json:"name"`
			URL  string `json:"url"`
		} `json:"links"`
	} `json:"assets"`
}

// GitLabMergeRequest represents a merge request in GitLab
type GitLabMergeRequest struct {
	ID          int    `json:"id"`
	IID         int    `json:"iid"`
	ProjectID   int    `json:"project_id"`
	Title       string `json:"title"`
	Description string `json:"description"`
	State       string `json:"state"`
	MergedBy    *struct {
		ID       int    `json:"id"`
		Username string `json:"username"`
		Name     string `json:"name"`
	} `json:"merged_by,omitempty"`
	MergedAt     interface{} `json:"merged_at"`
	CreatedAt    interface{} `json:"created_at"`
	UpdatedAt    interface{} `json:"updated_at"`
	TargetBranch string      `json:"target_branch"`
	SourceBranch string      `json:"source_branch"`
	Author       struct {
		ID       int    `json:"id"`
		Username string `json:"username"`
		Name     string `json:"name"`
	} `json:"author"`
	Assignees []struct {
		ID       int    `json:"id"`
		Username string `json:"username"`
		Name     string `json:"name"`
	} `json:"assignees"`
	SourceProjectID int    `json:"source_project_id"`
	TargetProjectID int    `json:"target_project_id"`
	WebURL          string `json:"web_url"`
	MergeStatus     string `json:"merge_status"`
	Changes         []struct {
		OldPath     string `json:"old_path"`
		NewPath     string `json:"new_path"`
		Diff        string `json:"diff"`
		NewFile     bool   `json:"new_file"`
		RenamedFile bool   `json:"renamed_file"`
		DeletedFile bool   `json:"deleted_file"`
	} `json:"changes,omitempty"`
	DiffRefs struct {
		BaseSHA  string `json:"base_sha"`
		HeadSHA  string `json:"head_sha"`
		StartSHA string `json:"start_sha"`
	} `json:"diff_refs"`
	UserNotesCount      int              `json:"user_notes_count"`
	HasConflicts        bool             `json:"has_conflicts"`
	Pipelines           []GitLabPipeline `json:"pipelines,omitempty"`
	MergeRequestContext struct {
		CommitMessages     []string `json:"commit_messages,omitempty"`
		AffectedFiles      []string `json:"affected_files,omitempty"`
		HelmChartAffected  bool     `json:"helm_chart_affected,omitempty"`
		KubernetesManifest bool     `json:"kubernetes_manifests_affected,omitempty"`
	} `json:"merge_request_context,omitempty"`
}

// GitLabMergeRequestComment represents a comment on a GitLab merge request
type GitLabMergeRequestComment struct {
	ID           int    `json:"id"`
	Body         string `json:"body"`
	CreatedAt    string `json:"created_at"`
	UpdatedAt    string `json:"updated_at"`
	System       bool   `json:"system"`
	NoteableID   int    `json:"noteable_id"`
	NoteableType string `json:"noteable_type"`
	Author       struct {
		ID       int    `json:"id"`
		Username string `json:"username"`
		Name     string `json:"name"`
	} `json:"author"`
}

// GitLabMergeRequestApproval represents approval information for a merge request
type GitLabMergeRequestApproval struct {
	ID               int  `json:"id"`
	ProjectID        int  `json:"project_id"`
	ApprovalRequired bool `json:"approval_required"`
	ApprovedBy       []struct {
		User struct {
			ID       int    `json:"id"`
			Username string `json:"username"`
			Name     string `json:"name"`
		} `json:"user"`
	} `json:"approved_by"`
	ApprovalsRequired int `json:"approvals_required"`
	ApprovalsLeft     int `json:"approvals_left"`
}

```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/helm/parser.go:
--------------------------------------------------------------------------------

```go
// internal/helm/parser.go

package helm

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
)

// Parser handles Helm chart parsing and analysis
type Parser struct {
	workDir string
	logger  *logging.Logger
}

// NewParser creates a new Helm chart parser
func NewParser(logger *logging.Logger) *Parser {
	if logger == nil {
		logger = logging.NewLogger().Named("helm")
	}

	// Create a temporary working directory
	workDir, err := os.MkdirTemp("", "helm-parser-*")
	if err != nil {
		logger.Error("Failed to create working directory", "error", err)
		return nil
	}

	return &Parser{
		workDir: workDir,
		logger:  logger,
	}
}

// ParseChart renders a Helm chart and returns the resulting Kubernetes manifests
func (p *Parser) ParseChart(ctx context.Context, chartPath string, valuesFiles []string, values map[string]interface{}) ([]string, error) {
	p.logger.Debug("Parsing Helm chart", "chartPath", chartPath, "valuesFiles", valuesFiles)

	// Check if helm command is available
	if _, err := exec.LookPath("helm"); err != nil {
		return nil, fmt.Errorf("helm command not found in PATH: %w", err)
	}

	// Prepare helm template command
	args := []string{"template", "release", chartPath}

	// Add values files
	for _, valuesFile := range valuesFiles {
		args = append(args, "-f", valuesFile)
	}

	// Add --set arguments for values
	for k, v := range values {
		args = append(args, "--set", fmt.Sprintf("%s=%v", k, v))
	}

	// Execute helm template command
	cmd := exec.CommandContext(ctx, "helm", args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	p.logger.Debug("Executing helm template command", "args", args)
	err := cmd.Run()
	if err != nil {
		return nil, fmt.Errorf("failed to execute helm template: %s, error: %w", stderr.String(), err)
	}

	// Parse the rendered templates
	manifests := p.splitYAMLDocuments(stdout.String())
	p.logger.Debug("Parsed Helm chart", "manifestCount", len(manifests))

	return manifests, nil
}

// WriteChartFiles writes chart files to the working directory for processing
func (p *Parser) WriteChartFiles(files map[string]string) (string, error) {
	chartDir := filepath.Join(p.workDir, "chart")

	// Create chart directory if not exists
	if err := os.MkdirAll(chartDir, 0755); err != nil {
		return "", fmt.Errorf("failed to create chart directory: %w", err)
	}

	// Write files
	for path, content := range files {
		fullPath := filepath.Join(chartDir, path)
		dirPath := filepath.Dir(fullPath)

		// Create directories
		if err := os.MkdirAll(dirPath, 0755); err != nil {
			return "", fmt.Errorf("failed to create directory %s: %w", dirPath, err)
		}

		// Write file
		if err := os.WriteFile(fullPath, []byte(content), 0644); err != nil {
			return "", fmt.Errorf("failed to write file %s: %w", fullPath, err)
		}
	}

	return chartDir, nil
}

// WriteValuesFile writes a values file to the working directory
func (p *Parser) WriteValuesFile(content string) (string, error) {
	valuesFile := filepath.Join(p.workDir, "values.yaml")

	if err := os.WriteFile(valuesFile, []byte(content), 0644); err != nil {
		return "", fmt.Errorf("failed to write values file: %w", err)
	}

	return valuesFile, nil
}

// ParseYAML parses a YAML file to extract Kubernetes resources
func (p *Parser) ParseYAML(content string) ([]map[string]interface{}, error) {
	// Split YAML documents
	documents := p.splitYAMLDocuments(content)

	var resources []map[string]interface{}

	for _, doc := range documents {
		// Parse each document as YAML
		var resource map[string]interface{}

		// *** Add this line (or similar depending on your library) ***
		err := yaml.Unmarshal([]byte(doc), &resource) // Use your chosen library's unmarshal function
		if err != nil {
			// Handle the error appropriately, maybe log it and continue
			p.logger.Warn("Failed to unmarshal YAML document", "error", err)
			continue
		}

		// Add to resources if it's a valid Kubernetes resource (and not empty after parsing)
		if resource != nil {
			resources = append(resources, resource)
		}
	}

	return resources, nil
}

// splitYAMLDocuments splits multi-document YAML into individual documents
func (p *Parser) splitYAMLDocuments(content string) []string {
	// Simple implementation - in a real system, use a proper YAML parser
	var documents []string

	// Split on document separator
	parts := strings.Split(content, "---")

	for _, part := range parts {
		// Trim whitespace
		trimmed := strings.TrimSpace(part)
		if trimmed != "" {
			documents = append(documents, trimmed)
		}
	}

	return documents
}

// Cleanup removes temporary files
func (p *Parser) Cleanup() {
	if p.workDir != "" {
		p.logger.Debug("Cleaning up working directory", "path", p.workDir)
		os.RemoveAll(p.workDir)
	}
}

// DiffChartVersions compares two versions of a chart and returns resources that would be affected
func (p *Parser) DiffChartVersions(ctx context.Context, chartPath1, chartPath2 string, valuesFiles []string) ([]string, error) {
	// Render both chart versions
	manifests1, err := p.ParseChart(ctx, chartPath1, valuesFiles, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to parse first chart version: %w", err)
	}

	manifests2, err := p.ParseChart(ctx, chartPath2, valuesFiles, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to parse second chart version: %w", err)
	}

	// Compare manifests to find differences
	diff := p.compareManifests(manifests1, manifests2)

	return diff, nil
}

// compareManifests compares two sets of manifests and returns the names of resources that differ
func (p *Parser) compareManifests(manifests1, manifests2 []string) []string {
	// This is a simplified implementation
	// In a real system, you would parse the YAML and compare by resource identifiers

	var changedResources []string

	// For now, we just assume all manifests might be affected
	// In a real implementation, you'd compare name/kind/namespace

	for _, manifest := range manifests2 {
		// Extract resource name and kind
		if strings.Contains(manifest, "kind:") && strings.Contains(manifest, "name:") {
			// Very simplistic parsing - would need proper YAML parsing in real code
			lines := strings.Split(manifest, "\n")
			var kind, name string

			for _, line := range lines {
				line = strings.TrimSpace(line)
				if strings.HasPrefix(line, "kind:") {
					kind = strings.TrimSpace(strings.TrimPrefix(line, "kind:"))
				} else if strings.HasPrefix(line, "name:") {
					name = strings.TrimSpace(strings.TrimPrefix(line, "name:"))
				}

				if kind != "" && name != "" {
					changedResources = append(changedResources, fmt.Sprintf("%s/%s", kind, name))
					break
				}
			}
		}
	}

	return changedResources
}

```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/k8s/events.go:
--------------------------------------------------------------------------------

```go
package k8s

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
)

// GetResourceEvents returns events related to a specific resource
func (c *Client) GetResourceEvents(ctx context.Context, namespace, kind, name string) ([]models.K8sEvent, error) {
	c.logger.Debug("Getting events for resource", "namespace", namespace, "kind", kind, "name", name)

	// Build field selector
	var fieldSelector fields.Selector
	if namespace != "" {
		// For namespaced resources
		fieldSelector = fields.AndSelectors(
			fields.OneTermEqualSelector("involvedObject.name", name),
			fields.OneTermEqualSelector("involvedObject.kind", kind),
			fields.OneTermEqualSelector("involvedObject.namespace", namespace),
		)
	} else {
		// For cluster-scoped resources (no namespace)
		fieldSelector = fields.AndSelectors(
			fields.OneTermEqualSelector("involvedObject.name", name),
			fields.OneTermEqualSelector("involvedObject.kind", kind),
		)
	}

	// Get events
	eventList, err := c.clientset.CoreV1().Events(namespace).List(ctx, metav1.ListOptions{
		FieldSelector: fieldSelector.String(),
	})
	if err != nil {
		return nil, fmt.Errorf("failed to list events: %w", err)
	}

	// Convert to our model
	var events []models.K8sEvent
	for _, event := range eventList.Items {
		e := models.K8sEvent{
			Reason:    event.Reason,
			Message:   event.Message,
			Type:      event.Type,
			Count:     int(event.Count),
			FirstTime: event.FirstTimestamp.Time,
			LastTime:  event.LastTimestamp.Time,
			Object: struct {
				Kind      string `json:"kind"`
				Name      string `json:"name"`
				Namespace string `json:"namespace"`
			}{
				Kind:      event.InvolvedObject.Kind,
				Name:      event.InvolvedObject.Name,
				Namespace: event.InvolvedObject.Namespace,
			},
		}
		events = append(events, e)
	}

	// Sort events by last time, most recent first
	sort.Slice(events, func(i, j int) bool {
		return events[i].LastTime.After(events[j].LastTime)
	})

	c.logger.Debug("Got events for resource",
		"namespace", namespace,
		"kind", kind,
		"name", name,
		"count", len(events))
	return events, nil
}

// GetNamespaceEvents returns all events in a namespace
func (c *Client) GetNamespaceEvents(ctx context.Context, namespace string) ([]models.K8sEvent, error) {
	c.logger.Debug("Getting events for namespace", "namespace", namespace)

	// Get events
	eventList, err := c.clientset.CoreV1().Events(namespace).List(ctx, metav1.ListOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to list events: %w", err)
	}

	// Convert to our model
	var events []models.K8sEvent
	for _, event := range eventList.Items {
		e := models.K8sEvent{
			Reason:    event.Reason,
			Message:   event.Message,
			Type:      event.Type,
			Count:     int(event.Count),
			FirstTime: event.FirstTimestamp.Time,
			LastTime:  event.LastTimestamp.Time,
			Object: struct {
				Kind      string `json:"kind"`
				Name      string `json:"name"`
				Namespace string `json:"namespace"`
			}{
				Kind:      event.InvolvedObject.Kind,
				Name:      event.InvolvedObject.Name,
				Namespace: event.InvolvedObject.Namespace,
			},
		}
		events = append(events, e)
	}

	// Sort events by last time, most recent first
	sort.Slice(events, func(i, j int) bool {
		return events[i].LastTime.After(events[j].LastTime)
	})

	c.logger.Debug("Got events for namespace", "namespace", namespace, "count", len(events))
	return events, nil
}

// GetRecentWarningEvents returns recent warning events across all namespaces
func (c *Client) GetRecentWarningEvents(ctx context.Context, timeWindow time.Duration) ([]models.K8sEvent, error) {
	c.logger.Debug("Getting recent warning events", "timeWindow", timeWindow)

	// Calculate the cutoff time
	cutoffTime := time.Now().Add(-timeWindow)

	// Get events from all namespaces
	eventList, err := c.clientset.CoreV1().Events("").List(ctx, metav1.ListOptions{
		FieldSelector: fields.OneTermEqualSelector("type", "Warning").String(),
	})
	if err != nil {
		return nil, fmt.Errorf("failed to list warning events: %w", err)
	}

	// Filter and convert to our model
	var events []models.K8sEvent
	for _, event := range eventList.Items {
		// Skip events older than the cutoff time
		if event.LastTimestamp.Time.Before(cutoffTime) {
			continue
		}

		e := models.K8sEvent{
			Reason:    event.Reason,
			Message:   event.Message,
			Type:      event.Type,
			Count:     int(event.Count),
			FirstTime: event.FirstTimestamp.Time,
			LastTime:  event.LastTimestamp.Time,
			Object: struct {
				Kind      string `json:"kind"`
				Name      string `json:"name"`
				Namespace string `json:"namespace"`
			}{
				Kind:      event.InvolvedObject.Kind,
				Name:      event.InvolvedObject.Name,
				Namespace: event.InvolvedObject.Namespace,
			},
		}
		events = append(events, e)
	}

	// Sort events by last time, most recent first
	sort.Slice(events, func(i, j int) bool {
		return events[i].LastTime.After(events[j].LastTime)
	})

	c.logger.Debug("Got recent warning events", "count", len(events), "timeWindow", timeWindow)
	return events, nil
}

// GetClusterHealthEvents returns events that might indicate cluster health issues
func (c *Client) GetClusterHealthEvents(ctx context.Context) ([]models.K8sEvent, error) {
	c.logger.Debug("Getting cluster health events")

	// Define keywords that might indicate cluster health issues
	healthIssueKeywords := []string{
		"Failed", "Error", "CrashLoopBackOff", "OOMKilled", "Evicted",
		"NodeNotReady", "Unhealthy", "OutOfDisk", "MemoryPressure", "DiskPressure",
		"NetworkUnavailable", "Unschedulable",
	}

	// Build field selector for warning events
	fieldSelector := fields.OneTermEqualSelector("type", "Warning")

	// Get events from all namespaces
	eventList, err := c.clientset.CoreV1().Events("").List(ctx, metav1.ListOptions{
		FieldSelector: fieldSelector.String(),
	})
	if err != nil {
		return nil, fmt.Errorf("failed to list warning events: %w", err)
	}

	// Filter and convert to our model
	var events []models.K8sEvent
	for _, event := range eventList.Items {
		// Check if the event matches any health issue keywords
		matchesKeyword := false
		for _, keyword := range healthIssueKeywords {
			if strings.Contains(event.Reason, keyword) || strings.Contains(event.Message, keyword) {
				matchesKeyword = true
				break
			}
		}

		if !matchesKeyword {
			continue
		}

		e := models.K8sEvent{
			Reason:    event.Reason,
			Message:   event.Message,
			Type:      event.Type,
			Count:     int(event.Count),
			FirstTime: event.FirstTimestamp.Time,
			LastTime:  event.LastTimestamp.Time,
			Object: struct {
				Kind      string `json:"kind"`
				Name      string `json:"name"`
				Namespace string `json:"namespace"`
			}{
				Kind:      event.InvolvedObject.Kind,
				Name:      event.InvolvedObject.Name,
				Namespace: event.InvolvedObject.Namespace,
			},
		}
		events = append(events, e)
	}

	// Sort events by last time, most recent first
	sort.Slice(events, func(i, j int) bool {
		return events[i].LastTime.After(events[j].LastTime)
	})

	c.logger.Debug("Got cluster health events", "count", len(events))
	return events, nil
}

```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/k8s/enhanced_client.go:
--------------------------------------------------------------------------------

```go
package k8s

import (
	"context"
	"fmt"
	"strings"
	"sync"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// NamespaceResourcesCollection contains all resources in a namespace
type NamespaceResourcesCollection struct {
	Namespace string                                `json:"namespace"`
	Resources map[string][]unstructured.Unstructured `json:"resources"`
	Stats     map[string]int                         `json:"stats"`
}

// ResourceDetails contains detailed information about a resource
type ResourceDetails struct {
	Resource      *unstructured.Unstructured   `json:"resource"`
	Events        []interface{}                `json:"events"`
	Relationships []ResourceRelationship       `json:"relationships"`
	Metrics       map[string]interface{}       `json:"metrics"`
}

// GetAllNamespaceResources retrieves all resources in a namespace
func (c *Client) GetAllNamespaceResources(ctx context.Context, namespace string) (*NamespaceResourcesCollection, error) {
	c.logger.Info("Getting all resources in namespace", "namespace", namespace)
	
	collection := &NamespaceResourcesCollection{
		Namespace: namespace,
		Resources: make(map[string][]unstructured.Unstructured),
		Stats:     make(map[string]int),
	}
	
	// Discover all available resource types
	resources, err := c.discoveryClient.ServerPreferredResources()
	if err != nil {
		return nil, fmt.Errorf("failed to get server resources: %w", err)
	}
	
	// Use a wait group to parallelize resource collection
	var wg sync.WaitGroup
	var mu sync.Mutex // Mutex for safely updating the collection
	
	// Collect resources for each API group concurrently
	for _, resourceList := range resources {
		wg.Add(1)
		
		go func(resourceList *metav1.APIResourceList) {
			defer wg.Done()
			
			gv, err := schema.ParseGroupVersion(resourceList.GroupVersion)
			if err != nil {
				c.logger.Warn("Failed to parse group version", "groupVersion", resourceList.GroupVersion)
				return
			}
			
			for _, r := range resourceList.APIResources {
				// Skip resources that can't be listed or aren't namespaced
				if !strings.Contains(r.Verbs.String(), "list") || !r.Namespaced {
					continue
				}
				
				// Skip subresources (contains slash)
				if strings.Contains(r.Name, "/") {
					continue
				}
				
				// Build GVR for this resource type
				gvr := schema.GroupVersionResource{
					Group:    gv.Group,
					Version:  gv.Version,
					Resource: r.Name,
				}
				
				// List resources of this type
				list, err := c.dynamicClient.Resource(gvr).Namespace(namespace).List(ctx, metav1.ListOptions{})
				if err != nil {
					c.logger.Warn("Failed to list resources", 
						"namespace", namespace, 
						"resource", r.Name, 
						"error", err)
					continue
				}
				
				// Skip if no resources found
				if len(list.Items) == 0 {
					continue
				}
				
				// Add to collection with thread safety
				mu.Lock()
				collection.Resources[r.Kind] = list.Items
				collection.Stats[r.Kind] = len(list.Items)
				mu.Unlock()
			}
		}(resourceList)
	}
	
	// Wait for all resource collections to complete
	wg.Wait()
	
	c.logger.Info("Collected all namespace resources", 
		"namespace", namespace, 
		"resourceTypes", len(collection.Resources), 
		"totalResources", c.countTotalResources(collection.Stats))
	
	return collection, nil
}

// countTotalResources counts the total number of resources across all types
func (c *Client) countTotalResources(stats map[string]int) int {
	total := 0
	for _, count := range stats {
		total += count
	}
	return total
}

// GetResourceDetails gets detailed information about a specific resource
func (c *Client) GetResourceDetails(ctx context.Context, kind, namespace, name string) (*ResourceDetails, error) {
	c.logger.Info("Getting resource details", "kind", kind, "namespace", namespace, "name", name)
	
	// Get the resource
	resource, err := c.GetResource(ctx, kind, namespace, name)
	if err != nil {
		return nil, fmt.Errorf("failed to get resource: %w", err)
	}
	
	// Initialize resource details
	details := &ResourceDetails{
		Resource: resource,
		Metrics:  make(map[string]interface{}),
	}
	
	// Get resource events
	events, err := c.GetResourceEvents(ctx, namespace, kind, name)
	if err != nil {
		c.logger.Warn("Failed to get resource events", "error", err)
	} else {
		// Convert events to interface for JSON serialization
		eventsInterface := make([]interface{}, len(events))
		for i, event := range events {
			eventMap := map[string]interface{}{
				"reason":    event.Reason,
				"message":   event.Message,
				"type":      event.Type,
				"count":     event.Count,
				"firstTime": event.FirstTime,
				"lastTime":  event.LastTime,
				"object": map[string]string{
					"kind":      event.Object.Kind,
					"name":      event.Object.Name,
					"namespace": event.Object.Namespace,
				},
			}
			eventsInterface[i] = eventMap
		}
		details.Events = eventsInterface
	}
	
	// Add resource-specific metrics
	c.addResourceMetrics(ctx, resource, details)
	
	return details, nil
}

// addResourceMetrics adds resource-specific metrics based on resource type
func (c *Client) addResourceMetrics(ctx context.Context, resource *unstructured.Unstructured, details *ResourceDetails) {
	kind := resource.GetKind()
	
	switch kind {
	case "Pod":
		// Add container statuses
		containers, found, _ := unstructured.NestedSlice(resource.Object, "spec", "containers")
		if found {
			details.Metrics["containerCount"] = len(containers)
		}
		
		// Add status phase
		phase, found, _ := unstructured.NestedString(resource.Object, "status", "phase")
		if found {
			details.Metrics["phase"] = phase
		}
		
		// Add restart counts
		containerStatuses, found, _ := unstructured.NestedSlice(resource.Object, "status", "containerStatuses")
		if found {
			totalRestarts := 0
			for _, cs := range containerStatuses {
				containerStatus, ok := cs.(map[string]interface{})
				if !ok {
					continue
				}
				
				restarts, found, _ := unstructured.NestedInt64(containerStatus, "restartCount")
				if found {
					totalRestarts += int(restarts)
				}
			}
			details.Metrics["totalRestarts"] = totalRestarts
		}
		
	case "Deployment", "StatefulSet", "DaemonSet", "ReplicaSet":
		// Add replica counts
		replicas, found, _ := unstructured.NestedInt64(resource.Object, "spec", "replicas")
		if found {
			details.Metrics["desiredReplicas"] = replicas
		}
		
		availableReplicas, found, _ := unstructured.NestedInt64(resource.Object, "status", "availableReplicas")
		if found {
			details.Metrics["availableReplicas"] = availableReplicas
		}
		
		readyReplicas, found, _ := unstructured.NestedInt64(resource.Object, "status", "readyReplicas")
		if found {
			details.Metrics["readyReplicas"] = readyReplicas
		}
		
		if kind == "Deployment" {
			// Add deployment strategy
			strategy, found, _ := unstructured.NestedString(resource.Object, "spec", "strategy", "type")
			if found {
				details.Metrics["strategy"] = strategy
			}
		}
		
	case "Service":
		// Add service type
		serviceType, found, _ := unstructured.NestedString(resource.Object, "spec", "type")
		if found {
			details.Metrics["type"] = serviceType
		}
		
		// Add port count
		ports, found, _ := unstructured.NestedSlice(resource.Object, "spec", "ports")
		if found {
			details.Metrics["portCount"] = len(ports)
		}
		
	case "PersistentVolumeClaim":
		// Add storage capacity
		capacity, found, _ := unstructured.NestedString(resource.Object, "spec", "resources", "requests", "storage")
		if found {
			details.Metrics["requestedStorage"] = capacity
		}
		
		// Add access modes
		accessModes, found, _ := unstructured.NestedStringSlice(resource.Object, "spec", "accessModes")
		if found {
			details.Metrics["accessModes"] = accessModes
		}
		
		// Add phase
		phase, found, _ := unstructured.NestedString(resource.Object, "status", "phase")
		if found {
			details.Metrics["phase"] = phase
		}
	}
}
```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/correlator/helm_correlator.go:
--------------------------------------------------------------------------------

```go
// internal/correlator/helm_correlator.go

package correlator

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/gitlab"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/helm"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
)

// HelmCorrelator correlates Helm charts with Kubernetes resources
type HelmCorrelator struct {
	gitlabClient *gitlab.Client
	helmParser   *helm.Parser
	logger       *logging.Logger
}

// NewHelmCorrelator creates a new Helm correlator
func NewHelmCorrelator(gitlabClient *gitlab.Client, logger *logging.Logger) *HelmCorrelator {
	if logger == nil {
		logger = logging.NewLogger().Named("helm-correlator")
	}

	return &HelmCorrelator{
		gitlabClient: gitlabClient,
		helmParser:   helm.NewParser(logger.Named("helm")),
		logger:       logger,
	}
}

// AnalyzeCommitHelmChanges analyzes Helm changes in a commit
func (c *HelmCorrelator) AnalyzeCommitHelmChanges(ctx context.Context, projectID string, commitSHA string) ([]string, error) {
	c.logger.Debug("Analyzing Helm changes in commit", "projectID", projectID, "commitSHA", commitSHA)

	// Get commit diff
	diffs, err := c.gitlabClient.GetCommitDiff(ctx, projectID, commitSHA)
	if err != nil {
		return nil, fmt.Errorf("failed to get commit diff: %w", err)
	}

	// Identify Helm chart changes
	helmCharts := c.identifyHelmCharts(diffs)
	if len(helmCharts) == 0 {
		c.logger.Debug("No Helm chart changes found in commit")
		return nil, nil
	}

	// Analyze each chart
	var affectedResources []string

	for chartPath, files := range helmCharts {
		resources, err := c.analyzeHelmChart(ctx, projectID, commitSHA, chartPath, files)
		if err != nil {
			c.logger.Warn("Failed to analyze Helm chart", "chartPath", chartPath, "error", err)
			continue
		}

		affectedResources = append(affectedResources, resources...)
	}

	return affectedResources, nil
}

// AnalyzeMergeRequestHelmChanges analyzes Helm changes in a merge request
func (c *HelmCorrelator) AnalyzeMergeRequestHelmChanges(ctx context.Context, projectID string, mergeRequestIID int) ([]string, error) {
	c.logger.Debug("Analyzing Helm changes in merge request", "projectID", projectID, "mergeRequestIID", mergeRequestIID)

	// Get merge request changes
	mrChanges, err := c.gitlabClient.GetMergeRequestChanges(ctx, projectID, mergeRequestIID)
	if err != nil {
		return nil, fmt.Errorf("failed to get merge request changes: %w", err)
	}

	// Identify Helm chart changes
	var gitlabDiffs []models.GitLabDiff
	for _, change := range mrChanges.Changes {
		diff := models.GitLabDiff{
			OldPath:     change.OldPath,
			NewPath:     change.NewPath,
			Diff:        change.Diff,
			NewFile:     change.NewFile,
			RenamedFile: change.RenamedFile,
			DeletedFile: change.DeletedFile,
		}
		gitlabDiffs = append(gitlabDiffs, diff)
	}
	helmCharts := c.identifyHelmCharts(gitlabDiffs)
	if len(helmCharts) == 0 {
		c.logger.Debug("No Helm chart changes found in merge request")
		return nil, nil
	}

	// Get commits in the merge request
	commits, err := c.gitlabClient.GetMergeRequestCommits(ctx, projectID, mergeRequestIID)
	if err != nil {
		return nil, fmt.Errorf("failed to get merge request commits: %w", err)
	}

	// Use the latest commit SHA for analysis
	var latestCommitSHA string
	if len(commits) > 0 {
		latestCommitSHA = commits[0].ID
	} else {
		latestCommitSHA = mrChanges.DiffRefs.HeadSHA
	}

	// Analyze each chart
	var affectedResources []string

	for chartPath, files := range helmCharts {
		resources, err := c.analyzeHelmChart(ctx, projectID, latestCommitSHA, chartPath, files)
		if err != nil {
			c.logger.Warn("Failed to analyze Helm chart", "chartPath", chartPath, "error", err)
			continue
		}

		affectedResources = append(affectedResources, resources...)
	}

	return affectedResources, nil
}

// identifyHelmCharts identifies Helm charts in changed files
func (c *HelmCorrelator) identifyHelmCharts(diffs []models.GitLabDiff) map[string][]string {
	helmCharts := make(map[string][]string)

	for _, diff := range diffs {
		path := diff.NewPath

		// Skip deleted files
		if diff.DeletedFile {
			continue
		}

		// Check if it's a Helm-related file
		if strings.Contains(path, "Chart.yaml") ||
			strings.Contains(path, "values.yaml") ||
			(strings.Contains(path, "templates/") && strings.HasSuffix(path, ".yaml")) {

			// Extract chart path (parent directory of Chart.yaml or parent's parent for templates)
			chartPath := filepath.Dir(path)
			if strings.Contains(path, "templates/") {
				chartPath = filepath.Dir(filepath.Dir(path))
			}

			// Add to chart files
			if _, exists := helmCharts[chartPath]; !exists {
				helmCharts[chartPath] = []string{}
			}

			helmCharts[chartPath] = append(helmCharts[chartPath], path)
		}
	}

	return helmCharts
}

// analyzeHelmChart analyzes changes in a Helm chart
func (c *HelmCorrelator) analyzeHelmChart(ctx context.Context, projectID, commitSHA, chartPath string, changedFiles []string) ([]string, error) {
	c.logger.Debug("Analyzing Helm chart", "chartPath", chartPath, "changedFiles", changedFiles)

	// Determine chart structure
	chartFiles := make(map[string]string)

	// Get Chart.yaml
	chartYaml, err := c.gitlabClient.GetFileContent(ctx, projectID, fmt.Sprintf("%s/Chart.yaml", chartPath), commitSHA)
	if err != nil {
		c.logger.Warn("Failed to get Chart.yaml", "error", err)
		// Try to continue without Chart.yaml
	} else {
		chartFiles["Chart.yaml"] = chartYaml
	}

	// Get values.yaml
	valuesYaml, err := c.gitlabClient.GetFileContent(ctx, projectID, fmt.Sprintf("%s/values.yaml", chartPath), commitSHA)

	if err != nil {
		c.logger.Warn("Failed to get values.yaml", "error", err)
		// Try to continue without values.yaml
	} else {
		chartFiles["values.yaml"] = valuesYaml
	}

	// Get template files
	for _, file := range changedFiles {
		if strings.Contains(file, "templates/") {
			content, err := c.gitlabClient.GetFileContent(ctx, projectID, file, commitSHA)
			if err != nil {
				c.logger.Warn("Failed to get template file", "file", file, "error", err)
				continue
			}

			// Store template file relative to chart path
			relPath := strings.TrimPrefix(file, chartPath+"/")
			chartFiles[relPath] = content
		}
	}

	// Write chart files to disk for processing
	chartDir, err := c.helmParser.WriteChartFiles(chartFiles)
	if err != nil {
		return nil, fmt.Errorf("failed to write chart files: %w", err)
	}

	// Parse chart to get manifests
	manifests, err := c.helmParser.ParseChart(ctx, chartDir, nil, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to parse chart: %w", err)
	}

	// Extract resources from manifests
	var resources []string
	for _, manifest := range manifests {
		// Extract resource information
		kind, name, namespace := c.extractResourceInfo(manifest)
		if kind != "" && name != "" {
			resource := fmt.Sprintf("%s/%s", kind, name)
			if namespace != "" {
				resource = fmt.Sprintf("%s/%s/%s", namespace, kind, name)
			}
			resources = append(resources, resource)
		}
	}

	c.logger.Debug("Analyzed Helm chart", "chartPath", chartPath, "resourceCount", len(resources))
	return resources, nil
}

// extractResourceInfo extracts kind, name, and namespace from a YAML manifest
func (c *HelmCorrelator) extractResourceInfo(manifest string) (kind, name, namespace string) {
	// Simple parsing - in a real implementation, use proper YAML parsing
	lines := strings.Split(manifest, "\n")

	for _, line := range lines {
		line = strings.TrimSpace(line)

		if strings.HasPrefix(line, "kind:") {
			kind = strings.TrimSpace(strings.TrimPrefix(line, "kind:"))
		} else if strings.HasPrefix(line, "name:") {
			name = strings.TrimSpace(strings.TrimPrefix(line, "name:"))
		} else if strings.HasPrefix(line, "namespace:") {
			namespace = strings.TrimSpace(strings.TrimPrefix(line, "namespace:"))
		}
	}

	return kind, name, namespace
}

// Cleanup cleans up temporary resources
func (c *HelmCorrelator) Cleanup() {
	if c.helmParser != nil {
		c.helmParser.Cleanup()
	}
}

```

--------------------------------------------------------------------------------
/docs/src/content/docs/model-context-protocol.md:
--------------------------------------------------------------------------------

```markdown
---
title: Model Context Protocol
description: Learn about the Model Context Protocol (MCP) and how it powers AI-driven analysis of Kubernetes and GitOps workflows.
date: 2025-03-01
order: 7
tags: ['concepts', 'architecture']
---

# Model Context Protocol

The Model Context Protocol (MCP) is the core technology behind the Kubernetes Claude MCP server. It enables the collection, correlation, and presentation of rich contextual information to Claude AI, allowing for deep analysis and troubleshooting of complex Kubernetes environments.

## What is MCP?

MCP is a framework for providing structured context to large language models (LLMs) like Claude. It solves a fundamental challenge when using AI to analyze complex systems: how to collect and structure all the relevant information about a system so that an AI can understand the complete picture.

In the context of Kubernetes and GitOps:

- **Complete Context**: MCP gathers comprehensive information about resources, their relationships, history, and current state.
- **Cross-System Correlation**: It correlates information from Kubernetes, ArgoCD, GitLab, and other systems.
- **Intelligent Filtering**: It filters and prioritizes information to focus on what's most relevant.
- **Structured Formatting**: It presents information in a way that maximizes Claude's understanding.

## Core Components of MCP

The MCP framework consists of several key components:

### 1. Context Collection

The first step of MCP is gathering comprehensive information about the system being analyzed. For Kubernetes environments, this includes:

- **Resource Definitions**: The complete YAML/JSON specifications of resources
- **Resource Status**: Current runtime status information
- **Events**: Related Kubernetes events
- **Logs**: Container logs for relevant pods
- **Relationships**: Parent-child relationships between resources
- **History**: Deployment history, changes, and previous states
- **GitOps Context**: ArgoCD sync status, GitLab commits, pipelines

### 2. Context Correlation

Once data is collected, MCP correlates information across different systems:

- **Resource to Git**: Which Git repository, branch, and files define a resource
- **Resource to CI/CD**: Which pipelines deployed a resource
- **Resource to Owners**: Which teams or individuals own a resource
- **Dependencies**: How resources depend on each other
- **Change Impact**: How changes in one system affect others

### 3. Context Formatting

MCP formats the correlated information in a standardized structure:

- **Hierarchical Organization**: Information is organized in a logical hierarchy
- **Relevance Sorting**: Most important information is presented first
- **Cross-References**: Clear references between related pieces of information
- **Compact Representation**: Information is presented efficiently to maximize context window usage

### 4. Context Presentation

Finally, MCP presents the formatted context to Claude for analysis:

- **System Prompt**: Instructs Claude on how to interpret the context
- **User Query**: Focuses Claude's analysis on specific questions or issues
- **Analysis Parameters**: Controls the depth, breadth, and style of analysis

## MCP in Action

Here's a simplified view of how MCP works when troubleshooting a Kubernetes deployment:

1. **User Query**: "Why is my deployment not scaling?"
2. **Context Collection**: MCP gathers information about the deployment, related pods, events, logs, node resources, and GitOps configurations.
3. **Context Correlation**: MCP connects the deployment to its ArgoCD application and recent GitLab commits.
4. **Context Formatting**: The information is structured in a hierarchical format that prioritizes scaling-related details.
5. **Claude Analysis**: Claude analyzes the context and identifies that the deployment can't scale because of resource constraints.
6. **Response**: The user receives a detailed explanation and recommendations.

## Protocol Architecture

The MCP implementation consists of several key components:

### 1. Collectors

Collectors are responsible for gathering information from different sources:

- **Kubernetes Collector**: Gathers resource definitions, status, and events
- **ArgoCD Collector**: Gathers application definitions, sync status, and history
- **GitLab Collector**: Gathers repository information, commits, and pipelines
- **Log Collector**: Gathers container logs and application logs

### 2. Correlators

Correlators connect information across different systems:

- **GitOps Correlator**: Connects Kubernetes resources to their Git definitions
- **Deployment Correlator**: Connects resources to their deployment pipelines
- **Issue Correlator**: Connects observed issues to their potential causes
- **Resource Correlator**: Connects resources to their related resources

### 3. Context Manager

The Context Manager is responsible for organizing and formatting the context:

- **Context Selection**: Determines what information to include
- **Context Prioritization**: Prioritizes the most relevant information
- **Context Formatting**: Formats the information for maximum effectiveness
- **Context Truncation**: Ensures the context fits within Claude's context window

### 4. Protocol Handler

The Protocol Handler handles the interaction with Claude:

- **Prompt Generation**: Creates effective system and user prompts
- **Response Processing**: Processes and formats Claude's responses
- **Follow-up Management**: Handles follow-up queries and clarifications

## Example MCP Context

Here's a simplified example of how MCP formats context for Claude:

```
# Kubernetes Resource: Deployment/my-app
Namespace: default
API Version: apps/v1

## Specification
Replicas: 5
Strategy: RollingUpdate
Selector: app=my-app
Template:
  ...truncated for brevity...

## Status
Available Replicas: 3
Ready Replicas: 3
Updated Replicas: 3
Conditions:
- Type: Available, Status: True
- Type: Progressing, Status: True

## Recent Events
1. [Warning] FailedCreate: pods "my-app-7b9d7f8d9-" failed to fit in any node
2. [Normal] ScalingReplicaSet: Scaled up replica set my-app-7b9d7f8d9 to 5
3. [Warning] FailedScheduling: 0/3 nodes are available: insufficient cpu

## ArgoCD Application
Name: my-app
Sync Status: Synced
Health Status: Degraded
Source: https://github.com/myorg/myrepo.git
Path: applications/my-app
Target Revision: main

## Recent GitLab Commits
1. [2025-03-01T10:15:30Z] 7a8b9c0d: Increase replicas from 3 to 5 (John Smith)
2. [2025-02-28T15:45:20Z] 1b2c3d4e: Update resource requests (Jane Doe)

## Node Resources
Total CPU Capacity: 12 cores
Used CPU: 10.5 cores
Available CPU: 1.5 cores
```

This formatted context makes it easy for Claude to understand the complete picture and identify that the deployment can't scale to 5 replicas because of insufficient CPU resources.

## Benefits of MCP

Using the Model Context Protocol provides several key benefits:

1. **Complete Understanding**: Claude gets a holistic view of your environment.
2. **Deeper Analysis**: With more context, Claude can provide more accurate and insightful analysis.
3. **Cross-System Correlation**: Issues that span multiple systems are easier to identify.
4. **Efficient Context Usage**: Structured information maximizes the use of Claude's context window.
5. **Consistent Analysis**: Standardized context leads to more consistent analysis over time.

## Extending MCP

The Model Context Protocol is designed to be extensible. You can add support for additional systems and information sources:

1. **Custom Collectors**: Implement collectors for your specific systems.
2. **Custom Correlators**: Create correlators for your organization's workflows.
3. **Context Templates**: Define custom context templates for your use cases.
4. **Prompt Templates**: Customize prompts for your specific needs.

For more information on extending MCP, see the [Custom Integrations](/docs/custom-integrations) guide.

## Next Steps

Now that you understand the Model Context Protocol, you can:

1. [Explore GitOps Integration](/docs/gitops-integration) to learn how MCP connects with ArgoCD and GitLab.
2. [Try Troubleshooting Resources](/docs/troubleshooting-resources) to see MCP in action.
3. [Review the API Reference](/docs/api-overview) to learn how to interact with the MCP server.
```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/k8s/resources.go:
--------------------------------------------------------------------------------

```go
package k8s

import (
	"bytes"
    "io"
	"context"
	"fmt"
	"strings"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
	
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

// resourceMappings maps common resource types to their API versions and kinds
var resourceMappings = map[string]schema.GroupVersionResource{
	"pod":         {Group: "", Version: "v1", Resource: "pods"},
	"deployment":  {Group: "apps", Version: "v1", Resource: "deployments"},
	"service":     {Group: "", Version: "v1", Resource: "services"},
	"configmap":   {Group: "", Version: "v1", Resource: "configmaps"},
	"secret":      {Group: "", Version: "v1", Resource: "secrets"},
	"statefulset": {Group: "apps", Version: "v1", Resource: "statefulsets"},
	"daemonset":   {Group: "apps", Version: "v1", Resource: "daemonsets"},
	"job":         {Group: "batch", Version: "v1", Resource: "jobs"},
	"cronjob":     {Group: "batch", Version: "v1", Resource: "cronjobs"},
	"ingress":     {Group: "networking.k8s.io", Version: "v1", Resource: "ingresses"},
	"namespace":   {Group: "", Version: "v1", Resource: "namespaces"},
	"node":        {Group: "", Version: "v1", Resource: "nodes"},
	"pv":          {Group: "", Version: "v1", Resource: "persistentvolumes"},
	"pvc":         {Group: "", Version: "v1", Resource: "persistentvolumeclaims"},
}

// getGVR returns the GroupVersionResource for a given resource type
func (c *Client) getGVR(resourceType string) (schema.GroupVersionResource, error) {
	// Check if it's in our pre-defined mappings
	resourceType = strings.ToLower(resourceType)
	if gvr, ok := resourceMappings[resourceType]; ok {
		return gvr, nil
	}

	// Try to get it from the API discovery
	c.logger.Debug("Resource not in predefined mappings, discovering from API", "resourceType", resourceType)
	resources, err := c.discoveryClient.ServerPreferredResources()
	if err != nil {
		return schema.GroupVersionResource{}, fmt.Errorf("failed to get server resources: %w", err)
	}

	for _, list := range resources {
		gv, err := schema.ParseGroupVersion(list.GroupVersion)
		if err != nil {
			continue
		}

		for _, r := range list.APIResources {
			if strings.EqualFold(r.Name, resourceType) || strings.EqualFold(r.SingularName, resourceType) {
				c.logger.Debug("Found resource via API discovery", 
					"resourceType", resourceType, 
					"group", gv.Group, 
					"version", gv.Version, 
					"resource", r.Name)
				return schema.GroupVersionResource{
					Group:    gv.Group,
					Version:  gv.Version,
					Resource: r.Name,
				}, nil
			}
		}
	}

	return schema.GroupVersionResource{}, fmt.Errorf("unknown resource type: %s", resourceType)
}

// GetResource retrieves a specific resource by kind, namespace, and name
func (c *Client) GetResource(ctx context.Context, kind, namespace, name string) (*unstructured.Unstructured, error) {
	c.logger.Debug("Getting resource", "kind", kind, "namespace", namespace, "name", name)
	
	gvr, err := c.getGVR(kind)
	if err != nil {
		return nil, err
	}

	var obj *unstructured.Unstructured
	if namespace != "" {
		obj, err = c.dynamicClient.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
	} else {
		obj, err = c.dynamicClient.Resource(gvr).Get(ctx, name, metav1.GetOptions{})
	}

	if err != nil {
		return nil, fmt.Errorf("failed to get %s %s/%s: %w", kind, namespace, name, err)
	}

	return obj, nil
}

// ListResources lists resources of a specific type, optionally filtered by namespace
func (c *Client) ListResources(ctx context.Context, kind, namespace string) ([]unstructured.Unstructured, error) {
	c.logger.Debug("Listing resources", "kind", kind, "namespace", namespace)
	
	gvr, err := c.getGVR(kind)
	if err != nil {
		return nil, err
	}

	var list *unstructured.UnstructuredList
	if namespace != "" {
		list, err = c.dynamicClient.Resource(gvr).Namespace(namespace).List(ctx, metav1.ListOptions{})
	} else {
		list, err = c.dynamicClient.Resource(gvr).List(ctx, metav1.ListOptions{})
	}

	if err != nil {
		return nil, fmt.Errorf("failed to list resources: %w", err)
	}

	c.logger.Debug("Listed resources", "kind", kind, "count", len(list.Items))
	return list.Items, nil
}

// GetPodStatus returns detailed status information for a pod
func (c *Client) GetPodStatus(ctx context.Context, namespace, name string) (*models.K8sPodStatus, error) {
	c.logger.Debug("Getting pod status", "namespace", namespace, "name", name)
	
	pod, err := c.clientset.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to get pod: %w", err)
	}

	status := &models.K8sPodStatus{
		Phase: string(pod.Status.Phase),
	}

	for _, condition := range pod.Status.Conditions {
		status.Conditions = append(status.Conditions, struct {
			Type   string `json:"type"`
			Status string `json:"status"`
		}{
			Type:   string(condition.Type),
			Status: string(condition.Status),
		})
	}

	// Copy container statuses
	for _, containerStatus := range pod.Status.ContainerStatuses {
		cs := struct {
			Name         string `json:"name"`
			Ready        bool   `json:"ready"`
			RestartCount int    `json:"restartCount"`
			State        struct {
				Running    *struct{} `json:"running,omitempty"`
				Waiting    *struct{} `json:"waiting,omitempty"`
				Terminated *struct{} `json:"terminated,omitempty"`
			} `json:"state"`
			LastState struct {
				Running    *struct{} `json:"running,omitempty"`
				Waiting    *struct{} `json:"waiting,omitempty"`
				Terminated *struct{} `json:"terminated,omitempty"`
			} `json:"lastState"`
		}{
			Name:         containerStatus.Name,
			Ready:        containerStatus.Ready,
			RestartCount: int(containerStatus.RestartCount),
		}

		// Set state
		if containerStatus.State.Running != nil {
			cs.State.Running = &struct{}{}
		}
		if containerStatus.State.Waiting != nil {
			cs.State.Waiting = &struct{}{}
		}
		if containerStatus.State.Terminated != nil {
			cs.State.Terminated = &struct{}{}
		}

		// Set last state
		if containerStatus.LastTerminationState.Running != nil {
			cs.LastState.Running = &struct{}{}
		}
		if containerStatus.LastTerminationState.Waiting != nil {
			cs.LastState.Waiting = &struct{}{}
		}
		if containerStatus.LastTerminationState.Terminated != nil {
			cs.LastState.Terminated = &struct{}{}
		}

		status.ContainerStatuses = append(status.ContainerStatuses, cs)
	}

	return status, nil
}

// GetPodLogs returns logs for a specific container in a pod
func (c *Client) GetPodLogs(ctx context.Context, namespace, name, container string, tailLines int64) (string, error) {
	c.logger.Debug("Getting pod logs", 
		"namespace", namespace, 
		"name", name, 
		"container", container, 
		"tailLines", tailLines)
	
	podLogOptions := corev1.PodLogOptions{
		Container: container,
	}
	
	if tailLines > 0 {
		podLogOptions.TailLines = &tailLines
	}
	
	req := c.clientset.CoreV1().Pods(namespace).GetLogs(name, &podLogOptions)
	podLogs, err := req.Stream(ctx)
	if err != nil {
		return "", fmt.Errorf("failed to get pod logs: %w", err)
	}
	defer podLogs.Close()

	buf := new(bytes.Buffer)
	_, err = io.Copy(buf, podLogs)
	if err != nil {
		return "", fmt.Errorf("failed to read pod logs: %w", err)
	}

	return buf.String(), nil
}

// FindOwnerReferences finds the owner references for a resource
func (c *Client) FindOwnerReferences(ctx context.Context, obj *unstructured.Unstructured) ([]unstructured.Unstructured, error) {
	c.logger.Debug("Finding owner references", 
		"kind", obj.GetKind(), 
		"name", obj.GetName(), 
		"namespace", obj.GetNamespace())
	
	ownerRefs := obj.GetOwnerReferences()
	if len(ownerRefs) == 0 {
		return nil, nil
	}

	var owners []unstructured.Unstructured
	for _, ref := range ownerRefs {
		c.logger.Debug("Found owner reference", 
			"kind", ref.Kind, 
			"name", ref.Name, 
			"namespace", obj.GetNamespace())
		
		gvr, err := c.getGVR(ref.Kind)
		if err != nil {
			c.logger.Warn("Failed to get GroupVersionResource for owner", 
				"kind", ref.Kind, 
				"error", err)
			continue
		}

		namespace := obj.GetNamespace()
		owner, err := c.dynamicClient.Resource(gvr).Namespace(namespace).Get(ctx, ref.Name, metav1.GetOptions{})
		if err != nil {
			if errors.IsNotFound(err) {
				c.logger.Warn("Owner not found", 
					"kind", ref.Kind, 
					"name", ref.Name, 
					"namespace", namespace)
				continue
			}
			return nil, fmt.Errorf("failed to get owner reference: %w", err)
		}

		owners = append(owners, *owner)
	}

	return owners, nil
}
```

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

```go
// internal/gitlab/mergerequests.go

package gitlab

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
)

// ListMergeRequests returns a list of merge requests for a project
func (c *Client) ListMergeRequests(ctx context.Context, projectID string, state string) ([]models.GitLabMergeRequest, error) {
	c.logger.Debug("Listing merge requests", "projectID", projectID, "state", state)

	// Create endpoint with query parameters
	endpoint := fmt.Sprintf("projects/%s/merge_requests", url.PathEscape(projectID))

	u, err := url.Parse(endpoint)
	if err != nil {
		return nil, fmt.Errorf("invalid endpoint: %w", err)
	}

	q := u.Query()
	if state != "" {
		q.Set("state", state)
	}
	q.Set("order_by", "updated_at")
	q.Set("sort", "desc")
	q.Set("per_page", "20")
	u.RawQuery = q.Encode()

	resp, err := c.doRequest(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var mergeRequests []models.GitLabMergeRequest
	if err := json.NewDecoder(resp.Body).Decode(&mergeRequests); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	c.logger.Debug("Listed merge requests", "projectID", projectID, "count", len(mergeRequests))
	return mergeRequests, nil
}

// GetMergeRequest returns details about a specific merge request
func (c *Client) GetMergeRequest(ctx context.Context, projectID string, mergeRequestIID int) (*models.GitLabMergeRequest, error) {
	c.logger.Debug("Getting merge request", "projectID", projectID, "mergeRequestIID", mergeRequestIID)

	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d", url.PathEscape(projectID), mergeRequestIID)
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var mergeRequest models.GitLabMergeRequest
	if err := json.NewDecoder(resp.Body).Decode(&mergeRequest); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &mergeRequest, nil
}

// GetMergeRequestChanges returns the changes in a specific merge request
func (c *Client) GetMergeRequestChanges(ctx context.Context, projectID string, mergeRequestIID int) (*models.GitLabMergeRequest, error) {
	c.logger.Debug("Getting merge request changes", "projectID", projectID, "mergeRequestIID", mergeRequestIID)

	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d/changes", url.PathEscape(projectID), mergeRequestIID)
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var mergeRequest models.GitLabMergeRequest
	if err := json.NewDecoder(resp.Body).Decode(&mergeRequest); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &mergeRequest, nil
}

// GetMergeRequestApprovals returns approval information for a merge request
func (c *Client) GetMergeRequestApprovals(ctx context.Context, projectID string, mergeRequestIID int) (*models.GitLabMergeRequestApproval, error) {
	c.logger.Debug("Getting merge request approvals", "projectID", projectID, "mergeRequestIID", mergeRequestIID)

	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", url.PathEscape(projectID), mergeRequestIID)
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var approvals models.GitLabMergeRequestApproval
	if err := json.NewDecoder(resp.Body).Decode(&approvals); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &approvals, nil
}

// GetMergeRequestComments returns comments on a merge request
func (c *Client) GetMergeRequestComments(ctx context.Context, projectID string, mergeRequestIID int) ([]models.GitLabMergeRequestComment, error) {
	c.logger.Debug("Getting merge request comments", "projectID", projectID, "mergeRequestIID", mergeRequestIID)

	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d/notes", url.PathEscape(projectID), mergeRequestIID)
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var comments []models.GitLabMergeRequestComment
	if err := json.NewDecoder(resp.Body).Decode(&comments); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	c.logger.Debug("Got merge request comments", "projectID", projectID, "mergeRequestIID", mergeRequestIID, "count", len(comments))
	return comments, nil
}

// GetMergeRequestCommits returns the commits in a merge request
func (c *Client) GetMergeRequestCommits(ctx context.Context, projectID string, mergeRequestIID int) ([]models.GitLabCommit, error) {
	c.logger.Debug("Getting merge request commits", "projectID", projectID, "mergeRequestIID", mergeRequestIID)

	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d/commits", url.PathEscape(projectID), mergeRequestIID)
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var commits []models.GitLabCommit
	if err := json.NewDecoder(resp.Body).Decode(&commits); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	c.logger.Debug("Got merge request commits", "projectID", projectID, "mergeRequestIID", mergeRequestIID, "count", len(commits))
	return commits, nil
}

// AnalyzeMergeRequest analyzes a merge request for Kubernetes/Helm changes
func (c *Client) AnalyzeMergeRequest(ctx context.Context, projectID string, mergeRequestIID int) (*models.GitLabMergeRequest, error) {
	c.logger.Debug("Analyzing merge request", "projectID", projectID, "mergeRequestIID", mergeRequestIID)

	// Get basic merge request data
	mr, err := c.GetMergeRequest(ctx, projectID, mergeRequestIID)
	if err != nil {
		return nil, fmt.Errorf("failed to get merge request: %w", err)
	}

	// Get changes
	mrChanges, err := c.GetMergeRequestChanges(ctx, projectID, mergeRequestIID)
	if err != nil {
		return nil, fmt.Errorf("failed to get merge request changes: %w", err)
	}

	// Copy changes to the original merge request
	mr.Changes = mrChanges.Changes

	// Initialize context analysis
	mr.MergeRequestContext.AffectedFiles = make([]string, 0)
	mr.MergeRequestContext.HelmChartAffected = false
	mr.MergeRequestContext.KubernetesManifest = false

	// Analyze changes
	for _, change := range mr.Changes {
		mr.MergeRequestContext.AffectedFiles = append(mr.MergeRequestContext.AffectedFiles, change.NewPath)

		// Check for Helm charts
		if strings.Contains(change.NewPath, "Chart.yaml") ||
			strings.Contains(change.NewPath, "values.yaml") ||
			(strings.Contains(change.NewPath, "templates/") && strings.HasSuffix(change.NewPath, ".yaml")) {
			mr.MergeRequestContext.HelmChartAffected = true
		}

		// Check for Kubernetes manifests
		if strings.HasSuffix(change.NewPath, ".yaml") || strings.HasSuffix(change.NewPath, ".yml") {
			// Look for Kubernetes kind in the file content
			if strings.Contains(change.Diff, "kind:") &&
				(strings.Contains(change.Diff, "Deployment") ||
					strings.Contains(change.Diff, "Service") ||
					strings.Contains(change.Diff, "ConfigMap") ||
					strings.Contains(change.Diff, "Secret") ||
					strings.Contains(change.Diff, "Pod")) {
				mr.MergeRequestContext.KubernetesManifest = true
			}
		}
	}

	// Get commits
	commits, err := c.GetMergeRequestCommits(ctx, projectID, mergeRequestIID)
	if err != nil {
		c.logger.Warn("Failed to get merge request commits", "error", err)
	} else {
		// Extract commit messages
		mr.MergeRequestContext.CommitMessages = make([]string, 0)
		for _, commit := range commits {
			mr.MergeRequestContext.CommitMessages = append(mr.MergeRequestContext.CommitMessages, commit.Title)
		}
	}

	return mr, nil
}

// CreateMergeRequestComment creates a new comment on a merge request
func (c *Client) CreateMergeRequestComment(ctx context.Context, projectID string, mergeRequestIID int, body string) (*models.GitLabMergeRequestComment, error) {
	c.logger.Debug("Creating merge request comment", "projectID", projectID, "mergeRequestIID", mergeRequestIID)

	endpoint := fmt.Sprintf("projects/%s/merge_requests/%d/notes", url.PathEscape(projectID), mergeRequestIID)

	// Create request payload
	reqBody := map[string]string{
		"body": body,
	}

	jsonBody, err := json.Marshal(reqBody)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request body: %w", err)
	}

	resp, err := c.doRequest(ctx, http.MethodPost, endpoint, strings.NewReader(string(jsonBody)))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var comment models.GitLabMergeRequestComment
	if err := json.NewDecoder(resp.Body).Decode(&comment); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	return &comment, nil
}

```

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

```
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import CodeBlock from '../../components/CodeBlock.astro';

const exampleCategories = [
  {
    title: "Basic API Usage",
    description: "Examples of common API calls for resource analysis",
    examples: [
      {
        title: "Analyzing a Pod",
        description: "Query the status and health of a pod",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "resource": "pod",
    "name": "my-app-pod",
    "namespace": "default",
    "query": "Is this pod healthy? What do the resource usage metrics show?"
  }' \\
  http://mcp-server.example.com/api/v1/mcp/resource`,
        language: "bash"
      },
      {
        title: "Checking Service Connectivity",
        description: "Investigate connectivity issues between services",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "resource": "service",
    "name": "backend-service",
    "namespace": "default",
    "query": "Why can't my frontend pods connect to this service?"
  }' \\
  http://mcp-server.example.com/api/v1/mcp/resource`,
        language: "bash"
      },
      {
        title: "Deployment Analysis",
        description: "Understand why a deployment isn't scaling properly",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "resource": "deployment",
    "name": "web-frontend",
    "namespace": "default",
    "query": "Why is this deployment not scaling to the requested replicas?"
  }' \\
  http://mcp-server.example.com/api/v1/mcp/resource`,
        language: "bash"
      }
    ]
  },
  {
    title: "Troubleshooting",
    description: "Examples for diagnosing and fixing common issues",
    examples: [
      {
        title: "CrashLoopBackOff Investigation",
        description: "Troubleshoot a pod in CrashLoopBackOff state",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "resource": "pod",
    "name": "crashing-pod",
    "namespace": "production",
    "query": "This pod is in CrashLoopBackOff. What's causing it and how can I fix it?"
  }' \\
  http://mcp-server.example.com/api/v1/mcp/troubleshoot`,
        language: "bash"
      },
      {
        title: "Ingress Issues",
        description: "Debug problems with an Ingress resource",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "resource": "ingress",
    "name": "app-ingress",
    "namespace": "default",
    "query": "External users are getting 404 errors when accessing the application. What's wrong with this ingress?"
  }' \\
  http://mcp-server.example.com/api/v1/mcp/troubleshoot`,
        language: "bash"
      },
      {
        title: "Storage Problems",
        description: "Troubleshoot issues with PersistentVolumeClaims",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "resource": "persistentvolumeclaim",
    "name": "database-storage",
    "namespace": "database",
    "query": "Why is this PVC stuck in pending state?"
  }' \\
  http://mcp-server.example.com/api/v1/mcp/troubleshoot`,
        language: "bash"
      }
    ]
  },
  {
    title: "GitOps Workflows",
    description: "Examples for CI/CD integration and GitOps analysis",
    examples: [
      {
        title: "ArgoCD Application Analysis",
        description: "Check sync status and health of an ArgoCD application",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "resource": "application",
    "name": "production-app",
    "namespace": "argocd",
    "query": "Is this application synced and healthy? If not, what are the issues?"
  }' \\
  http://mcp-server.example.com/api/v1/mcp/resource`,
        language: "bash"
      },
      {
        title: "Commit Impact Analysis",
        description: "Analyze how a specific commit affected the cluster",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "projectId": "mygroup/myproject",
    "commitSha": "a1b2c3d4e5f6",
    "query": "What changes were made in this commit and how have they affected the deployed resources?"
  }' \\
  http://mcp-server.example.com/api/v1/mcp/commit`,
        language: "bash"
      },
      {
        title: "ArgoCD Sync Failure",
        description: "Troubleshoot why an ArgoCD application isn't syncing",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "resource": "application",
    "name": "failing-app",
    "namespace": "argocd",
    "query": "Why is this application failing to sync? What specific errors are occurring?"
  }' \\
  http://mcp-server.example.com/api/v1/mcp/troubleshoot`,
        language: "bash"
      }
    ]
  },
  {
    title: "Advanced Usage",
    description: "Examples for more complex scenarios and integrations",
    examples: [
      {
        title: "Resource Relationship Analysis",
        description: "Understanding dependencies between resources",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "resource": "deployment",
    "name": "application",
    "namespace": "production",
    "query": "Create a map of all resources related to this deployment, including services, configmaps, secrets, and ingresses."
  }' \\
  http://mcp-server.example.com/api/v1/mcp/resource`,
        language: "bash"
      },
      {
        title: "Multi-Resource Correlation",
        description: "Analyze interactions between multiple resources",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "query": "Analyze the connection between the frontend deployment, backend service, and redis statefulset in the web namespace. Are there any connectivity or configuration issues?"
  }' \\
  http://mcp-server.example.com/api/v1/mcp`,
        language: "bash"
      },
      {
        title: "GitOps Security Audit",
        description: "Audit resources for security issues and best practices",
        code: `curl -X POST \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: your_api_key" \\
  -d '{
    "resource": "namespace",
    "name": "production",
    "query": "Perform a security audit of all resources in this namespace. Check for security best practices, RBAC issues, and potential vulnerabilities."
  }' \\
  http://mcp-server.example.com/api/v1/mcp/resource`,
        language: "bash"
      }
    ]
  }
];
---

<BaseLayout title="Examples | Kubernetes Claude MCP">
  <div class="container mx-auto px-4 py-12">
    <div class="max-w-5xl mx-auto">
      <h1 class="text-4xl font-bold mb-6 text-primary-600">Examples</h1>
      <p class="text-xl text-slate-600 mb-10">
        Explore practical examples of using the Kubernetes Claude MCP server for various use cases. 
        These examples demonstrate how to leverage the API for resource analysis, troubleshooting, and GitOps workflows.
      </p>
      
      <div class="space-y-16">
        {exampleCategories.map(category => (
          <section class="example-category">
            <h2 class="text-2xl font-bold mb-3 text-primary-600">{category.title}</h2>
            <p class="text-lg text-slate-600 mb-6">{category.description}</p>
            
            <div class="space-y-8">
              {category.examples.map(example => (
                <div class="example-card border border-secondary-300 rounded-lg overflow-hidden bg-secondary-50">
                  <div class="p-5 border-b border-secondary-300 bg-secondary-100">
                    <h3 class="text-xl font-semibold text-primary-600">{example.title}</h3>
                    <p class="text-slate-600 mt-1">{example.description}</p>
                  </div>
                  <div class="p-5">
                    <CodeBlock 
                      code={example.code} 
                      lang={example.language}
                      showLineNumbers={true}
                    />
                  </div>
                </div>
              ))}
            </div>
          </section>
        ))}
      </div>
      
      <div class="mt-12 text-center">
        <h2 class="text-2xl font-bold mb-4 text-primary-600">Need More Help?</h2>
        <p class="text-lg text-slate-600 mb-6">
          Check out the detailed usage guides in the documentation or visit our GitHub repository.
        </p>
        <div class="flex flex-col sm:flex-row gap-4 justify-center">
          <a href="/docs/api-overview" class="btn bg-primary-500 hover:bg-primary-600 text-white font-medium py-2 px-6 rounded-md">
            API Reference
          </a>
          
        </div>
      </div>
    </div>
  </div>
</BaseLayout>
```

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

```markdown
---
title: API Overview
description: Comprehensive documentation of the Kubernetes Claude MCP REST API endpoints, parameters, and response formats.
date: 2025-03-01
order: 5
tags: ['api', 'reference']
---

# API Overview

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.

## API Structure

The API is organized into the following sections:

- **General**: Health check and general information
- **Kubernetes API**: Direct access to Kubernetes resources
- **ArgoCD API**: Access to ArgoCD applications and sync status
- **MCP API**: AI-powered analysis and troubleshooting

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.

```bash
# Using X-API-Key header
curl -H "X-API-Key: your_api_key" https://mcp.example.com/api/v1/health

# Using Authorization header
curl -H "Authorization: Bearer your_api_key" https://mcp.example.com/api/v1/health
```

## Health Check

### GET /api/v1/health

Check the health status of the server and its connected services.

**Response:**

```json
{
  "status": "ok",
  "services": {
    "kubernetes": "available",
    "argocd": "available",
    "gitlab": "available",
    "claude": "assumed available"
  }
}
```

The `status` field will be `ok` if all required services are available, or `degraded` if some services are unavailable.

## Kubernetes API

### GET /api/v1/namespaces

List all namespaces in the Kubernetes cluster.

**Response:**

```json
{
  "namespaces": [
    "default",
    "kube-system",
    "monitoring",
    "argocd"
  ]
}
```

### GET /api/v1/resources/{kind}?namespace={ns}

List all resources of a specific kind, optionally filtered by namespace.

**Parameters:**
- `kind`: The Kubernetes resource kind (e.g., `pod`, `deployment`, `service`)
- `namespace`: (Optional) The namespace to filter by

**Response:**

```json
{
  "resources": [
    {
      "apiVersion": "v1",
      "kind": "Pod",
      "metadata": {
        "name": "example-pod",
        "namespace": "default",
        "...": "..."
      },
      "spec": { "...": "..." },
      "status": { "...": "..." }
    },
    // More resources...
  ]
}
```

### GET /api/v1/resources/{kind}/{name}?namespace={ns}

Get a specific resource by kind, name, and namespace.

**Parameters:**
- `kind`: The Kubernetes resource kind
- `name`: The resource name
- `namespace`: (Optional) The namespace of the resource

**Response:**

```json
{
  "apiVersion": "v1",
  "kind": "Pod",
  "metadata": {
    "name": "example-pod",
    "namespace": "default",
    "...": "..."
  },
  "spec": { "...": "..." },
  "status": { "...": "..." }
}
```

### GET /api/v1/events?namespace={ns}&resource={kind}&name={name}

Get events related to a specific resource.

**Parameters:**
- `namespace`: The namespace of the resource
- `resource`: The resource kind
- `name`: The resource name

**Response:**

```json
{
  "events": [
    {
      "reason": "Created",
      "message": "Created container nginx",
      "type": "Normal",
      "count": 1,
      "firstTime": "2025-03-01T12:00:00Z",
      "lastTime": "2025-03-01T12:00:00Z",
      "object": {
        "kind": "Pod",
        "name": "example-pod",
        "namespace": "default"
      }
    },
    // More events...
  ]
}
```

## ArgoCD API

### GET /api/v1/argocd/applications

List all ArgoCD applications.

**Response:**

```json
{
  "applications": [
    {
      "metadata": {
        "name": "example-app",
        "namespace": "argocd"
      },
      "spec": {
        "source": {
          "repoURL": "https://github.com/example/repo.git",
          "path": "manifests",
          "targetRevision": "HEAD"
        },
        "destination": {
          "server": "https://kubernetes.default.svc",
          "namespace": "default"
        }
      },
      "status": {
        "sync": {
          "status": "Synced"
        },
        "health": {
          "status": "Healthy"
        }
      }
    },
    // More applications...
  ]
}
```

### GET /api/v1/argocd/applications/{name}

Get a specific ArgoCD application by name.

**Parameters:**
- `name`: The ArgoCD application name

**Response:**

```json
{
  "metadata": {
    "name": "example-app",
    "namespace": "argocd"
  },
  "spec": {
    "source": {
      "repoURL": "https://github.com/example/repo.git",
      "path": "manifests",
      "targetRevision": "HEAD"
    },
    "destination": {
      "server": "https://kubernetes.default.svc",
      "namespace": "default"
    }
  },
  "status": {
    "sync": {
      "status": "Synced"
    },
    "health": {
      "status": "Healthy"
    }
  }
}
```

## MCP API

The MCP API provides access to Claude's AI capabilities for analyzing Kubernetes resources and GitOps workflows.

### POST /api/v1/mcp

Generic MCP request for Claude analysis.

**Request:**

```json
{
  "action": "string",
  "resource": "string",
  "name": "string",
  "namespace": "string",
  "query": "string",
  "commitSha": "string",
  "projectId": "string",
  "resourceSpecs": {},
  "context": "string"
}
```

**Response:**

```json
{
  "success": true,
  "message": "Successfully processed request",
  "analysis": "Detailed analysis from Claude...",
  "actions": ["suggested actions..."],
  "context": {}
}
```

### POST /api/v1/mcp/resource

Analyze a specific Kubernetes resource.

**Request:**

```json
{
  "resource": "pod",
  "name": "example-pod",
  "namespace": "default",
  "query": "Is this pod healthy? If not, what are the issues?"
}
```

**Response:**

```json
{
  "success": true,
  "message": "Successfully processed queryResource request",
  "analysis": "Detailed analysis of the pod's health status...",
  "context": {
    "kind": "Pod",
    "name": "example-pod",
    "namespace": "default",
    "argoApplication": {},
    "gitlabProject": {},
    "events": []
  }
}
```

### POST /api/v1/mcp/commit

Analyze the impact of a specific GitLab commit.

**Request:**

```json
{
  "projectId": "group/project",
  "commitSha": "abcdef123456",
  "query": "What changes were made in this commit and how do they affect the deployed resources?"
}
```

**Response:**

```json
{
  "success": true,
  "message": "Successfully processed queryCommit request",
  "analysis": "Detailed analysis of the commit and its impact...",
  "context": {
    "commit": {},
    "affectedResources": []
  }
}
```

### POST /api/v1/mcp/troubleshoot

Troubleshoot a specific Kubernetes resource.

**Request:**

```json
{
  "resource": "deployment",
  "name": "example-deployment",
  "namespace": "default",
  "query": "Why is this deployment not scaling properly?"
}
```

**Response:**

```json
{
  "success": true,
  "message": "Successfully processed troubleshoot request",
  "analysis": "Detailed troubleshooting analysis...",
  "troubleshootResult": {
    "issues": [
      {
        "title": "Resource Constraint",
        "category": "ResourceIssue",
        "severity": "Warning",
        "source": "Kubernetes",
        "description": "Deployment cannot scale due to insufficient CPU resources"
      }
    ],
    "recommendations": [
      "Increase CPU request to allow for additional replicas",
      "Check node resources to ensure sufficient capacity"
    ]
  }
}
```

## Error Handling

All API endpoints return standard HTTP status codes:

- `200 OK`: Request was successful
- `400 Bad Request`: Invalid request format or parameters
- `401 Unauthorized`: Missing or invalid API key
- `404 Not Found`: Resource not found
- `500 Internal Server Error`: Server error

Error responses include a JSON body with details:

```json
{
  "error": "Failed to get resource",
  "details": "pod 'example-pod' not found in namespace 'default'"
}
```

## Pagination

For endpoints that return collections, pagination is supported using the following query parameters:

- `limit`: Maximum number of items to return (default: 100)
- `page`: Page number to return (default: 1)

Example:

```
GET /api/v1/resources/pods?namespace=default&limit=10&page=2
```

Response includes pagination metadata:

```json
{
  "resources": [...],
  "pagination": {
    "total": 25,
    "pages": 3,
    "currentPage": 2,
    "limit": 10
  }
}
```

## API Versioning

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.

## Rate Limiting

The API implements rate limiting to prevent abuse. Rate limits vary by endpoint:

- General endpoints: 100 requests per minute
- Kubernetes endpoints: 60 requests per minute
- ArgoCD endpoints: 60 requests per minute
- MCP endpoints: 20 requests per minute

Rate limit information is included in response headers:

- `X-RateLimit-Limit`: Total requests allowed per minute
- `X-RateLimit-Remaining`: Remaining requests in the current window
- `X-RateLimit-Reset`: Seconds until the rate limit resets
```

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

```
---
import BaseLayout from '../layouts/BaseLayout.astro';
---

<BaseLayout title="Kubernetes Claude MCP - AI-powered GitOps with Claude" 
  description="The official documentation for Kubernetes Claude MCP Server, integrating Claude AI with Kubernetes, ArgoCD, and GitLab">
  
  <!-- Hero Section -->
  <section class="py-20 px-4 bg-gradient-to-b from-secondary-200 to-secondary-100">
    <div class="container mx-auto max-w-5xl">
      <div class="flex flex-col items-center text-center">
        <img src="/images/logo.svg" alt="Kubernetes Claude MCP Logo" class="w-48 h-48 mb-6">
        <h1 class="text-5xl font-bold tracking-tight mb-6 text-primary-600">
          Kubernetes Claude MCP
        </h1>
        <p class="text-xl text-slate-700 mb-8 max-w-2xl">
          Model Context Protocol server for Kubernetes, integrating Claude AI with ArgoCD and GitLab for advanced GitOps workflows.
        </p>
        <div class="flex flex-col sm:flex-row gap-4">
          <a href="/docs/introduction" class="btn bg-primary-500 hover:bg-primary-600 text-white font-medium py-2 px-6 rounded-md">
            Get Started
          </a>
          <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">
            GitHub
          </a>
        </div>
      </div>
    </div>
  </section>

  <!-- Features Section -->
  <section class="py-16 px-4 bg-secondary-100">
    <div class="container mx-auto max-w-6xl">
      <h2 class="text-3xl font-bold text-center mb-12 text-primary-600">Key Features</h2>
      
      <div class="grid md:grid-cols-3 gap-8">
        <!-- Feature 1 -->
        <div class="feature-card p-6 rounded-lg border border-secondary-200 shadow-sm bg-secondary-100">
          <div class="mb-4 text-primary-500">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <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" />
            </svg>
          </div>
          <h3 class="text-xl font-semibold mb-2 text-primary-600">Kubernetes Integration</h3>
          <p class="text-slate-600">
            Seamlessly connects to your Kubernetes cluster, providing automated analysis and troubleshooting of resources.
          </p>
        </div>

        <!-- Feature 2 -->
        <div class="feature-card p-6 rounded-lg border border-secondary-200 shadow-sm bg-secondary-100">
          <div class="mb-4 text-primary-500">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <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" />
            </svg>
          </div>
          <h3 class="text-xl font-semibold mb-2 text-primary-600">Claude AI Powered</h3>
          <p class="text-slate-600">
            Leverages Claude's AI capabilities to analyze configurations, explain issues, and recommend solutions for your cluster.
          </p>
        </div>

        <!-- Feature 3 -->
        <div class="feature-card p-6 rounded-lg border border-secondary-200 shadow-sm bg-secondary-100">
          <div class="mb-4 text-primary-500">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <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" />
            </svg>
          </div>
          <h3 class="text-xl font-semibold mb-2 text-primary-600">GitOps Integration</h3>
          <p class="text-slate-600">
            Integrates with ArgoCD and GitLab to provide complete GitOps context for your deployments and configuration changes.
          </p>
        </div>
      </div>
      
      <div class="grid md:grid-cols-2 gap-8 mt-8">
        <!-- Feature 4 -->
        <div class="feature-card p-6 rounded-lg border border-secondary-200 shadow-sm bg-secondary-100">
          <div class="mb-4 text-primary-500">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <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" />
            </svg>
          </div>
          <h3 class="text-xl font-semibold mb-2 text-primary-600">Troubleshooting & Analysis</h3>
          <p class="text-slate-600">
            Quickly identify issues in your cluster with comprehensive troubleshooting and analysis capabilities that trace through your entire deployment pipeline.
          </p>
        </div>

        <!-- Feature 5 -->
        <div class="feature-card p-6 rounded-lg border border-secondary-200 shadow-sm bg-secondary-100">
          <div class="mb-4 text-primary-500">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <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" />
            </svg>
          </div>
          <h3 class="text-xl font-semibold mb-2 text-primary-600">RESTful API</h3>
          <p class="text-slate-600">
            Comprehensive API for integrating with your existing tools and workflows, with detailed documentation and examples.
          </p>
        </div>
      </div>
    </div>
  </section>

  <!-- How It Works Section -->
  <section class="py-16 px-4">
    <div class="container mx-auto max-w-6xl">
      <h2 class="text-3xl font-bold text-center mb-12 text-primary-600">How It Works</h2>
      
      <div class="grid md:grid-cols-3 gap-8 text-center">
        <!-- Step 1 -->
        <div class="step">
          <div class="bg-secondary-200 rounded-full h-16 w-16 flex items-center justify-center mx-auto mb-4 border border-secondary-300">
            <span class="text-primary-600 text-xl font-bold">1</span>
          </div>
          <h3 class="text-xl font-semibold mb-2 text-primary-600">Connect</h3>
          <p class="text-slate-600">
            Connect the server to your Kubernetes cluster, ArgoCD instance, and GitLab repositories.
          </p>
        </div>
        
        <!-- Step 2 -->
        <div class="step">
          <div class="bg-secondary-200 rounded-full h-16 w-16 flex items-center justify-center mx-auto mb-4 border border-secondary-300">
            <span class="text-primary-600 text-xl font-bold">2</span>
          </div>
          <h3 class="text-xl font-semibold mb-2 text-primary-600">Query</h3>
          <p class="text-slate-600">
            Query resources, deployments, or commits through the API to get AI-powered analysis and context.
          </p>
        </div>
        
        <!-- Step 3 -->
        <div class="step">
          <div class="bg-secondary-200 rounded-full h-16 w-16 flex items-center justify-center mx-auto mb-4 border border-secondary-300">
            <span class="text-primary-600 text-xl font-bold">3</span>
          </div>
          <h3 class="text-xl font-semibold mb-2 text-primary-600">Analyze</h3>
          <p class="text-slate-600">
            Receive detailed analysis, troubleshooting recommendations, and insights from Claude AI.
          </p>
        </div>
      </div>
    </div>
  </section>

  <!-- CTA Section -->
  <section class="py-16 px-4 bg-secondary-100">
    <div class="container mx-auto max-w-5xl">
      <div class="bg-primary-500 text-white rounded-lg p-8 md:p-12 shadow-lg">
        <div class="text-center">
          <h2 class="text-3xl font-bold mb-4">Ready to transform your Kubernetes experience?</h2>
          <p class="text-lg opacity-90 mb-8 max-w-2xl mx-auto">
            Get started with Kubernetes Claude MCP today and enhance your GitOps workflows with AI-powered analysis and troubleshooting.
          </p>
          <div class="flex flex-col sm:flex-row gap-4 justify-center">
            <a href="/docs/quick-start" class="btn bg-white text-primary-700 hover:bg-secondary-100 font-medium py-2 px-6 rounded-md">
              Quick Start Guide
            </a>
            <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">
              Installation Guide
            </a>
          </div>
        </div>
      </div>
    </div>
  </section>
</BaseLayout>
```

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

```go
package argocd

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/auth"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/config"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
)

// Client handles communication with the ArgoCD API
type Client struct {
	baseURL            string
	httpClient         *http.Client
	credentialProvider *auth.CredentialProvider
	config             *config.ArgoCDConfig
	logger             *logging.Logger
}

// NewClient creates a new ArgoCD API client
func NewClient(cfg *config.ArgoCDConfig, credProvider *auth.CredentialProvider, logger *logging.Logger) *Client {
	if logger == nil {
		logger = logging.NewLogger().Named("argocd")
	}

	// Create transport with optional insecure mode
	transport := &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: cfg.Insecure,
		},
	}

	return &Client{
		baseURL: cfg.URL,
		httpClient: &http.Client{
			Timeout:   30 * time.Second,
			Transport: transport,
		},
		credentialProvider: credProvider,
		config:             cfg,
		logger:             logger,
	}
}

// CheckConnectivity tests the connection to the ArgoCD API
func (c *Client) CheckConnectivity(ctx context.Context) error {
	c.logger.Debug("Checking ArgoCD connectivity")

	// Try to get ArgoCD version as a basic connectivity test
	endpoint := "/api/version"
	resp, err := c.doRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return fmt.Errorf("failed to connect to ArgoCD: %w", err)
	}
	defer resp.Body.Close()

	var version struct {
		Version string `json:"version"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&version); err != nil {
		return fmt.Errorf("failed to decode ArgoCD version: %w", err)
	}

	c.logger.Debug("ArgoCD connectivity check successful", "version", version.Version)
	return nil
}

// doRequest performs an HTTP request to the ArgoCD API with authentication
func (c *Client) doRequest(ctx context.Context, method, endpoint string, body io.Reader) (*http.Response, error) {
    // Try the request with current credentials
    resp, err := c.attemptRequest(ctx, method, endpoint, body)
    
    // If we get a 401 unauthorized, try to refresh the token and retry once
    if err != nil && resp != nil && resp.StatusCode == http.StatusUnauthorized {
        c.logger.Debug("Received 401 from ArgoCD, attempting to refresh token")
        
        // Only try to refresh the token if we have username/password
        creds, err := c.credentialProvider.GetCredentials(auth.ServiceArgoCD)
        if err == nil && creds.Username != "" && creds.Password != "" {
            // Attempt to create a new session
            newToken, _, err := c.createSession(ctx, creds.Username, creds.Password)
            if err != nil {
                return nil, fmt.Errorf("failed to refresh ArgoCD token: %w", err)
            }
            
            // Update the credentials with the new token
            c.credentialProvider.UpdateArgoToken(ctx, newToken)
            
            // Retry the request with the new token
            return c.attemptRequest(ctx, method, endpoint, body)
        }
    }
    
    return resp, err
}

// attemptRequest makes a single request attempt
func (c *Client) attemptRequest(ctx context.Context, method, endpoint string, body io.Reader) (*http.Response, error) {
    // This contains the original doRequest logic
    u, err := url.Parse(c.baseURL)
    if err != nil {
        return nil, fmt.Errorf("invalid ArgoCD URL: %w", err)
    }
    u.Path = path.Join(u.Path, endpoint)

    req, err := http.NewRequestWithContext(ctx, method, u.String(), body)
    if err != nil {
        return nil, fmt.Errorf("failed to create request: %w", err)
    }

    if err := c.addAuth(req); err != nil {
        return nil, fmt.Errorf("failed to add authentication: %w", err)
    }

    req.Header.Set("Content-Type", "application/json")

    c.logger.Debug("Sending request to ArgoCD API", "method", method, "endpoint", endpoint)
    resp, err := c.httpClient.Do(req)
    if err != nil {
        return nil, fmt.Errorf("request failed: %w", err)
    }

    if resp.StatusCode >= 400 && resp.StatusCode != 401 {
        defer resp.Body.Close()
        body, _ := io.ReadAll(resp.Body)
        return nil, fmt.Errorf("ArgoCD API error (status %d): %s", resp.StatusCode, string(body))
    }

    return resp, nil
}

// createSession creates a new ArgoCD session
func (c *Client) createSession(ctx context.Context, username, password string) (string, time.Time, error) {
    // Create session request
    sessionReq := struct {
        Username string `json:"username"`
        Password string `json:"password"`
    }{
        Username: username,
        Password: password,
    }

    // Convert to JSON
    sessionReqBody, err := json.Marshal(sessionReq)
    if err != nil {
        return "", time.Time{}, fmt.Errorf("failed to marshal session request: %w", err)
    }

    // Create a new HTTP client without authentication for this request
    u, err := url.Parse(c.baseURL)
    if err != nil {
        return "", time.Time{}, fmt.Errorf("invalid ArgoCD URL: %w", err)
    }
    u.Path = path.Join(u.Path, "/api/v1/session")

    req, err := http.NewRequestWithContext(
        ctx,
        http.MethodPost,
        u.String(),
        bytes.NewReader(sessionReqBody),
    )
    if err != nil {
        return "", time.Time{}, fmt.Errorf("failed to create session request: %w", err)
    }

    req.Header.Set("Content-Type", "application/json")

    resp, err := c.httpClient.Do(req)
    if err != nil {
        return "", time.Time{}, fmt.Errorf("session request failed: %w", err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        body, _ := io.ReadAll(resp.Body)
        return "", time.Time{}, fmt.Errorf("failed to create session (status %d): %s", resp.StatusCode, string(body))
    }

    var sessionResp struct {
        Token string `json:"token"`
    }

    if err := json.NewDecoder(resp.Body).Decode(&sessionResp); err != nil {
        return "", time.Time{}, fmt.Errorf("failed to decode session response: %w", err)
    }

    // ArgoCD tokens will expire after 24 hours by default...
    expiry := time.Now().Add(24 * time.Hour)

    return sessionResp.Token, expiry, nil
}

// addAuth adds authentication to the request
func (c *Client) addAuth(req *http.Request) error {
    creds, err := c.credentialProvider.GetCredentials(auth.ServiceArgoCD)
    if err != nil {
        return fmt.Errorf("failed to get ArgoCD credentials: %w", err)
    }

    if creds.Token != "" {
        // Set both header formats that ArgoCD might accept
        req.Header.Set("Authorization", "Bearer "+creds.Token)
        req.Header.Set("Cookie", "argocd.token="+creds.Token)
        return nil
    }

    if creds.Username != "" && creds.Password != "" {
        // We need to get a session token first
        token, _, err := c.createSession(req.Context(), creds.Username, creds.Password)
        if err != nil {
            return fmt.Errorf("failed to create ArgoCD session: %w", err)
        }
        
        // Update credentials with the new token
        c.credentialProvider.UpdateArgoToken(req.Context(), token)
        
        // Set both header formats
        req.Header.Set("Authorization", "Bearer "+token)
        req.Header.Set("Cookie", "argocd.token="+token)
        return nil
    }

    return fmt.Errorf("no valid ArgoCD credentials available")
}

// refreshToken gets a new token using username/password credentials
func (c *Client) refreshToken(ctx context.Context) (string, time.Time, error) {
	creds, err := c.credentialProvider.GetCredentials(auth.ServiceArgoCD)
	if err != nil {
		return "", time.Time{}, fmt.Errorf("failed to get ArgoCD credentials: %w", err)
	}

	if creds.Username == "" || creds.Password == "" {
		return "", time.Time{}, fmt.Errorf("username/password required for token refresh")
	}

	// Create session request
	sessionReq := struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}{
		Username: creds.Username,
		Password: creds.Password,
	}

	// Convert to JSON
	sessionReqBody, err := json.Marshal(sessionReq)
	if err != nil {
		return "", time.Time{}, fmt.Errorf("failed to marshal session request: %w", err)
	}

	// Create a new HTTP client without authentication for this request
	req, err := http.NewRequestWithContext(
		ctx,
		http.MethodPost,
		fmt.Sprintf("%s/api/v1/session", c.baseURL),
		io.NopCloser(strings.NewReader(string(sessionReqBody))),
	)
	if err != nil {
		return "", time.Time{}, fmt.Errorf("failed to create session request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return "", time.Time{}, fmt.Errorf("session request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return "", time.Time{}, fmt.Errorf("failed to create session (status %d): %s", resp.StatusCode, string(body))
	}

	var sessionResp struct {
		Token string `json:"token"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&sessionResp); err != nil {
		return "", time.Time{}, fmt.Errorf("failed to decode session response: %w", err)
	}

	// ArgoCD tokens typically expire after 24 hours
	expiry := time.Now().Add(24 * time.Hour)

	return sessionResp.Token, expiry, nil
}

```

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

```go
package mcp

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/claude"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/correlator"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/k8s"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/utils"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

// ProtocolHandler handles the Model Context Protocol for Kubernetes
type ProtocolHandler struct {
	claudeClient     *claude.Client
	claudeProtocol   *claude.ProtocolHandler
	gitOpsCorrelator *correlator.GitOpsCorrelator
	k8sClient        *k8s.Client
	contextManager   *ContextManager
	promptGenerator  *PromptGenerator
	logger           *logging.Logger
}

// NewProtocolHandler creates a new MCP protocol handler
func NewProtocolHandler(
	claudeClient *claude.Client,
	gitOpsCorrelator *correlator.GitOpsCorrelator,
	k8sClient *k8s.Client,
	logger *logging.Logger,
) *ProtocolHandler {
	if logger == nil {
		logger = logging.NewLogger().Named("mcp")
	}

	return &ProtocolHandler{
		claudeClient:     claudeClient,
		claudeProtocol:   claude.NewProtocolHandler(claudeClient),
		gitOpsCorrelator: gitOpsCorrelator,
		k8sClient:        k8sClient,
		contextManager:   NewContextManager(100000, logger.Named("context")),
		promptGenerator:  NewPromptGenerator(logger.Named("prompt")),
		logger:           logger,
	}
}

// ProcessRequest processes an MCP request
func (h *ProtocolHandler) ProcessRequest(ctx context.Context, request *models.MCPRequest) (*models.MCPResponse, error) {
	startTime := time.Now()
	h.logger.Info("Processing MCP request", "action", request.Action)

	var resourceContext string
	var err error

	// Handle different types of queries
	switch request.Action {
	case "queryResource":
		// If we have pre-populated context, use it
		if request.Context != "" {
			resourceContext = request.Context
		} else {
			// Trace deployment for a specific resource
			resourceInfo, err := h.gitOpsCorrelator.TraceResourceDeployment(
				ctx,
				request.Namespace,
				request.Resource,
				request.Name,
			)
			if err != nil {
				return nil, fmt.Errorf("failed to trace resource deployment: %w", err)
			}

			// For non-namespace resources, enhance with the actual resource data
			if !strings.EqualFold(request.Resource, "namespace") {
				// Get the full resource details
				resource, err := h.k8sClient.GetResource(ctx, request.Resource, request.Namespace, request.Name)
				if err == nil && resource != nil {
					// Add the full resource details to the context
					resourceData, err := utils.ToJSON(resource.Object)
					if err == nil {
						resourceInfo.ResourceData = resourceData

						// Extract important deployment-specific information if available
						if strings.EqualFold(request.Resource, "deployment") {
							// Extract replicas info
							specReplicas, found, _ := unstructured.NestedInt64(resource.Object, "spec", "replicas")
							if found {
								if resourceInfo.Metadata == nil {
									resourceInfo.Metadata = make(map[string]interface{})
								}
								resourceInfo.Metadata["desiredReplicas"] = specReplicas
							}

							// Extract status replica counts
							statusReplicas, found, _ := unstructured.NestedInt64(resource.Object, "status", "replicas")
							if found {
								if resourceInfo.Metadata == nil {
									resourceInfo.Metadata = make(map[string]interface{})
								}
								resourceInfo.Metadata["currentReplicas"] = statusReplicas
							}

							// Extract readyReplicas
							readyReplicas, found, _ := unstructured.NestedInt64(resource.Object, "status", "readyReplicas")
							if found {
								if resourceInfo.Metadata == nil {
									resourceInfo.Metadata = make(map[string]interface{})
								}
								resourceInfo.Metadata["readyReplicas"] = readyReplicas
							}

							// Extract availableReplicas
							availableReplicas, found, _ := unstructured.NestedInt64(resource.Object, "status", "availableReplicas")
							if found {
								if resourceInfo.Metadata == nil {
									resourceInfo.Metadata = make(map[string]interface{})
								}
								resourceInfo.Metadata["availableReplicas"] = availableReplicas
							}

							// Extract container info
							containers, found, _ := unstructured.NestedSlice(resource.Object, "spec", "template", "spec", "containers")
							if found {
								var containerInfo []map[string]interface{}
								for _, c := range containers {
									container, ok := c.(map[string]interface{})
									if !ok {
										continue
									}

									containerData := map[string]interface{}{
										"name": container["name"],
									}

									if image, ok := container["image"].(string); ok {
										containerData["image"] = image
									}

									if resources, ok := container["resources"].(map[string]interface{}); ok {
										containerData["resources"] = resources
									}

									containerInfo = append(containerInfo, containerData)
								}

								if resourceInfo.Metadata == nil {
									resourceInfo.Metadata = make(map[string]interface{})
								}
								resourceInfo.Metadata["containers"] = containerInfo
							}
						}
					}
				}
			}

			formattedContext, err := h.contextManager.FormatResourceContext(resourceInfo)
			if err != nil {
				return nil, fmt.Errorf("failed to format resource context: %w", err)
			}

			resourceContext = formattedContext
		}

	case "queryMergeRequest":
		// Analyze merge request
		resources, err := h.gitOpsCorrelator.AnalyzeMergeRequest(
			ctx,
			request.ProjectID,
			request.MergeRequestIID,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to analyze merge request: %w", err)
		}

		resourceContext, err = h.contextManager.CombineContexts(ctx, resources)
		if err != nil {
			return nil, fmt.Errorf("failed to combine resource contexts: %w", err)
		}

	default:
		return nil, fmt.Errorf("unsupported action: %s", request.Action)
	}

	// Generate prompts for Claude
	h.logger.Debug("Generating prompts for Claude")
	systemPrompt := h.promptGenerator.GenerateSystemPrompt()
	userPrompt := h.promptGenerator.GenerateUserPrompt(resourceContext, request.Query)

	// Get completion from Claude
	h.logger.Debug("Sending request to Claude",
		"systemPromptLength", len(systemPrompt),
		"userPromptLength", len(userPrompt))

	analysis, err := h.claudeProtocol.GetCompletion(ctx, systemPrompt, userPrompt)
	if err != nil {
		return nil, fmt.Errorf("failed to get completion from Claude: %w", err)
	}

	// Build response
	response := &models.MCPResponse{
		Success:  true,
		Analysis: analysis,
		Message:  fmt.Sprintf("Successfully processed %s request in %v", request.Action, time.Since(startTime)),
	}

	h.logger.Info("MCP request processed successfully",
		"action", request.Action,
		"duration", time.Since(startTime),
		"responseLength", len(analysis))

	return response, nil
}

// ProcessTroubleshootRequest processes a troubleshooting request with detected issues
func (h *ProtocolHandler) ProcessTroubleshootRequest(ctx context.Context, request *models.MCPRequest, troubleshootResult *models.TroubleshootResult) (*models.MCPResponse, error) {
	startTime := time.Now()
	h.logger.Debug("Processing troubleshoot request")

	// Extract issues and recommendations
	var issuesText string
	for i, issue := range troubleshootResult.Issues {
		issuesText += fmt.Sprintf("%d. %s (%s): %s\n",
			i+1,
			issue.Title,
			issue.Severity,
			issue.Description)
	}

	var recommendationsText string
	for i, rec := range troubleshootResult.Recommendations {
		recommendationsText += fmt.Sprintf("%d. %s\n", i+1, rec)
	}

	// Create a prompt for Claude with the troubleshooting results
	userPrompt := fmt.Sprintf(
		"I'm troubleshooting a Kubernetes %s named '%s' in namespace '%s'.\n\n"+
			"The following issues were detected:\n%s\n"+
			"General recommendations:\n%s\n\n"+
			"Based on these detected issues, please provide specific kubectl commands "+
			"that I can use to troubleshoot and fix the problems. %s",
		request.Resource,
		request.Name,
		request.Namespace,
		issuesText,
		recommendationsText,
		request.Query)

	// Generate system prompt
	systemPrompt := h.promptGenerator.GenerateSystemPrompt()

	// Get Claude's analysis
	h.logger.Debug("Sending troubleshoot request to Claude",
		"systemPromptLength", len(systemPrompt),
		"userPromptLength", len(userPrompt))

	analysis, err := h.claudeProtocol.GetCompletion(ctx, systemPrompt, userPrompt)
	if err != nil {
		return nil, fmt.Errorf("failed to get completion for troubleshoot request: %w", err)
	}

	// Create response
	response := &models.MCPResponse{
		Success:  true,
		Analysis: analysis,
		Message:  fmt.Sprintf("Successfully processed troubleshoot request in %v", time.Since(startTime)),
	}

	h.logger.Info("Troubleshoot request processed successfully",
		"duration", time.Since(startTime),
		"responseLength", len(analysis))

	return response, nil
}

// WithCustomPrompt sets a custom base prompt template
func (h *ProtocolHandler) WithCustomPrompt(template string) *ProtocolHandler {
	h.promptGenerator.WithBasePrompt(template)
	return h
}

// WithMaxContextSize sets the maximum context size
func (h *ProtocolHandler) WithMaxContextSize(size int) *ProtocolHandler {
	h.contextManager = NewContextManager(size, h.logger.Named("context"))
	return h
}

```

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

```go
package mcp

import (
    "context"
    "fmt"
    "strings"
    "time"

    "github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
    k8s "github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/k8s"
)

// NamespaceAnalysisResult contains the analysis of a namespace's resources
type NamespaceAnalysisResult struct {
	Namespace           string                     `json:"namespace"`
	ResourceCounts      map[string]int             `json:"resourceCounts"`
	HealthStatus        map[string]map[string]int  `json:"healthStatus"`
	ResourceRelationships []k8s.ResourceRelationship `json:"resourceRelationships"`
	Issues              []models.Issue             `json:"issues"`
	Recommendations     []string                   `json:"recommendations"`
	Analysis            string                     `json:"analysis"`
}

// AnalyzeNamespace analyzes all resources in a namespace using Claude
func (h *ProtocolHandler) AnalyzeNamespace(ctx context.Context, namespace string) (*models.NamespaceAnalysisResult, error) {
    startTime := time.Now()
    h.logger.Info("Analyzing namespace", "namespace", namespace)
    
    // Get namespace topology
    topology, err := h.k8sClient.GetNamespaceTopology(ctx, namespace)
    if err != nil {
        return nil, fmt.Errorf("failed to get namespace topology: %w", err)
    }
    
    // Initialize result
    result := &models.NamespaceAnalysisResult{
        Namespace:      namespace,
        ResourceCounts: make(map[string]int),
        HealthStatus:   make(map[string]map[string]int),
        Issues:         []models.Issue{},
        Recommendations: []string{},
    }
    
    // Extract resource counts
    for kind, resources := range topology.Resources {
        result.ResourceCounts[kind] = len(resources)
    }
    
    // Extract health status
    for kind, statusMap := range topology.Health {
        healthCounts := make(map[string]int)
        for _, status := range statusMap {
            healthCounts[status]++
        }
        result.HealthStatus[kind] = healthCounts
    }
    
    // Add relationships - Convert from k8s.ResourceRelationship to models.ResourceRelationship
    for _, rel := range topology.Relationships {
        modelRel := models.ResourceRelationship{
            SourceKind:      rel.SourceKind,
            SourceName:      rel.SourceName,
            SourceNamespace: rel.SourceNamespace,
            TargetKind:      rel.TargetKind,
            TargetName:      rel.TargetName,
            TargetNamespace: rel.TargetNamespace,
            RelationType:    rel.RelationType,
        }
        result.ResourceRelationships = append(result.ResourceRelationships, modelRel)
    }
	
	// Get events for the namespace
	events, err := h.k8sClient.GetNamespaceEvents(ctx, namespace)
	if err != nil {
		h.logger.Warn("Failed to get namespace events", "error", err)
	}
	
	// Identify issues from events
	for _, event := range events {
		if event.Type == "Warning" {
			issue := models.Issue{
				Source:      "Kubernetes",
				Severity:    "Warning",
				Description: fmt.Sprintf("%s: %s", event.Reason, event.Message),
			}
			
			// Categorize common issues
			switch {
			case strings.Contains(event.Reason, "Failed") && strings.Contains(event.Message, "ImagePull"):
				issue.Category = "ImagePullError"
				issue.Title = "Image Pull Failure"
			
			case strings.Contains(event.Reason, "Unhealthy"):
				issue.Category = "HealthCheckFailure"
				issue.Title = "Health Check Failure"
			
			case strings.Contains(event.Message, "memory"):
				issue.Category = "ResourceIssue"
				issue.Title = "Memory Resource Issue"
				
			case strings.Contains(event.Message, "cpu"):
				issue.Category = "ResourceIssue"
				issue.Title = "CPU Resource Issue"
				
			case strings.Contains(event.Reason, "BackOff"):
				issue.Category = "CrashLoopBackOff"
				issue.Title = "Container Crash Loop"
				
			default:
				issue.Category = "OtherWarning"
				issue.Title = "Kubernetes Warning"
			}
			
			result.Issues = append(result.Issues, issue)
		}
	}
	
	// Generate Claude analysis
	analysisPrompt := h.generateNamespaceAnalysisPrompt(namespace, topology, events)
	systemPrompt := h.promptGenerator.GenerateSystemPrompt()
	
	h.logger.Debug("Sending namespace analysis request to Claude",
		"namespace", namespace,
		"systemPromptLength", len(systemPrompt),
		"analysisPromptLength", len(analysisPrompt))
	
	analysis, err := h.claudeProtocol.GetCompletion(ctx, systemPrompt, analysisPrompt)
	if err != nil {
		return nil, fmt.Errorf("failed to get completion for namespace analysis: %w", err)
	}
	
	// Extract recommendations from analysis
	lines := strings.Split(analysis, "\n")
	inRecommendations := false
	
	for _, line := range lines {
		if strings.Contains(strings.ToLower(line), "recommendation") || 
		   strings.Contains(strings.ToLower(line), "recommendations") || 
		   strings.Contains(strings.ToLower(line), "suggest") {
			inRecommendations = true
			continue
		}
		
		if inRecommendations && strings.TrimSpace(line) != "" && !strings.HasPrefix(line, "#") {
			// Remove leading dash or number if it exists
			cleanLine := strings.TrimSpace(line)
			if strings.HasPrefix(cleanLine, "- ") {
				cleanLine = cleanLine[2:]
			} else if len(cleanLine) > 2 && strings.HasPrefix(cleanLine, "* ") {
				cleanLine = cleanLine[2:]
			} else if len(cleanLine) > 3 && 
			           ((cleanLine[0] >= '1' && cleanLine[0] <= '9') && 
			           (cleanLine[1] == '.' || cleanLine[1] == ')') && 
			           (cleanLine[2] == ' ')) {
				cleanLine = cleanLine[3:]
			}
			
			if cleanLine != "" && len(result.Recommendations) < 10 {
				result.Recommendations = append(result.Recommendations, cleanLine)
			}
		}
	}
	
	result.Analysis = analysis
	
	h.logger.Info("Namespace analysis completed", 
		"namespace", namespace,
		"duration", time.Since(startTime),
		"issueCount", len(result.Issues),
		"recommendationCount", len(result.Recommendations))
	
	return result, nil
}

// generateNamespaceAnalysisPrompt creates a prompt for namespace analysis
func (h *ProtocolHandler) generateNamespaceAnalysisPrompt(namespace string, topology *k8s.NamespaceTopology, events []models.K8sEvent) string {
	// Start with namespace overview
	prompt := fmt.Sprintf("# Namespace Analysis: %s\n\n", namespace)
	
	// Add resource summary
	prompt += "## Resource Summary\n\n"
	for kind, resources := range topology.Resources {
		prompt += fmt.Sprintf("- %s: %d resources\n", kind, len(resources))
	}
	prompt += "\n"
	
	// Add health status summary
	prompt += "## Health Status\n\n"
	for kind, statusMap := range topology.Health {
		prompt += fmt.Sprintf("### %s Health\n", kind)
		
		// Count the statuses
		healthCounts := make(map[string]int)
		for _, status := range statusMap {
			healthCounts[status]++
		}
		
		// List the counts
		for status, count := range healthCounts {
			prompt += fmt.Sprintf("- %s: %d resources\n", status, count)
		}
		
		// List unhealthy resources
		unhealthyResources := []string{}
		for name, status := range statusMap {
			if status == "unhealthy" {
				unhealthyResources = append(unhealthyResources, name)
			}
		}
		
		if len(unhealthyResources) > 0 {
			prompt += "\nUnhealthy resources:\n"
			for _, name := range unhealthyResources {
				prompt += fmt.Sprintf("- %s\n", name)
			}
		}
		
		prompt += "\n"
	}
	
	// Add relationship summary
	if len(topology.Relationships) > 0 {
		prompt += "## Resource Relationships\n\n"
		
		// Group by relationship type
		relationshipsByType := make(map[string][]string)
		for _, rel := range topology.Relationships {
			key := rel.RelationType
			relationshipsByType[key] = append(
				relationshipsByType[key], 
				fmt.Sprintf("%s/%s -> %s/%s", 
					rel.SourceKind, rel.SourceName,
					rel.TargetKind, rel.TargetName))
		}
		
		// List relationships by type
		for relType, relations := range relationshipsByType {
			prompt += fmt.Sprintf("### %s Relationships\n", strings.Title(relType))
			for _, rel := range relations {
				prompt += fmt.Sprintf("- %s\n", rel)
			}
			prompt += "\n"
		}
	}
	
	// Add recent events
	if len(events) > 0 {
		prompt += "## Recent Events\n\n"
		
		// Group events by type
		warningEvents := []models.K8sEvent{}
		normalEvents := []models.K8sEvent{}
		
		for _, event := range events {
			if event.Type == "Warning" {
				warningEvents = append(warningEvents, event)
			} else {
				normalEvents = append(normalEvents, event)
			}
		}
		
		// Add warning events first (limited to 10)
		if len(warningEvents) > 0 {
			prompt += "### Warning Events\n"
			count := 0
			for _, event := range warningEvents {
				if count >= 10 {
					break
				}
				prompt += fmt.Sprintf("- [%s] %s: %s (%s)\n", 
					event.LastTime.Format(time.RFC3339),
					event.Reason,
					event.Message,
					fmt.Sprintf("%s/%s", event.Object.Kind, event.Object.Name))
				count++
			}
			prompt += "\n"
		}
		
		// Add a few normal events (limited to 5)
		if len(normalEvents) > 0 {
			prompt += "### Normal Events\n"
			count := 0
			for _, event := range normalEvents {
				if count >= 5 {
					break
				}
				prompt += fmt.Sprintf("- [%s] %s: %s (%s)\n", 
					event.LastTime.Format(time.RFC3339),
					event.Reason,
					event.Message,
					fmt.Sprintf("%s/%s", event.Object.Kind, event.Object.Name))
				count++
			}
			prompt += "\n"
		}
	}
	
	// Add analysis request
	prompt += "## Analysis Request\n\n"
	prompt += "Based on the information above, please provide a comprehensive analysis of this Kubernetes namespace, including:\n\n"
	prompt += "1. Overall health assessment\n"
	prompt += "2. Identification of any issues or problems\n"
	prompt += "3. Analysis of resource relationships and dependencies\n"
	prompt += "4. Potential bottlenecks or misconfigurations\n"
	prompt += "5. Security concerns (if any can be identified)\n"
	prompt += "6. Specific recommendations for improvement\n\n"
	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."
	
	return prompt
}

```
Page 2/5FirstPrevNextLast