Initial commit: forge-tools-proxmox — MCP-сервер для Proxmox VE
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user