mirror of
https://github.com/k8sgpt-ai/k8sgpt.git
synced 2026-03-18 19:17:25 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7b7a5db47 | ||
|
|
5480051230 |
@@ -1 +1 @@
|
||||
{".":"0.4.26"}
|
||||
{".":"0.4.27"}
|
||||
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## [0.4.27](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.4.26...v0.4.27) (2025-12-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* mcp v2 ([#1589](https://github.com/k8sgpt-ai/k8sgpt/issues/1589)) ([5480051](https://github.com/k8sgpt-ai/k8sgpt/commit/5480051230ce83b89c0382abd7992c7ecc4a85b8))
|
||||
|
||||
## [0.4.26](https://github.com/k8sgpt-ai/k8sgpt/compare/v0.4.25...v0.4.26) (2025-10-16)
|
||||
|
||||
|
||||
|
||||
482
MCP.md
Normal file
482
MCP.md
Normal file
@@ -0,0 +1,482 @@
|
||||
# K8sGPT Model Context Protocol (MCP) Server
|
||||
|
||||
K8sGPT provides a Model Context Protocol (MCP) server that exposes Kubernetes cluster operations as standardized tools, resources, and prompts for AI assistants like Claude, ChatGPT, and other MCP-compatible clients.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [What is MCP?](#what-is-mcp)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Server Modes](#server-modes)
|
||||
- [Available Tools](#available-tools)
|
||||
- [Available Resources](#available-resources)
|
||||
- [Available Prompts](#available-prompts)
|
||||
- [Usage Examples](#usage-examples)
|
||||
- [Integration with AI Assistants](#integration-with-ai-assistants)
|
||||
- [HTTP API Reference](#http-api-reference)
|
||||
|
||||
## What is MCP?
|
||||
|
||||
The Model Context Protocol (MCP) is an open standard that enables AI assistants to securely connect to external data sources and tools. K8sGPT's MCP server exposes Kubernetes operations through this standardized interface, allowing AI assistants to:
|
||||
|
||||
- Analyze cluster health and issues
|
||||
- Query Kubernetes resources
|
||||
- Access pod logs and events
|
||||
- Get troubleshooting guidance
|
||||
- Manage analyzer filters
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Start the MCP Server
|
||||
|
||||
**Stdio mode (for local AI assistants):**
|
||||
```bash
|
||||
k8sgpt serve --mcp
|
||||
```
|
||||
|
||||
**HTTP mode (for network access):**
|
||||
```bash
|
||||
k8sgpt serve --mcp --mcp-http --mcp-port 8089
|
||||
```
|
||||
|
||||
### Test with curl
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8089/mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "tools/list"
|
||||
}'
|
||||
```
|
||||
|
||||
## Server Modes
|
||||
|
||||
### Stdio Mode (Default)
|
||||
|
||||
Used by local AI assistants like Claude Desktop:
|
||||
|
||||
```bash
|
||||
k8sgpt serve --mcp
|
||||
```
|
||||
|
||||
Configure in your MCP client (e.g., Claude Desktop's `claude_desktop_config.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"k8sgpt": {
|
||||
"command": "k8sgpt",
|
||||
"args": ["serve", "--mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP Mode
|
||||
|
||||
Used for network access and webhooks:
|
||||
|
||||
```bash
|
||||
k8sgpt serve --mcp --mcp-http --mcp-port 8089
|
||||
```
|
||||
|
||||
The server runs in stateless mode, so no session management is required. Each request is independent.
|
||||
|
||||
## Available Tools
|
||||
|
||||
The MCP server exposes 12 tools for Kubernetes operations:
|
||||
|
||||
### Cluster Analysis
|
||||
|
||||
**analyze**
|
||||
- Analyze Kubernetes resources for issues and problems
|
||||
- Parameters:
|
||||
- `namespace` (optional): Namespace to analyze
|
||||
- `explain` (optional): Get AI explanations for issues
|
||||
- `filters` (optional): Comma-separated list of analyzers to use
|
||||
|
||||
**cluster-info**
|
||||
- Get Kubernetes cluster information and version
|
||||
|
||||
### Resource Management
|
||||
|
||||
**list-resources**
|
||||
- List Kubernetes resources of a specific type
|
||||
- Parameters:
|
||||
- `resourceType` (required): Type of resource (pods, deployments, services, nodes, jobs, cronjobs, statefulsets, daemonsets, replicasets, configmaps, secrets, ingresses, pvcs, pvs)
|
||||
- `namespace` (optional): Namespace to query
|
||||
- `labelSelector` (optional): Label selector for filtering
|
||||
|
||||
**get-resource**
|
||||
- Get detailed information about a specific Kubernetes resource
|
||||
- Parameters:
|
||||
- `resourceType` (required): Type of resource
|
||||
- `name` (required): Resource name
|
||||
- `namespace` (optional): Namespace
|
||||
|
||||
**list-namespaces**
|
||||
- List all namespaces in the cluster
|
||||
|
||||
### Debugging and Troubleshooting
|
||||
|
||||
**get-logs**
|
||||
- Get logs from a pod container
|
||||
- Parameters:
|
||||
- `podName` (required): Name of the pod
|
||||
- `namespace` (optional): Namespace
|
||||
- `container` (optional): Container name
|
||||
- `tail` (optional): Number of lines to show
|
||||
- `previous` (optional): Show logs from previous container instance
|
||||
- `sinceSeconds` (optional): Show logs from last N seconds
|
||||
|
||||
**list-events**
|
||||
- List Kubernetes events for debugging
|
||||
- Parameters:
|
||||
- `namespace` (optional): Namespace to query
|
||||
- `involvedObjectName` (optional): Filter by object name
|
||||
- `involvedObjectKind` (optional): Filter by object kind
|
||||
|
||||
### Analyzer Management
|
||||
|
||||
**list-filters**
|
||||
- List all available and active analyzers/filters
|
||||
|
||||
**add-filters**
|
||||
- Add filters to enable specific analyzers
|
||||
- Parameters:
|
||||
- `filters` (required): Comma-separated list of analyzer names
|
||||
|
||||
**remove-filters**
|
||||
- Remove filters to disable specific analyzers
|
||||
- Parameters:
|
||||
- `filters` (required): Comma-separated list of analyzer names
|
||||
|
||||
### Integrations
|
||||
|
||||
**list-integrations**
|
||||
- List available integrations (Prometheus, AWS, Keda, Kyverno, etc.)
|
||||
|
||||
### Configuration
|
||||
|
||||
**config**
|
||||
- Configure K8sGPT settings including custom analyzers and cache
|
||||
|
||||
## Available Resources
|
||||
|
||||
Resources provide read-only access to cluster information:
|
||||
|
||||
**cluster-info**
|
||||
- URI: `cluster-info`
|
||||
- Get information about the Kubernetes cluster
|
||||
|
||||
**namespaces**
|
||||
- URI: `namespaces`
|
||||
- List all namespaces in the cluster
|
||||
|
||||
**active-filters**
|
||||
- URI: `active-filters`
|
||||
- Get currently active analyzers/filters
|
||||
|
||||
## Available Prompts
|
||||
|
||||
Prompts provide guided troubleshooting workflows:
|
||||
|
||||
**troubleshoot-pod**
|
||||
- Interactive pod debugging workflow
|
||||
- Arguments:
|
||||
- `podName` (required): Name of the pod to troubleshoot
|
||||
- `namespace` (required): Namespace of the pod
|
||||
|
||||
**troubleshoot-deployment**
|
||||
- Interactive deployment debugging workflow
|
||||
- Arguments:
|
||||
- `deploymentName` (required): Name of the deployment
|
||||
- `namespace` (required): Namespace of the deployment
|
||||
|
||||
**troubleshoot-cluster**
|
||||
- General cluster troubleshooting workflow
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Example 1: Analyze a Namespace
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8089/mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "analyze",
|
||||
"arguments": {
|
||||
"namespace": "production",
|
||||
"explain": "true"
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Example 2: List Pods
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8089/mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "list-resources",
|
||||
"arguments": {
|
||||
"resourceType": "pods",
|
||||
"namespace": "default"
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Example 3: Get Pod Logs
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8089/mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "get-logs",
|
||||
"arguments": {
|
||||
"podName": "nginx-abc123",
|
||||
"namespace": "default",
|
||||
"tail": "100"
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Example 4: Access a Resource
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8089/mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"method": "resources/read",
|
||||
"params": {
|
||||
"uri": "namespaces"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Example 5: Get a Troubleshooting Prompt
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8089/mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"method": "prompts/get",
|
||||
"params": {
|
||||
"name": "troubleshoot-pod",
|
||||
"arguments": {
|
||||
"podName": "nginx-abc123",
|
||||
"namespace": "default"
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Integration with AI Assistants
|
||||
|
||||
### Claude Desktop
|
||||
|
||||
Add to `claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"k8sgpt": {
|
||||
"command": "k8sgpt",
|
||||
"args": ["serve", "--mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Restart Claude Desktop and you'll see k8sgpt tools available in the tool selector.
|
||||
|
||||
### Custom MCP Clients
|
||||
|
||||
Any MCP-compatible client can connect to the k8sgpt server. For HTTP-based clients:
|
||||
|
||||
1. Start the server: `k8sgpt serve --mcp --mcp-http --mcp-port 8089`
|
||||
2. Connect to: `http://localhost:8089/mcp`
|
||||
3. Use standard MCP protocol methods: `tools/list`, `tools/call`, `resources/read`, `prompts/get`
|
||||
|
||||
## HTTP API Reference
|
||||
|
||||
### Endpoint
|
||||
|
||||
```
|
||||
POST http://localhost:8089/mcp
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### Request Format
|
||||
|
||||
All requests follow the JSON-RPC 2.0 format:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "method_name",
|
||||
"params": {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Discovery Methods
|
||||
|
||||
**List Tools**
|
||||
```json
|
||||
{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
|
||||
```
|
||||
|
||||
**List Resources**
|
||||
```json
|
||||
{"jsonrpc": "2.0", "id": 2, "method": "resources/list"}
|
||||
```
|
||||
|
||||
**List Prompts**
|
||||
```json
|
||||
{"jsonrpc": "2.0", "id": 3, "method": "prompts/list"}
|
||||
```
|
||||
|
||||
### Tool Invocation
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "tool_name",
|
||||
"arguments": {
|
||||
"arg1": "value1",
|
||||
"arg2": "value2"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Resource Access
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"method": "resources/read",
|
||||
"params": {
|
||||
"uri": "resource_uri"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Prompt Access
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 6,
|
||||
"method": "prompts/get",
|
||||
"params": {
|
||||
"name": "prompt_name",
|
||||
"arguments": {
|
||||
"arg1": "value1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Response Format
|
||||
|
||||
Successful responses:
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Error responses:
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"error": {
|
||||
"code": -32600,
|
||||
"message": "Error description"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Custom Port
|
||||
|
||||
```bash
|
||||
k8sgpt serve --mcp --mcp-http --mcp-port 9000
|
||||
```
|
||||
|
||||
### With Specific Backend
|
||||
|
||||
```bash
|
||||
k8sgpt serve --mcp --backend openai
|
||||
```
|
||||
|
||||
### With Kubeconfig
|
||||
|
||||
```bash
|
||||
k8sgpt serve --mcp --kubeconfig ~/.kube/config
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Issues
|
||||
|
||||
Verify the server is running:
|
||||
```bash
|
||||
curl http://localhost:8089/mcp
|
||||
```
|
||||
|
||||
### Permission Issues
|
||||
|
||||
Ensure your kubeconfig has appropriate cluster access:
|
||||
```bash
|
||||
kubectl cluster-info
|
||||
```
|
||||
|
||||
### Tool Errors
|
||||
|
||||
List available tools to verify names:
|
||||
```bash
|
||||
curl -X POST http://localhost:8089/mcp \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'
|
||||
```
|
||||
|
||||
## Learn More
|
||||
|
||||
- [MCP Specification](https://modelcontextprotocol.io/)
|
||||
- [K8sGPT Documentation](https://docs.k8sgpt.ai/)
|
||||
- [MCP Go Library](https://github.com/mark3labs/mcp-go)
|
||||
36
README.md
36
README.md
@@ -34,6 +34,7 @@ _Out of the box integration with OpenAI, Azure, Cohere, Amazon Bedrock, Google G
|
||||
- [Examples](#examples)
|
||||
- [LLM AI Backends](#llm-ai-backends)
|
||||
- [Key Features](#key-features)
|
||||
- [Model Context Protocol (MCP)](#model-context-protocol-mcp)
|
||||
- [Documentation](#documentation)
|
||||
- [Contributing](#contributing)
|
||||
- [Community](#community)
|
||||
@@ -62,7 +63,7 @@ brew install k8sgpt
|
||||
<!---x-release-please-start-version-->
|
||||
|
||||
```
|
||||
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.26/k8sgpt_386.rpm
|
||||
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_386.rpm
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
|
||||
@@ -70,7 +71,7 @@ brew install k8sgpt
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.26/k8sgpt_amd64.rpm
|
||||
sudo rpm -ivh https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_amd64.rpm
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
</details>
|
||||
@@ -83,7 +84,7 @@ brew install k8sgpt
|
||||
<!---x-release-please-start-version-->
|
||||
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.26/k8sgpt_386.deb
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_386.deb
|
||||
sudo dpkg -i k8sgpt_386.deb
|
||||
```
|
||||
|
||||
@@ -94,7 +95,7 @@ sudo dpkg -i k8sgpt_386.deb
|
||||
<!---x-release-please-start-version-->
|
||||
|
||||
```
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.26/k8sgpt_amd64.deb
|
||||
curl -LO https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_amd64.deb
|
||||
sudo dpkg -i k8sgpt_amd64.deb
|
||||
```
|
||||
|
||||
@@ -109,7 +110,7 @@ sudo dpkg -i k8sgpt_amd64.deb
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.26/k8sgpt_386.apk
|
||||
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_386.apk
|
||||
apk add --allow-untrusted k8sgpt_386.apk
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
@@ -118,7 +119,7 @@ sudo dpkg -i k8sgpt_amd64.deb
|
||||
|
||||
<!---x-release-please-start-version-->
|
||||
```
|
||||
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.26/k8sgpt_amd64.apk
|
||||
wget https://github.com/k8sgpt-ai/k8sgpt/releases/download/v0.4.27/k8sgpt_amd64.apk
|
||||
apk add --allow-untrusted k8sgpt_amd64.apk
|
||||
```
|
||||
<!---x-release-please-end-->
|
||||
@@ -699,7 +700,30 @@ k8sgpt custom-analyzer remove --names "my-custom-analyzer,my-custom-analyzer-2"
|
||||
```
|
||||
|
||||
</details>
|
||||
## Model Context Protocol (MCP)
|
||||
|
||||
K8sGPT provides a Model Context Protocol server that exposes Kubernetes operations as standardized tools for AI assistants like Claude, ChatGPT, and other MCP-compatible clients.
|
||||
|
||||
**Start the MCP server:**
|
||||
|
||||
Stdio mode (for local AI assistants):
|
||||
```bash
|
||||
k8sgpt serve --mcp
|
||||
```
|
||||
|
||||
HTTP mode (for network access):
|
||||
```bash
|
||||
k8sgpt serve --mcp --mcp-http --mcp-port 8089
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- 12 tools for cluster analysis, resource management, and debugging
|
||||
- 3 resources for cluster information access
|
||||
- 3 interactive troubleshooting prompts
|
||||
- Stateless HTTP mode for one-off invocations
|
||||
- Full integration with Claude Desktop and other MCP clients
|
||||
|
||||
**Learn more:** See [MCP.md](MCP.md) for complete documentation, usage examples, and integration guides.
|
||||
## Documentation
|
||||
|
||||
Find our official documentation available [here](https://docs.k8sgpt.ai)
|
||||
|
||||
2
go.mod
2
go.mod
@@ -41,6 +41,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.14
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.33.0
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.30.0
|
||||
github.com/aws/smithy-go v1.22.2
|
||||
github.com/cohere-ai/cohere-go/v2 v2.12.2
|
||||
github.com/go-logr/zapr v1.3.0
|
||||
github.com/google/generative-ai-go v0.19.0
|
||||
@@ -92,7 +93,6 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect
|
||||
github.com/aws/smithy-go v1.22.2 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// K8sGptMCPServer represents an MCP server for k8sgpt
|
||||
@@ -67,6 +68,8 @@ func NewMCPServer(port string, aiProvider *ai.AIProvider, useHTTP bool, logger *
|
||||
// Create HTTP server with streamable transport
|
||||
httpOpts := []server.StreamableHTTPOption{
|
||||
server.WithLogger(&zapLoggerAdapter{logger: logger}),
|
||||
// Enable stateless mode for one-off tool invocations without session management
|
||||
server.WithStateLess(true),
|
||||
}
|
||||
|
||||
httpServer := server.NewStreamableHTTPServer(mcpServer, httpOpts...)
|
||||
@@ -218,6 +221,122 @@ func (s *K8sGptMCPServer) registerToolsAndResources() error {
|
||||
),
|
||||
)
|
||||
s.server.AddTool(configTool, s.handleConfig)
|
||||
|
||||
// Register resource listing tools
|
||||
listResourcesTool := mcp.NewTool("list-resources",
|
||||
mcp.WithDescription("List Kubernetes resources of a specific type (pods, deployments, services, nodes, etc.)"),
|
||||
mcp.WithString("resourceType",
|
||||
mcp.Required(),
|
||||
mcp.Description("Type of resource to list (e.g., pods, deployments, services, nodes, jobs, etc.)"),
|
||||
),
|
||||
mcp.WithString("namespace",
|
||||
mcp.Description("Namespace to list resources from (empty for all or cluster-scoped resources)"),
|
||||
),
|
||||
mcp.WithString("labelSelector",
|
||||
mcp.Description("Label selector to filter resources (e.g., 'app=myapp')"),
|
||||
),
|
||||
)
|
||||
s.server.AddTool(listResourcesTool, s.handleListResources)
|
||||
|
||||
// Register get resource tool
|
||||
getResourceTool := mcp.NewTool("get-resource",
|
||||
mcp.WithDescription("Get detailed information about a specific Kubernetes resource"),
|
||||
mcp.WithString("resourceType",
|
||||
mcp.Required(),
|
||||
mcp.Description("Type of resource (e.g., pod, deployment, service)"),
|
||||
),
|
||||
mcp.WithString("name",
|
||||
mcp.Required(),
|
||||
mcp.Description("Name of the resource"),
|
||||
),
|
||||
mcp.WithString("namespace",
|
||||
mcp.Description("Namespace of the resource (required for namespaced resources)"),
|
||||
),
|
||||
)
|
||||
s.server.AddTool(getResourceTool, s.handleGetResource)
|
||||
|
||||
// Register list namespaces tool
|
||||
listNamespacesTool := mcp.NewTool("list-namespaces",
|
||||
mcp.WithDescription("List all namespaces in the cluster"),
|
||||
)
|
||||
s.server.AddTool(listNamespacesTool, s.handleListNamespaces)
|
||||
|
||||
// Register list events tool
|
||||
listEventsTool := mcp.NewTool("list-events",
|
||||
mcp.WithDescription("List Kubernetes events for debugging and troubleshooting"),
|
||||
mcp.WithString("namespace",
|
||||
mcp.Description("Namespace to list events from (empty for all namespaces)"),
|
||||
),
|
||||
mcp.WithString("involvedObjectName",
|
||||
mcp.Description("Filter events by involved object name (e.g., pod name)"),
|
||||
),
|
||||
mcp.WithString("involvedObjectKind",
|
||||
mcp.Description("Filter events by involved object kind (e.g., Pod, Deployment)"),
|
||||
),
|
||||
mcp.WithNumber("limit",
|
||||
mcp.Description("Maximum number of events to return (default: 100)"),
|
||||
),
|
||||
)
|
||||
s.server.AddTool(listEventsTool, s.handleListEvents)
|
||||
|
||||
// Register get logs tool
|
||||
getLogsTool := mcp.NewTool("get-logs",
|
||||
mcp.WithDescription("Get logs from a pod container"),
|
||||
mcp.WithString("podName",
|
||||
mcp.Required(),
|
||||
mcp.Description("Name of the pod"),
|
||||
),
|
||||
mcp.WithString("namespace",
|
||||
mcp.Required(),
|
||||
mcp.Description("Namespace of the pod"),
|
||||
),
|
||||
mcp.WithString("container",
|
||||
mcp.Description("Container name (if pod has multiple containers)"),
|
||||
),
|
||||
mcp.WithBoolean("previous",
|
||||
mcp.Description("Get logs from previous terminated container"),
|
||||
),
|
||||
mcp.WithNumber("tailLines",
|
||||
mcp.Description("Number of lines from the end of logs (default: 100)"),
|
||||
),
|
||||
mcp.WithNumber("sinceSeconds",
|
||||
mcp.Description("Return logs newer than this many seconds"),
|
||||
),
|
||||
)
|
||||
s.server.AddTool(getLogsTool, s.handleGetLogs)
|
||||
|
||||
// Register filter management tools
|
||||
listFiltersTool := mcp.NewTool("list-filters",
|
||||
mcp.WithDescription("List all available and active analyzers/filters in k8sgpt"),
|
||||
)
|
||||
s.server.AddTool(listFiltersTool, s.handleListFilters)
|
||||
|
||||
addFiltersTool := mcp.NewTool("add-filters",
|
||||
mcp.WithDescription("Add filters to enable specific analyzers"),
|
||||
mcp.WithArray("filters",
|
||||
mcp.Required(),
|
||||
mcp.Description("List of filter names to add (e.g., ['Pod', 'Service', 'Deployment'])"),
|
||||
mcp.WithStringItems(),
|
||||
),
|
||||
)
|
||||
s.server.AddTool(addFiltersTool, s.handleAddFilters)
|
||||
|
||||
removeFiltersTool := mcp.NewTool("remove-filters",
|
||||
mcp.WithDescription("Remove filters to disable specific analyzers"),
|
||||
mcp.WithArray("filters",
|
||||
mcp.Required(),
|
||||
mcp.Description("List of filter names to remove"),
|
||||
mcp.WithStringItems(),
|
||||
),
|
||||
)
|
||||
s.server.AddTool(removeFiltersTool, s.handleRemoveFilters)
|
||||
|
||||
// Register integration management tools
|
||||
listIntegrationsTool := mcp.NewTool("list-integrations",
|
||||
mcp.WithDescription("List available integrations (Prometheus, AWS, Keda, Kyverno, etc.)"),
|
||||
)
|
||||
s.server.AddTool(listIntegrationsTool, s.handleListIntegrations)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -453,7 +572,26 @@ func (s *K8sGptMCPServer) handleConfig(ctx context.Context, request mcp.CallTool
|
||||
|
||||
// registerPrompts registers the prompts for the MCP server
|
||||
func (s *K8sGptMCPServer) registerPrompts() error {
|
||||
// Register any prompts needed for the MCP server
|
||||
// Register troubleshooting prompts
|
||||
podTroubleshootPrompt := mcp.NewPrompt("troubleshoot-pod",
|
||||
mcp.WithPromptDescription("Guide for troubleshooting pod issues in Kubernetes"),
|
||||
mcp.WithArgument("podName"),
|
||||
mcp.WithArgument("namespace"),
|
||||
)
|
||||
s.server.AddPrompt(podTroubleshootPrompt, s.getTroubleshootPodPrompt)
|
||||
|
||||
deploymentTroubleshootPrompt := mcp.NewPrompt("troubleshoot-deployment",
|
||||
mcp.WithPromptDescription("Guide for troubleshooting deployment issues in Kubernetes"),
|
||||
mcp.WithArgument("deploymentName"),
|
||||
mcp.WithArgument("namespace"),
|
||||
)
|
||||
s.server.AddPrompt(deploymentTroubleshootPrompt, s.getTroubleshootDeploymentPrompt)
|
||||
|
||||
generalTroubleshootPrompt := mcp.NewPrompt("troubleshoot-cluster",
|
||||
mcp.WithPromptDescription("General guide for troubleshooting Kubernetes cluster issues"),
|
||||
)
|
||||
s.server.AddPrompt(generalTroubleshootPrompt, s.getTroubleshootClusterPrompt)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -463,8 +601,20 @@ func (s *K8sGptMCPServer) registerResources() error {
|
||||
mcp.WithResourceDescription("Get information about the Kubernetes cluster"),
|
||||
mcp.WithMIMEType("application/json"),
|
||||
)
|
||||
|
||||
s.server.AddResource(clusterInfoResource, s.getClusterInfo)
|
||||
|
||||
namespacesResource := mcp.NewResource("namespaces", "namespaces",
|
||||
mcp.WithResourceDescription("List all namespaces in the cluster"),
|
||||
mcp.WithMIMEType("application/json"),
|
||||
)
|
||||
s.server.AddResource(namespacesResource, s.getNamespacesResource)
|
||||
|
||||
activeFiltersResource := mcp.NewResource("active-filters", "active-filters",
|
||||
mcp.WithResourceDescription("Get currently active analyzers/filters"),
|
||||
mcp.WithMIMEType("application/json"),
|
||||
)
|
||||
s.server.AddResource(activeFiltersResource, s.getActiveFiltersResource)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -505,6 +655,72 @@ func (s *K8sGptMCPServer) getClusterInfo(ctx context.Context, request mcp.ReadRe
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *K8sGptMCPServer) getNamespacesResource(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
|
||||
client, err := kubernetes.NewClient("", "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create Kubernetes client: %v", err)
|
||||
}
|
||||
|
||||
namespaces, err := client.Client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list namespaces: %v", err)
|
||||
}
|
||||
|
||||
// Extract just the namespace names
|
||||
names := make([]string, 0, len(namespaces.Items))
|
||||
for _, ns := range namespaces.Items {
|
||||
names = append(names, ns.Name)
|
||||
}
|
||||
|
||||
data, err := json.Marshal(map[string]interface{}{
|
||||
"count": len(names),
|
||||
"namespaces": names,
|
||||
})
|
||||
if err != nil {
|
||||
return []mcp.ResourceContents{
|
||||
&mcp.TextResourceContents{
|
||||
URI: "namespaces",
|
||||
MIMEType: "text/plain",
|
||||
Text: "Failed to marshal namespaces",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return []mcp.ResourceContents{
|
||||
&mcp.TextResourceContents{
|
||||
URI: "namespaces",
|
||||
MIMEType: "application/json",
|
||||
Text: string(data),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *K8sGptMCPServer) getActiveFiltersResource(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
|
||||
activeFilters := viper.GetStringSlice("active_filters")
|
||||
|
||||
data, err := json.Marshal(map[string]interface{}{
|
||||
"activeFilters": activeFilters,
|
||||
"count": len(activeFilters),
|
||||
})
|
||||
if err != nil {
|
||||
return []mcp.ResourceContents{
|
||||
&mcp.TextResourceContents{
|
||||
URI: "active-filters",
|
||||
MIMEType: "text/plain",
|
||||
Text: "Failed to marshal active filters",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return []mcp.ResourceContents{
|
||||
&mcp.TextResourceContents{
|
||||
URI: "active-filters",
|
||||
MIMEType: "application/json",
|
||||
Text: string(data),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the MCP server and releases resources
|
||||
func (s *K8sGptMCPServer) Close() error {
|
||||
return nil
|
||||
|
||||
438
pkg/server/mcp_handlers.go
Normal file
438
pkg/server/mcp_handlers.go
Normal file
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
Copyright 2024 The K8sGPT Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/analyzer"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/integration"
|
||||
"github.com/k8sgpt-ai/k8sgpt/pkg/kubernetes"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/spf13/viper"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// handleListResources lists Kubernetes resources of a specific type
|
||||
func (s *K8sGptMCPServer) handleListResources(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
var req struct {
|
||||
ResourceType string `json:"resourceType"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
LabelSelector string `json:"labelSelector,omitempty"`
|
||||
}
|
||||
if err := request.BindArguments(&req); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewClient("", "")
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
|
||||
}
|
||||
|
||||
listOptions := metav1.ListOptions{}
|
||||
if req.LabelSelector != "" {
|
||||
listOptions.LabelSelector = req.LabelSelector
|
||||
}
|
||||
|
||||
var result string
|
||||
resourceType := strings.ToLower(req.ResourceType)
|
||||
|
||||
switch resourceType {
|
||||
case "pod", "pods":
|
||||
pods, err := client.Client.CoreV1().Pods(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list pods: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(pods.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "deployment", "deployments":
|
||||
deps, err := client.Client.AppsV1().Deployments(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list deployments: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(deps.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "service", "services", "svc":
|
||||
svcs, err := client.Client.CoreV1().Services(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list services: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(svcs.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "node", "nodes":
|
||||
nodes, err := client.Client.CoreV1().Nodes().List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list nodes: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(nodes.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "job", "jobs":
|
||||
jobs, err := client.Client.BatchV1().Jobs(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list jobs: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(jobs.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "cronjob", "cronjobs":
|
||||
cronjobs, err := client.Client.BatchV1().CronJobs(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list cronjobs: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(cronjobs.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "statefulset", "statefulsets", "sts":
|
||||
sts, err := client.Client.AppsV1().StatefulSets(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list statefulsets: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(sts.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "daemonset", "daemonsets", "ds":
|
||||
ds, err := client.Client.AppsV1().DaemonSets(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list daemonsets: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(ds.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "replicaset", "replicasets", "rs":
|
||||
rs, err := client.Client.AppsV1().ReplicaSets(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list replicasets: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(rs.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "configmap", "configmaps", "cm":
|
||||
cms, err := client.Client.CoreV1().ConfigMaps(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list configmaps: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(cms.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "secret", "secrets":
|
||||
secrets, err := client.Client.CoreV1().Secrets(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list secrets: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(secrets.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "ingress", "ingresses", "ing":
|
||||
ingresses, err := client.Client.NetworkingV1().Ingresses(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list ingresses: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(ingresses.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "persistentvolumeclaim", "persistentvolumeclaims", "pvc":
|
||||
pvcs, err := client.Client.CoreV1().PersistentVolumeClaims(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list PVCs: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(pvcs.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "persistentvolume", "persistentvolumes", "pv":
|
||||
pvs, err := client.Client.CoreV1().PersistentVolumes().List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list PVs: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(pvs.Items, "", " ")
|
||||
result = string(data)
|
||||
|
||||
default:
|
||||
return mcp.NewToolResultErrorf("Unsupported resource type: %s. Supported types: pods, deployments, services, nodes, jobs, cronjobs, statefulsets, daemonsets, replicasets, configmaps, secrets, ingresses, pvc, pv", resourceType), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(result), nil
|
||||
}
|
||||
|
||||
// handleGetResource gets detailed information about a specific resource
|
||||
func (s *K8sGptMCPServer) handleGetResource(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
var req struct {
|
||||
ResourceType string `json:"resourceType"`
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
if err := request.BindArguments(&req); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewClient("", "")
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
|
||||
}
|
||||
|
||||
var result string
|
||||
resourceType := strings.ToLower(req.ResourceType)
|
||||
|
||||
switch resourceType {
|
||||
case "pod", "pods":
|
||||
pod, err := client.Client.CoreV1().Pods(req.Namespace).Get(ctx, req.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to get pod: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(pod, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "deployment", "deployments":
|
||||
dep, err := client.Client.AppsV1().Deployments(req.Namespace).Get(ctx, req.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to get deployment: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(dep, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "service", "services", "svc":
|
||||
svc, err := client.Client.CoreV1().Services(req.Namespace).Get(ctx, req.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to get service: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(svc, "", " ")
|
||||
result = string(data)
|
||||
|
||||
case "node", "nodes":
|
||||
node, err := client.Client.CoreV1().Nodes().Get(ctx, req.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to get node: %v", err), nil
|
||||
}
|
||||
data, _ := json.MarshalIndent(node, "", " ")
|
||||
result = string(data)
|
||||
|
||||
default:
|
||||
return mcp.NewToolResultErrorf("Unsupported resource type: %s", resourceType), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(result), nil
|
||||
}
|
||||
|
||||
// handleListNamespaces lists all namespaces in the cluster
|
||||
func (s *K8sGptMCPServer) handleListNamespaces(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
client, err := kubernetes.NewClient("", "")
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
|
||||
}
|
||||
|
||||
namespaces, err := client.Client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list namespaces: %v", err), nil
|
||||
}
|
||||
|
||||
data, _ := json.MarshalIndent(namespaces.Items, "", " ")
|
||||
return mcp.NewToolResultText(string(data)), nil
|
||||
}
|
||||
|
||||
// handleListEvents lists Kubernetes events
|
||||
func (s *K8sGptMCPServer) handleListEvents(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
var req struct {
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
InvolvedObjectName string `json:"involvedObjectName,omitempty"`
|
||||
InvolvedObjectKind string `json:"involvedObjectKind,omitempty"`
|
||||
Limit int64 `json:"limit,omitempty"`
|
||||
}
|
||||
if err := request.BindArguments(&req); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
|
||||
}
|
||||
|
||||
if req.Limit == 0 {
|
||||
req.Limit = 100
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewClient("", "")
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
|
||||
}
|
||||
|
||||
listOptions := metav1.ListOptions{
|
||||
Limit: req.Limit,
|
||||
}
|
||||
|
||||
events, err := client.Client.CoreV1().Events(req.Namespace).List(ctx, listOptions)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to list events: %v", err), nil
|
||||
}
|
||||
|
||||
// Filter events if needed
|
||||
filteredEvents := []corev1.Event{}
|
||||
for _, event := range events.Items {
|
||||
if req.InvolvedObjectName != "" && event.InvolvedObject.Name != req.InvolvedObjectName {
|
||||
continue
|
||||
}
|
||||
if req.InvolvedObjectKind != "" && event.InvolvedObject.Kind != req.InvolvedObjectKind {
|
||||
continue
|
||||
}
|
||||
filteredEvents = append(filteredEvents, event)
|
||||
}
|
||||
|
||||
data, _ := json.MarshalIndent(filteredEvents, "", " ")
|
||||
return mcp.NewToolResultText(string(data)), nil
|
||||
}
|
||||
|
||||
// handleGetLogs retrieves logs from a pod container
|
||||
func (s *K8sGptMCPServer) handleGetLogs(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
var req struct {
|
||||
PodName string `json:"podName"`
|
||||
Namespace string `json:"namespace"`
|
||||
Container string `json:"container,omitempty"`
|
||||
Previous bool `json:"previous,omitempty"`
|
||||
TailLines int64 `json:"tailLines,omitempty"`
|
||||
SinceSeconds int64 `json:"sinceSeconds,omitempty"`
|
||||
}
|
||||
if err := request.BindArguments(&req); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
|
||||
}
|
||||
|
||||
if req.TailLines == 0 {
|
||||
req.TailLines = 100
|
||||
}
|
||||
|
||||
client, err := kubernetes.NewClient("", "")
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to create Kubernetes client: %v", err), nil
|
||||
}
|
||||
|
||||
podLogOpts := &corev1.PodLogOptions{
|
||||
Container: req.Container,
|
||||
Previous: req.Previous,
|
||||
TailLines: &req.TailLines,
|
||||
}
|
||||
|
||||
if req.SinceSeconds > 0 {
|
||||
podLogOpts.SinceSeconds = &req.SinceSeconds
|
||||
}
|
||||
|
||||
logRequest := client.Client.CoreV1().Pods(req.Namespace).GetLogs(req.PodName, podLogOpts)
|
||||
logStream, err := logRequest.Stream(ctx)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to get logs: %v", err), nil
|
||||
}
|
||||
defer func() {
|
||||
_ = logStream.Close()
|
||||
}()
|
||||
|
||||
logs, err := io.ReadAll(logStream)
|
||||
if err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to read logs: %v", err), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(string(logs)), nil
|
||||
}
|
||||
|
||||
// handleListFilters lists available and active filters
|
||||
func (s *K8sGptMCPServer) handleListFilters(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
coreFilters, additionalFilters, integrationFilters := analyzer.ListFilters()
|
||||
active := viper.GetStringSlice("active_filters")
|
||||
|
||||
result := map[string]interface{}{
|
||||
"coreFilters": coreFilters,
|
||||
"additionalFilters": additionalFilters,
|
||||
"integrationFilters": integrationFilters,
|
||||
"activeFilters": active,
|
||||
}
|
||||
|
||||
data, _ := json.MarshalIndent(result, "", " ")
|
||||
return mcp.NewToolResultText(string(data)), nil
|
||||
}
|
||||
|
||||
// handleAddFilters adds filters to enable specific analyzers
|
||||
func (s *K8sGptMCPServer) handleAddFilters(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
var req struct {
|
||||
Filters []string `json:"filters"`
|
||||
}
|
||||
if err := request.BindArguments(&req); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
|
||||
}
|
||||
|
||||
activeFilters := viper.GetStringSlice("active_filters")
|
||||
for _, filter := range req.Filters {
|
||||
if !contains(activeFilters, filter) {
|
||||
activeFilters = append(activeFilters, filter)
|
||||
}
|
||||
}
|
||||
|
||||
viper.Set("active_filters", activeFilters)
|
||||
if err := viper.WriteConfig(); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to save configuration: %v", err), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(fmt.Sprintf("Successfully added filters: %v", req.Filters)), nil
|
||||
}
|
||||
|
||||
// handleRemoveFilters removes filters to disable specific analyzers
|
||||
func (s *K8sGptMCPServer) handleRemoveFilters(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
var req struct {
|
||||
Filters []string `json:"filters"`
|
||||
}
|
||||
if err := request.BindArguments(&req); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to parse request arguments: %v", err), nil
|
||||
}
|
||||
|
||||
activeFilters := viper.GetStringSlice("active_filters")
|
||||
newFilters := []string{}
|
||||
for _, filter := range activeFilters {
|
||||
if !contains(req.Filters, filter) {
|
||||
newFilters = append(newFilters, filter)
|
||||
}
|
||||
}
|
||||
|
||||
viper.Set("active_filters", newFilters)
|
||||
if err := viper.WriteConfig(); err != nil {
|
||||
return mcp.NewToolResultErrorf("Failed to save configuration: %v", err), nil
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(fmt.Sprintf("Successfully removed filters: %v", req.Filters)), nil
|
||||
}
|
||||
|
||||
// handleListIntegrations lists available integrations
|
||||
func (s *K8sGptMCPServer) handleListIntegrations(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
integrationProvider := integration.NewIntegration()
|
||||
integrations := integrationProvider.List()
|
||||
|
||||
result := []map[string]interface{}{}
|
||||
for _, integ := range integrations {
|
||||
active, _ := integrationProvider.IsActivate(integ)
|
||||
result = append(result, map[string]interface{}{
|
||||
"name": integ,
|
||||
"active": active,
|
||||
})
|
||||
}
|
||||
|
||||
data, _ := json.MarshalIndent(result, "", " ")
|
||||
return mcp.NewToolResultText(string(data)), nil
|
||||
}
|
||||
|
||||
// contains checks if a string slice contains a specific string
|
||||
func contains(slice []string, item string) bool {
|
||||
for _, s := range slice {
|
||||
if s == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
170
pkg/server/mcp_prompts.go
Normal file
170
pkg/server/mcp_prompts.go
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
Copyright 2024 The K8sGPT Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
// getTroubleshootPodPrompt returns a prompt for pod troubleshooting
|
||||
func (s *K8sGptMCPServer) getTroubleshootPodPrompt(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
|
||||
podName := ""
|
||||
namespace := ""
|
||||
if request.Params.Arguments != nil {
|
||||
podName = request.Params.Arguments["podName"]
|
||||
namespace = request.Params.Arguments["namespace"]
|
||||
}
|
||||
|
||||
promptText := fmt.Sprintf(`You are troubleshooting a Kubernetes pod issue.
|
||||
|
||||
Pod: %s
|
||||
Namespace: %s
|
||||
|
||||
Troubleshooting steps:
|
||||
1. Use 'get-resource' tool to get pod details and check status, conditions, and events
|
||||
2. Use 'list-events' tool with the pod name to see recent events
|
||||
3. Use 'get-logs' tool to check container logs for errors
|
||||
4. Check if the pod has multiple containers and inspect each
|
||||
5. If the pod is in CrashLoopBackOff, use 'get-logs' with previous=true
|
||||
6. Use 'analyze' tool with filters=['Pod'] to get AI-powered analysis
|
||||
7. Check related resources like ConfigMaps, Secrets, and PVCs
|
||||
|
||||
Common issues to check:
|
||||
- Image pull errors (check imagePullSecrets)
|
||||
- Resource limits (CPU/memory)
|
||||
- Liveness/readiness probe failures
|
||||
- Volume mount issues
|
||||
- Environment variable problems
|
||||
- Network connectivity issues`, podName, namespace)
|
||||
|
||||
return &mcp.GetPromptResult{
|
||||
Description: "Pod troubleshooting guide",
|
||||
Messages: []mcp.PromptMessage{
|
||||
{
|
||||
Role: "user",
|
||||
Content: mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: promptText,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getTroubleshootDeploymentPrompt returns a prompt for deployment troubleshooting
|
||||
func (s *K8sGptMCPServer) getTroubleshootDeploymentPrompt(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
|
||||
deploymentName := ""
|
||||
namespace := ""
|
||||
if request.Params.Arguments != nil {
|
||||
deploymentName = request.Params.Arguments["deploymentName"]
|
||||
namespace = request.Params.Arguments["namespace"]
|
||||
}
|
||||
|
||||
promptText := fmt.Sprintf(`You are troubleshooting a Kubernetes deployment issue.
|
||||
|
||||
Deployment: %s
|
||||
Namespace: %s
|
||||
|
||||
Troubleshooting steps:
|
||||
1. Use 'get-resource' tool to get deployment details and check replica status
|
||||
2. Use 'list-resources' with resourceType='replicasets' to check ReplicaSets
|
||||
3. Use 'list-resources' with resourceType='pods' and labelSelector to find deployment pods
|
||||
4. Use 'list-events' tool to see deployment-related events
|
||||
5. Use 'analyze' tool with filters=['Deployment','Pod'] for comprehensive analysis
|
||||
6. Check pod status and logs for individual pod issues
|
||||
7. Verify image availability and pull secrets
|
||||
8. Check resource quotas and limits
|
||||
|
||||
Common deployment issues:
|
||||
- Insufficient resources in the cluster
|
||||
- Image pull failures
|
||||
- Invalid configuration (ConfigMaps/Secrets)
|
||||
- Failed rolling updates
|
||||
- Readiness probe failures preventing rollout
|
||||
- PVC binding issues
|
||||
- Node selector/affinity constraints`, deploymentName, namespace)
|
||||
|
||||
return &mcp.GetPromptResult{
|
||||
Description: "Deployment troubleshooting guide",
|
||||
Messages: []mcp.PromptMessage{
|
||||
{
|
||||
Role: "user",
|
||||
Content: mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: promptText,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getTroubleshootClusterPrompt returns a prompt for general cluster troubleshooting
|
||||
func (s *K8sGptMCPServer) getTroubleshootClusterPrompt(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
|
||||
promptText := `You are performing a general Kubernetes cluster health check and troubleshooting.
|
||||
|
||||
Recommended troubleshooting workflow:
|
||||
|
||||
1. CLUSTER OVERVIEW:
|
||||
- Use 'cluster-info' to get cluster version
|
||||
- Use 'list-namespaces' to see all namespaces
|
||||
- Use 'list-resources' with resourceType='nodes' to check node health
|
||||
|
||||
2. RESOURCE ANALYSIS:
|
||||
- Use 'analyze' tool with explain=true for comprehensive AI-powered analysis
|
||||
- Start with core resources: filters=['Pod','Deployment','Service']
|
||||
- Add more filters as needed: ['Node','PersistentVolumeClaim','Job','CronJob']
|
||||
|
||||
3. EVENT INSPECTION:
|
||||
- Use 'list-events' to see recent cluster events
|
||||
- Filter by namespace for focused troubleshooting
|
||||
- Look for Warning and Error events
|
||||
|
||||
4. SPECIFIC RESOURCE INVESTIGATION:
|
||||
- Use 'list-resources' to find problematic resources
|
||||
- Use 'get-resource' for detailed inspection
|
||||
- Use 'get-logs' to examine container logs
|
||||
|
||||
5. CONFIGURATION CHECK:
|
||||
- Use 'list-filters' to see available analyzers
|
||||
- Use 'list-integrations' to check integrations (Prometheus, AWS, etc.)
|
||||
- Use 'config' tool to modify settings if needed
|
||||
|
||||
Common cluster-wide issues:
|
||||
- Node pressure (CPU, memory, disk)
|
||||
- Network policies blocking traffic
|
||||
- Storage provisioning problems
|
||||
- RBAC permission issues
|
||||
- Certificate expiration
|
||||
- Control plane component failures
|
||||
- Resource quota exhaustion
|
||||
- DNS resolution problems
|
||||
|
||||
Use the available tools systematically to narrow down the issue.`
|
||||
|
||||
return &mcp.GetPromptResult{
|
||||
Description: "General cluster troubleshooting guide",
|
||||
Messages: []mcp.PromptMessage{
|
||||
{
|
||||
Role: "user",
|
||||
Content: mcp.TextContent{
|
||||
Type: "text",
|
||||
Text: promptText,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user