package tools
import (
"context"
"fmt"
"os/exec"
"strings"
"github.com/modelcontextprotocol/go-sdk/mcp"
"github.com/sirupsen/logrus"
k8scontext "mcp-k8swizard/internal/context"
)
// ContextTools handles context management operations
type ContextTools struct {
contextManager *k8scontext.Manager
}
// NewContextTools creates a new ContextTools instance
func NewContextTools(cm *k8scontext.Manager) *ContextTools {
return &ContextTools{
contextManager: cm,
}
}
// GetAvailableContexts gets all available contexts
func (ct *ContextTools) GetAvailableContexts() ([]string, error) {
return ct.getAvailableContexts()
}
// HandleSetContext handles setting the current context
func (ct *ContextTools) HandleSetContext(ctx context.Context, req *mcp.CallToolRequest, args SetContextArgs) (result *mcp.CallToolResult, data any, err error) {
// Add panic recovery
defer func() {
if r := recover(); r != nil {
logrus.Errorf("Panic in HandleSetContext: %v", r)
result = &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: fmt.Sprintf("Internal error: %v", r)},
},
}
data = nil
err = nil
}
}()
logrus.Info("handleSetContext called")
logrus.Debugf("handleSetContext called with args: %+v", args)
// First, get available contexts if not already cached
availableContexts, err := ct.getAvailableContexts()
if err != nil {
logrus.Errorf("Failed to get available contexts: %v", err)
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: fmt.Sprintf("Error getting available contexts: %v", err)},
},
}, nil, nil
}
// Update the context manager with available contexts
ct.contextManager.SetAvailableContexts(availableContexts)
// Set the current context
err = ct.contextManager.SetCurrentContext(args.Context)
if err != nil {
logrus.Errorf("Failed to set context: %v", err)
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: fmt.Sprintf("โ Failed to set context: %v", err)},
},
}, nil, nil
}
// Switch kubectl context
err = ct.switchKubectlContext(args.Context)
if err != nil {
logrus.Warnf("Failed to switch kubectl context: %v", err)
// Don't fail the operation, just warn
}
formatted := fmt.Sprintf("โ
**Context switched successfully!**\n")
formatted += fmt.Sprintf("๐ฏ **Current context**: %s\n", args.Context)
formatted += fmt.Sprintf("๐ **Available contexts**: %d\n", len(availableContexts))
formatted += fmt.Sprintf("๐ก **All subsequent operations will use this context until you change it**")
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: formatted},
},
}, nil, nil
}
// HandleGetCurrentContext handles getting the current context
func (ct *ContextTools) HandleGetCurrentContext(ctx context.Context, req *mcp.CallToolRequest, args GetCurrentContextArgs) (*mcp.CallToolResult, any, error) {
logrus.Info("handleGetCurrentContext called")
currentContext := ct.contextManager.GetCurrentContext()
contextInfo := ct.contextManager.GetContextInfo()
formatted := fmt.Sprintf("๐ฏ **Current Context Information**\n\n")
if currentContext == "" {
formatted += "โ **No context set** - using default context\n"
formatted += "๐ก Use `k8s_set_context` to set a specific context\n"
} else {
formatted += fmt.Sprintf("โ
**Current context**: %s\n", currentContext)
formatted += fmt.Sprintf("๐ **Available contexts**: %d\n", contextInfo["total"])
formatted += fmt.Sprintf("๐ **Last updated**: %s\n", contextInfo["lastUpdated"])
}
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: formatted},
},
}, nil, nil
}
// HandleListContexts handles listing all available contexts
func (ct *ContextTools) HandleListContexts(ctx context.Context, req *mcp.CallToolRequest, args ListContextsArgs) (*mcp.CallToolResult, any, error) {
logrus.Info("handleListContexts called")
contexts, err := ct.getAvailableContexts()
if err != nil {
logrus.Errorf("Failed to get contexts: %v", err)
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: fmt.Sprintf("Error getting contexts: %v", err)},
},
}, nil, nil
}
// Update the context manager
ct.contextManager.SetAvailableContexts(contexts)
currentContext := ct.contextManager.GetCurrentContext()
formatted := fmt.Sprintf("๐ **Available Kubernetes Contexts**\n\n")
for i, ctx := range contexts {
marker := " "
if ctx == currentContext {
marker = "๐ฏ"
}
formatted += fmt.Sprintf("%s %d. %s\n", marker, i+1, ctx)
}
if currentContext != "" {
formatted += fmt.Sprintf("\nโ
**Current context**: %s", currentContext)
} else {
formatted += fmt.Sprintf("\nโ **No context set** - using default")
}
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: formatted},
},
}, nil, nil
}
// HandleClearContext handles clearing the current context
func (ct *ContextTools) HandleClearContext(ctx context.Context, req *mcp.CallToolRequest, args ClearContextArgs) (*mcp.CallToolResult, any, error) {
logrus.Info("handleClearContext called")
previousContext := ct.contextManager.GetCurrentContext()
ct.contextManager.ClearContext()
formatted := fmt.Sprintf("๐งน **Context cleared successfully!**\n")
if previousContext != "" {
formatted += fmt.Sprintf("๐ค **Previous context**: %s\n", previousContext)
}
formatted += fmt.Sprintf("๐ **Now using**: Default context\n")
formatted += fmt.Sprintf("๐ก Use `k8s_set_context` to set a specific context")
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: formatted},
},
}, nil, nil
}
// getAvailableContexts gets all available contexts using kubectl
func (ct *ContextTools) getAvailableContexts() ([]string, error) {
cmd := exec.Command("kubectl", "config", "get-contexts", "-o", "name")
output, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("failed to get contexts: %w", err)
}
contexts := strings.Split(strings.TrimSpace(string(output)), "\n")
var result []string
for _, ctx := range contexts {
if ctx != "" {
result = append(result, ctx)
}
}
return result, nil
}
// switchKubectlContext switches the kubectl context
func (ct *ContextTools) switchKubectlContext(contextName string) error {
cmd := exec.Command("kubectl", "config", "use-context", contextName)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to switch kubectl context: %w, output: %s", err, string(output))
}
logrus.Debugf("Switched kubectl context to: %s", contextName)
return nil
}