Initial commit: Caddy MCP Server
This commit is contained in:
+305
@@ -0,0 +1,305 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Site Management Handlers
|
||||
func (s *MCPServer) handleListSites(id interface{}) {
|
||||
sites, err := s.client.ListSites()
|
||||
if err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var output string
|
||||
for _, site := range sites {
|
||||
domain, _ := site["domain"].(string)
|
||||
upstreams := s.client.extractUpstreams(site["route"].(map[string]interface{}))
|
||||
output += fmt.Sprintf("📍 %s\n", domain)
|
||||
if len(upstreams) > 0 {
|
||||
output += fmt.Sprintf(" → %v\n", upstreams)
|
||||
}
|
||||
if s.client.hasBasicAuth(site["route"].(map[string]interface{})) {
|
||||
output += " 🔒 Auth enabled\n"
|
||||
}
|
||||
output += "\n"
|
||||
}
|
||||
if len(sites) == 0 {
|
||||
output = "No sites configured\n"
|
||||
}
|
||||
s.sendTextResult(id, output)
|
||||
}
|
||||
|
||||
func (s *MCPServer) handleGetSite(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
site, err := s.client.GetSite(domain)
|
||||
if err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
output := fmt.Sprintf("Domain: %s\n", domain)
|
||||
output += fmt.Sprintf("Server: %s\n", site["server"])
|
||||
output += fmt.Sprintf("Upstreams: %v\n", site["upstreams"])
|
||||
output += fmt.Sprintf("Auth: %v\n", site["has_auth"])
|
||||
handlers, _ := json.MarshalIndent(site["handlers"], "", " ")
|
||||
output += fmt.Sprintf("Handlers: %s\n", string(handlers))
|
||||
s.sendTextResult(id, output)
|
||||
}
|
||||
|
||||
func (s *MCPServer) handleAddSite(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
if err := validateDomain(domain); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
upstream, _ := args["upstream"].(string)
|
||||
if err := s.client.AddSite(domain, upstream); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Added %s → %s", domain, upstream))
|
||||
}
|
||||
|
||||
func (s *MCPServer) handleRemoveSite(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
if err := s.client.RemoveSite(domain); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Removed %s", domain))
|
||||
}
|
||||
|
||||
func (s *MCPServer) handleUpdateUpstream(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
upstream, _ := args["upstream"].(string)
|
||||
if err := s.client.UpdateUpstream(domain, upstream); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Updated %s → %s", domain, upstream))
|
||||
}
|
||||
|
||||
// Path Route Handlers
|
||||
func (s *MCPServer) handleAddPathRoute(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
if err := validateDomain(domain); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
path, _ := args["path"].(string)
|
||||
if err := validatePath(path); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
upstream, _ := args["upstream"].(string)
|
||||
if err := s.client.AddPathRoute(domain, path, upstream); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Added %s%s/* → %s", domain, path, upstream))
|
||||
}
|
||||
|
||||
func (s *MCPServer) handleRemovePathRoute(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
if err := validateDomain(domain); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
path, _ := args["path"].(string)
|
||||
if err := validatePath(path); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
if err := s.client.RemovePathRoute(domain, path); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Removed %s%s/*", domain, path))
|
||||
}
|
||||
|
||||
// Auth Handlers
|
||||
func (s *MCPServer) handleAddBasicAuth(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
username, _ := args["username"].(string)
|
||||
password, _ := args["password"].(string)
|
||||
if err := s.client.AddBasicAuth(domain, username, password); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Added basic auth for %s (user: %s)", domain, username))
|
||||
}
|
||||
|
||||
func (s *MCPServer) handleRemoveBasicAuth(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
if err := s.client.RemoveBasicAuth(domain); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Removed basic auth from %s", domain))
|
||||
}
|
||||
|
||||
// Header Handlers
|
||||
func (s *MCPServer) handleAddRequestHeader(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
if err := validateDomain(domain); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
header, _ := args["header"].(string)
|
||||
if err := validateHeaderName(header); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
value, _ := args["value"].(string)
|
||||
if err := s.client.AddHeaderUp(domain, header, value); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Added request header %s: %s for %s", header, value, domain))
|
||||
}
|
||||
|
||||
func (s *MCPServer) handleAddResponseHeader(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
if err := validateDomain(domain); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
header, _ := args["header"].(string)
|
||||
if err := validateHeaderName(header); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
value, _ := args["value"].(string)
|
||||
if err := s.client.AddHeaderDown(domain, header, value); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Added response header %s: %s for %s", header, value, domain))
|
||||
}
|
||||
|
||||
// File Server Handlers
|
||||
func (s *MCPServer) handleAddFileServer(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
root, _ := args["root"].(string)
|
||||
browse, _ := args["browse"].(bool)
|
||||
if err := s.client.AddFileServer(domain, root, browse); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Added file server %s → %s (browse: %v)", domain, root, browse))
|
||||
}
|
||||
|
||||
func (s *MCPServer) handleUpdateFileRoot(id interface{}, args map[string]interface{}) {
|
||||
domain, _ := args["domain"].(string)
|
||||
root, _ := args["root"].(string)
|
||||
if err := s.client.UpdateFileServerRoot(domain, root); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Updated %s root → %s", domain, root))
|
||||
}
|
||||
|
||||
// Review Environment Handler
|
||||
func (s *MCPServer) handleAddWipEnvironment(id interface{}, args map[string]interface{}) {
|
||||
project, _ := args["project"].(string)
|
||||
portFloat, _ := args["port"].(float64)
|
||||
port := int(portFloat)
|
||||
|
||||
reviewDomain := os.Getenv("REVIEW_DOMAIN")
|
||||
if reviewDomain == "" {
|
||||
reviewDomain = "review.example.com"
|
||||
}
|
||||
path := fmt.Sprintf("/%s/wip", project)
|
||||
upstream := fmt.Sprintf("localhost:%d", port)
|
||||
|
||||
if err := s.client.AddPathRoute(reviewDomain, path, upstream); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Added WIP https://%s%s/* → %s", reviewDomain, path, upstream))
|
||||
}
|
||||
|
||||
// Config Handlers
|
||||
func (s *MCPServer) handleExportConfig(id interface{}) {
|
||||
config, err := s.client.GetConfig()
|
||||
if err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
data, _ := json.MarshalIndent(config, "", " ")
|
||||
s.sendTextResult(id, string(data))
|
||||
}
|
||||
|
||||
func (s *MCPServer) handleAdaptCaddyfile(id interface{}, args map[string]interface{}) {
|
||||
caddyfile, _ := args["caddyfile"].(string)
|
||||
result, err := s.client.AdaptCaddyfile(caddyfile)
|
||||
if err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
data, _ := json.MarshalIndent(result, "", " ")
|
||||
s.sendTextResult(id, string(data))
|
||||
}
|
||||
|
||||
func (s *MCPServer) handleValidateConfig(id interface{}, args map[string]interface{}) {
|
||||
configArg, _ := args["config"].(map[string]interface{})
|
||||
if err := s.client.ValidateConfig(configArg); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, "✅ Configuration is valid")
|
||||
}
|
||||
|
||||
// Control Handlers
|
||||
func (s *MCPServer) handleCaddyStatus(id interface{}) {
|
||||
sites, err := s.client.ListSites()
|
||||
if err != nil {
|
||||
s.sendTextResult(id, fmt.Sprintf("❌ Caddy unreachable: %v", err))
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, fmt.Sprintf("✅ Caddy running (%d sites configured)", len(sites)))
|
||||
}
|
||||
|
||||
func (s *MCPServer) handleStopCaddy(id interface{}) {
|
||||
if err := s.client.Stop(); err != nil {
|
||||
s.sendError(id, -32000, err.Error())
|
||||
return
|
||||
}
|
||||
s.sendTextResult(id, "✅ Caddy stopped")
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
func (s *MCPServer) sendTextResult(id interface{}, text string) {
|
||||
s.sendResult(id, map[string]interface{}{
|
||||
"content": []interface{}{
|
||||
map[string]interface{}{"type": "text", "text": text},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (s *MCPServer) sendResult(id interface{}, result interface{}) {
|
||||
resp := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"result": result,
|
||||
}
|
||||
data, _ := json.Marshal(resp)
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
|
||||
func (s *MCPServer) sendError(id interface{}, code int, message string) {
|
||||
resp := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"error": map[string]interface{}{
|
||||
"code": code,
|
||||
"message": message,
|
||||
},
|
||||
}
|
||||
data, _ := json.Marshal(resp)
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
Reference in New Issue
Block a user