Initial commit: forge-tools-proxmox — MCP-сервер для Proxmox VE
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
// vm_net.go — сетевые интерфейсы ВМ (net0..netN): добавить/обновить/удалить.
|
||||
// Это структурные изменения конфига → write gate + confirm + probe. Модель
|
||||
// задаёт iface (net0), модель/бридж; guard валидирует имя интерфейса.
|
||||
|
||||
func registerVMNetTools(s *mcp.Server) {
|
||||
registerPatternTool(s, &mcp.Tool{
|
||||
Name: "vm_add_network",
|
||||
Description: "Add a network interface (netN) to a VM, e.g. net0 via vmbr0. Requires confirm + write permission.",
|
||||
InputSchema: schema(map[string]any{
|
||||
"node": strProps("Node name", true),
|
||||
"vmid": strProps("VM ID", true),
|
||||
"iface": strProps("Interface slot (e.g. net0)", true),
|
||||
"model": strProps("NIC model", true, "virtio", "e1000", "vmxnet3", "rtl8139"),
|
||||
"bridge": strProps("Bridge (e.g. vmbr0)", true),
|
||||
"mac": strProps("MAC (empty = auto)", false),
|
||||
"firewall": boolProps("Enable firewall (default: true)", false),
|
||||
"confirm": strProps("Set to \"true\" to confirm", true, "true"),
|
||||
}, []string{"node", "vmid", "iface", "bridge", "confirm"}),
|
||||
}, vmPatterns, vmAddNetworkHandler)
|
||||
|
||||
registerPatternTool(s, &mcp.Tool{
|
||||
Name: "vm_update_network",
|
||||
Description: "Update an existing network interface (model/bridge/mac/firewall). Requires confirm + write permission.",
|
||||
InputSchema: schema(map[string]any{
|
||||
"node": strProps("Node name", true),
|
||||
"vmid": strProps("VM ID", true),
|
||||
"iface": strProps("Interface slot (e.g. net0)", true),
|
||||
"model": strProps("NIC model", false, "virtio", "e1000", "vmxnet3", "rtl8139"),
|
||||
"bridge": strProps("Bridge (e.g. vmbr0)", false),
|
||||
"mac": strProps("MAC address", false),
|
||||
"firewall": boolProps("Enable firewall (default: true)", false),
|
||||
"confirm": strProps("Set to \"true\" to confirm", true, "true"),
|
||||
}, []string{"node", "vmid", "iface", "confirm"}),
|
||||
}, vmPatterns, vmUpdateNetworkHandler)
|
||||
|
||||
registerPatternTool(s, &mcp.Tool{
|
||||
Name: "vm_remove_network",
|
||||
Description: "Remove a network interface from a VM. Requires confirm + write permission.",
|
||||
InputSchema: schema(map[string]any{
|
||||
"node": strProps("Node name", true),
|
||||
"vmid": strProps("VM ID", true),
|
||||
"iface": strProps("Interface slot (e.g. net0)", true),
|
||||
"confirm": strProps("Set to \"true\" to confirm", true, "true"),
|
||||
}, []string{"node", "vmid", "iface", "confirm"}),
|
||||
}, vmPatterns, vmRemoveNetworkHandler)
|
||||
}
|
||||
|
||||
func vmAddNetworkHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
t, err := tenantFor(ctx, req)
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
args := requestArgs(req)
|
||||
node, vmid, host, r := resolveVMArgs(t, args)
|
||||
if r != nil {
|
||||
return r, nil
|
||||
}
|
||||
if err := confirm(args, "vm_add_network"); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
if err := gateVMWrite(t, host, vmid); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
iface, err := requireName(args, "iface")
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
model := getString(args, "model", "virtio")
|
||||
bridge := getString(args, "bridge", "")
|
||||
if bridge == "" {
|
||||
return errorResult("'bridge' is required"), nil
|
||||
}
|
||||
value := fmt.Sprintf("%s,bridge=%s", model, bridge)
|
||||
if mac := getString(args, "mac", ""); mac != "" {
|
||||
value += ",mac=" + mac
|
||||
}
|
||||
if getBool(args, "firewall", true) {
|
||||
value += ",firewall=1"
|
||||
}
|
||||
upid, err := t.ConfigPost(ctx, host, node, vmTypeQEMU, vmid, url.Values{iface: {value}})
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
return textResult(upidMsg("vm_add_network", fmt.Sprintf("%s/%d %s", node, vmid, iface), upid)), nil
|
||||
}
|
||||
|
||||
func vmUpdateNetworkHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
t, err := tenantFor(ctx, req)
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
args := requestArgs(req)
|
||||
node, vmid, host, r := resolveVMArgs(t, args)
|
||||
if r != nil {
|
||||
return r, nil
|
||||
}
|
||||
if err := confirm(args, "vm_update_network"); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
if err := gateVMWrite(t, host, vmid); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
iface, err := requireName(args, "iface")
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
// update перезаписывает весь сетевой спецификатор: собираем из явных полей.
|
||||
parts := []string{getString(args, "model", "virtio")}
|
||||
if b := getString(args, "bridge", ""); b != "" {
|
||||
parts = append(parts, "bridge="+b)
|
||||
}
|
||||
if m := getString(args, "mac", ""); m != "" {
|
||||
parts = append(parts, "mac="+m)
|
||||
}
|
||||
if getBool(args, "firewall", true) {
|
||||
parts = append(parts, "firewall=1")
|
||||
}
|
||||
upid, err := t.ConfigPost(ctx, host, node, vmTypeQEMU, vmid, url.Values{iface: {strings.Join(parts, ",")}})
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
return textResult(upidMsg("vm_update_network", fmt.Sprintf("%s/%d %s", node, vmid, iface), upid)), nil
|
||||
}
|
||||
|
||||
func vmRemoveNetworkHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
t, err := tenantFor(ctx, req)
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
args := requestArgs(req)
|
||||
node, vmid, host, r := resolveVMArgs(t, args)
|
||||
if r != nil {
|
||||
return r, nil
|
||||
}
|
||||
if err := confirm(args, "vm_remove_network"); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
if err := gateVMWrite(t, host, vmid); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
iface, err := requireName(args, "iface")
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
upid, err := t.ConfigPost(ctx, host, node, vmTypeQEMU, vmid, url.Values{"delete": {iface}})
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
return textResult(upidMsg("vm_remove_network", fmt.Sprintf("%s/%d %s", node, vmid, iface), upid)), nil
|
||||
}
|
||||
Reference in New Issue
Block a user