package tools import ( "context" "forge-tools-proxmox/internal/pve" "github.com/modelcontextprotocol/go-sdk/mcp" ) // storage.go — хранилища: storage_list, storage_status (read) и содержимое // (templates_list / isos_list). Только наблюдение: изменение хранилищ // (Ceph/ZFS/SDN) — зона отдельного модуля (см. README «Roadmap»), чтобы не // тащить комбайн и не рисковать дисковой подсистемой. func registerStorageTools(s *mcp.Server) { s.AddTool(&mcp.Tool{ Name: "storage_list", Description: "List storage pools (type/content/usage). Read-only.", InputSchema: schema(map[string]any{ "host": strProps("Cluster alias (default: primary)", false), }, nil), }, storageListHandler) s.AddTool(&mcp.Tool{ Name: "storage_status", Description: "Storage pool status and usage (per node). Read-only.", InputSchema: schema(map[string]any{ "node": strProps("Node name", true), "host": strProps("Cluster alias (default: primary)", false), }, []string{"node"}), }, storageStatusHandler) s.AddTool(&mcp.Tool{ Name: "templates_list", Description: "List LXC templates (vztmpl) on a storage. Read-only.", InputSchema: schema(map[string]any{ "node": strProps("Node name", true), "storage": strProps("Storage (default: local)", false), "host": strProps("Cluster alias (default: primary)", false), }, []string{"node"}), }, templatesListHandler) s.AddTool(&mcp.Tool{ Name: "isos_list", Description: "List ISO images on a storage. Read-only.", InputSchema: schema(map[string]any{ "node": strProps("Node name", true), "storage": strProps("Storage (default: local)", false), "host": strProps("Cluster alias (default: primary)", false), }, []string{"node"}), }, isosListHandler) } func storageListHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { t, err := tenantFor(ctx, req) if err != nil { return errorResult(err.Error()), nil } host, err := resolveHost(t, requestArgs(req)) if err != nil { return errorResult(err.Error()), nil } cctx, cancel := timeout(ctx, t) defer cancel() data, err := t.StorageList(cctx, host) if err != nil { return errorResult(err.Error()), nil } return textResult(pretty(data)), nil } func storageStatusHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { // storage_status — статус хранилищ ноды (через /nodes/{node}/storage). t, err := tenantFor(ctx, req) if err != nil { return errorResult(err.Error()), nil } args := requestArgs(req) node, err := requireNode(args) if err != nil { return errorResult(err.Error()), nil } host, err := resolveHost(t, args) if err != nil { return errorResult(err.Error()), nil } cctx, cancel := timeout(ctx, t) defer cancel() data, err := t.NodeStorage(cctx, host, node) if err != nil { return errorResult(err.Error()), nil } return textResult(pretty(data)), nil } func templatesListHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { return contentListHandler(ctx, req, "vztmpl") } func isosListHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { return contentListHandler(ctx, req, "iso") } // contentListHandler — содержимое хранилища по типу (vztmpl/iso/backup/images). func contentListHandler(ctx context.Context, req *mcp.CallToolRequest, contentType string) (*mcp.CallToolResult, error) { t, err := tenantFor(ctx, req) if err != nil { return errorResult(err.Error()), nil } args := requestArgs(req) node, err := requireNode(args) if err != nil { return errorResult(err.Error()), nil } storage := getString(args, "storage", "local") if err := pve.ValidateIdentifier(storage); err != nil { return errorResult(err.Error()), nil } host, err := resolveHost(t, args) if err != nil { return errorResult(err.Error()), nil } cctx, cancel := timeout(ctx, t) defer cancel() data, err := t.StorageContent(cctx, host, node, storage, contentType) if err != nil { return errorResult(err.Error()), nil } return textResult(pretty(data)), nil }