279 lines
9.9 KiB
Go
279 lines
9.9 KiB
Go
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{}{}},
|
|
},
|
|
}
|
|
}
|