Initial commit: Caddy MCP Server

This commit is contained in:
2026-04-10 21:57:20 -07:00
commit 2a3107f9e2
21 changed files with 2194 additions and 0 deletions
+278
View File
@@ -0,0 +1,278 @@
package main
import (
"bufio"
"encoding/json"
"os"
)
type MCPServer struct {
client *CaddyClient
}
func main() {
caddyHost := os.Getenv("CADDY_HOST")
if caddyHost == "" {
caddyHost = "localhost:2019"
}
client := NewCaddyClient(caddyHost)
server := &MCPServer{client: client}
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := scanner.Text()
if line == "" {
continue
}
var req map[string]interface{}
if err := json.Unmarshal([]byte(line), &req); err != nil {
server.sendError(nil, -32700, "Parse error")
continue
}
server.handleRequest(req)
}
}
func (s *MCPServer) handleRequest(req map[string]interface{}) {
method, _ := req["method"].(string)
id := req["id"]
switch method {
case "initialize":
s.sendResult(id, map[string]interface{}{
"protocolVersion": "2024-11-05",
"capabilities": map[string]interface{}{"tools": map[string]interface{}{}},
"serverInfo": map[string]interface{}{
"name": "caddy-mcp",
"version": "2.0.0",
},
})
case "tools/list":
s.sendResult(id, map[string]interface{}{"tools": getAllTools()})
case "tools/call":
params, _ := req["params"].(map[string]interface{})
toolName, _ := params["name"].(string)
args, _ := params["arguments"].(map[string]interface{})
s.handleToolCall(id, toolName, args)
default:
s.sendError(id, -32601, "Method not found")
}
}
func getAllTools() []interface{} {
return []interface{}{
// Site management
map[string]interface{}{
"name": "list_sites",
"description": "List all configured Caddy sites with their upstreams",
"inputSchema": map[string]interface{}{"type": "object", "properties": map[string]interface{}{}},
},
map[string]interface{}{
"name": "get_site",
"description": "Get detailed configuration for a specific site",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"domain": map[string]interface{}{"type": "string", "description": "Domain name"}},
"required": []string{"domain"},
},
},
map[string]interface{}{
"name": "get_site_routes",
"description": "Get all routes (including path-based) for a site",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"domain": map[string]interface{}{"type": "string", "description": "Domain name"}},
"required": []string{"domain"},
},
},
map[string]interface{}{
"name": "add_site",
"description": "Add a new site with reverse proxy",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"domain": map[string]interface{}{"type": "string", "description": "Domain name"},
"upstream": map[string]interface{}{"type": "string", "description": "Upstream address (e.g., localhost:3000)"},
},
"required": []string{"domain", "upstream"},
},
},
map[string]interface{}{
"name": "remove_site",
"description": "Remove a site configuration",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"domain": map[string]interface{}{"type": "string", "description": "Domain to remove"}},
"required": []string{"domain"},
},
},
map[string]interface{}{
"name": "update_upstream",
"description": "Update the upstream address for a site",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"domain": map[string]interface{}{"type": "string", "description": "Domain name"},
"upstream": map[string]interface{}{"type": "string", "description": "New upstream address"},
},
"required": []string{"domain", "upstream"},
},
},
// Path-based routing
map[string]interface{}{
"name": "add_path_route",
"description": "Add a path-based route to a site (e.g., /api/* → localhost:3000)",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"domain": map[string]interface{}{"type": "string", "description": "Domain name"},
"path": map[string]interface{}{"type": "string", "description": "Path pattern (e.g., /api)"},
"upstream": map[string]interface{}{"type": "string", "description": "Upstream address"},
},
"required": []string{"domain", "path", "upstream"},
},
},
map[string]interface{}{
"name": "remove_path_route",
"description": "Remove a path-based route from a site",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"domain": map[string]interface{}{"type": "string", "description": "Domain name"},
"path": map[string]interface{}{"type": "string", "description": "Path pattern to remove"},
},
"required": []string{"domain", "path"},
},
},
// Authentication
map[string]interface{}{
"name": "add_basic_auth",
"description": "Add basic authentication to a site",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"domain": map[string]interface{}{"type": "string", "description": "Domain name"},
"username": map[string]interface{}{"type": "string", "description": "Username"},
"password": map[string]interface{}{"type": "string", "description": "Password (will be hashed)"},
},
"required": []string{"domain", "username", "password"},
},
},
map[string]interface{}{
"name": "remove_basic_auth",
"description": "Remove basic authentication from a site",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"domain": map[string]interface{}{"type": "string", "description": "Domain name"}},
"required": []string{"domain"},
},
},
// Headers
map[string]interface{}{
"name": "add_request_header",
"description": "Add a request header sent to upstream",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"domain": map[string]interface{}{"type": "string", "description": "Domain name"},
"header": map[string]interface{}{"type": "string", "description": "Header name"},
"value": map[string]interface{}{"type": "string", "description": "Header value"},
},
"required": []string{"domain", "header", "value"},
},
},
map[string]interface{}{
"name": "add_response_header",
"description": "Add a response header sent to client",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"domain": map[string]interface{}{"type": "string", "description": "Domain name"},
"header": map[string]interface{}{"type": "string", "description": "Header name"},
"value": map[string]interface{}{"type": "string", "description": "Header value"},
},
"required": []string{"domain", "header", "value"},
},
},
// File server
map[string]interface{}{
"name": "add_file_server",
"description": "Add a static file server for a domain",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"domain": map[string]interface{}{"type": "string", "description": "Domain name"},
"root": map[string]interface{}{"type": "string", "description": "Root directory path"},
"browse": map[string]interface{}{"type": "boolean", "description": "Enable directory browsing"},
},
"required": []string{"domain", "root"},
},
},
map[string]interface{}{
"name": "update_file_root",
"description": "Update the root directory for a file server",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"domain": map[string]interface{}{"type": "string", "description": "Domain name"},
"root": map[string]interface{}{"type": "string", "description": "New root directory"},
},
"required": []string{"domain", "root"},
},
},
// Review environment helpers
map[string]interface{}{
"name": "add_wip_environment",
"description": "Add a WIP review environment (e.g., /project/wip/* → port)",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"project": map[string]interface{}{"type": "string", "description": "Project name (e.g., my-project, frontend)"},
"port": map[string]interface{}{"type": "number", "description": "Port number"},
},
"required": []string{"project", "port"},
},
},
// Configuration
map[string]interface{}{
"name": "export_config",
"description": "Export full Caddy configuration as JSON",
"inputSchema": map[string]interface{}{"type": "object", "properties": map[string]interface{}{}},
},
map[string]interface{}{
"name": "adapt_caddyfile",
"description": "Convert Caddyfile format to JSON",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"caddyfile": map[string]interface{}{"type": "string", "description": "Caddyfile content"}},
"required": []string{"caddyfile"},
},
},
// Server control
map[string]interface{}{
"name": "caddy_status",
"description": "Check Caddy server health and status",
"inputSchema": map[string]interface{}{"type": "object", "properties": map[string]interface{}{}},
},
map[string]interface{}{
"name": "validate_config",
"description": "Validate a configuration without applying it",
"inputSchema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{"config": map[string]interface{}{"type": "object", "description": "Configuration to validate"}},
"required": []string{"config"},
},
},
map[string]interface{}{
"name": "stop_caddy",
"description": "Stop the Caddy server gracefully",
"inputSchema": map[string]interface{}{"type": "object", "properties": map[string]interface{}{}},
},
}
}