2025-06-05 14:31:32 -05:00
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js' ;
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' ;
import { CallToolRequestSchema , ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js' ;
import fetch from 'node-fetch' ;
import https from 'https' ;
2025-11-06 11:13:58 -06:00
import crypto from 'crypto' ;
2025-06-05 14:31:32 -05:00
import { readFileSync } from 'fs' ;
import { join , dirname } from 'path' ;
import { fileURLToPath } from 'url' ;
2026-03-02 12:14:08 -08:00
import { homedir } from 'os' ;
2025-06-05 14:31:32 -05:00
2026-03-02 12:14:08 -08:00
// Load environment variables from ~/.env file (standardized location)
const envPath = join ( homedir ( ) , '.env' ) ;
// Also try relative path as fallback for backwards compatibility
2025-06-05 14:31:32 -05:00
const _ _filename = fileURLToPath ( import . meta . url ) ;
const _ _dirname = dirname ( _ _filename ) ;
2026-03-02 12:14:08 -08:00
const envPathFallback = join ( _ _dirname , '../.env' ) ;
2025-06-05 14:31:32 -05:00
2026-03-02 12:14:08 -08:00
function loadEnvFile ( filePath ) {
try {
const envFile = readFileSync ( filePath , 'utf8' ) ;
const envVars = envFile . split ( '\n' ) . filter ( line => line . includes ( '=' ) && ! line . trim ( ) . startsWith ( '#' ) ) ;
for ( const line of envVars ) {
const [ key , ... values ] = line . split ( '=' ) ;
// Validate key is a valid environment variable name (alphanumeric and underscore only)
if ( key && values . length > 0 && /^[A-Z_][A-Z0-9_]*$/ . test ( key . trim ( ) ) ) {
// Remove surrounding quotes if present and trim
let value = values . join ( '=' ) . trim ( ) ;
if ( ( value . startsWith ( '"' ) && value . endsWith ( '"' ) ) || ( value . startsWith ( "'" ) && value . endsWith ( "'" ) ) ) {
value = value . slice ( 1 , - 1 ) ;
}
process . env [ key . trim ( ) ] = value ;
2025-11-06 11:13:58 -06:00
}
2025-06-05 14:31:32 -05:00
}
2026-03-02 12:14:08 -08:00
return true ;
} catch ( error ) {
return false ;
}
}
// Try ~/.env first, then fallback to relative path
if ( ! loadEnvFile ( envPath ) ) {
if ( ! loadEnvFile ( envPathFallback ) ) {
console . error ( 'Warning: Could not load .env file from ~/.env or ../.env' ) ;
2025-06-05 14:31:32 -05:00
}
}
class ProxmoxServer {
constructor ( ) {
this . server = new Server (
{
name : 'proxmox-server' ,
version : '1.0.0' ,
} ,
{
capabilities : {
tools : { } ,
} ,
}
) ;
this . proxmoxHost = process . env . PROXMOX _HOST || '192.168.6.247' ;
this . proxmoxUser = process . env . PROXMOX _USER || 'root@pam' ;
this . proxmoxTokenName = process . env . PROXMOX _TOKEN _NAME || 'mcpserver' ;
this . proxmoxTokenValue = process . env . PROXMOX _TOKEN _VALUE ;
this . proxmoxPort = process . env . PROXMOX _PORT || '8006' ;
this . allowElevated = process . env . PROXMOX _ALLOW _ELEVATED === 'true' ;
// Create agent that accepts self-signed certificates
this . httpsAgent = new https . Agent ( {
rejectUnauthorized : false
} ) ;
this . setupToolHandlers ( ) ;
}
2025-11-06 11:13:58 -06:00
// Input validation methods for security
validateNodeName ( node ) {
if ( ! node || typeof node !== 'string' ) {
throw new Error ( 'Node name is required and must be a string' ) ;
}
// Only allow alphanumeric, hyphens, and underscores
if ( ! /^[a-zA-Z0-9\-_]+$/ . test ( node ) ) {
throw new Error ( 'Invalid node name format. Only alphanumeric, hyphens, and underscores allowed' ) ;
}
if ( node . length > 64 ) {
throw new Error ( 'Node name too long (max 64 characters)' ) ;
}
return node ;
}
validateVMID ( vmid ) {
if ( ! vmid ) {
throw new Error ( 'VM ID is required' ) ;
}
const id = parseInt ( vmid , 10 ) ;
if ( isNaN ( id ) || id < 100 || id > 999999999 ) {
throw new Error ( 'Invalid VM ID. Must be a number between 100 and 999999999' ) ;
}
return id . toString ( ) ;
}
validateCommand ( command ) {
if ( ! command || typeof command !== 'string' ) {
throw new Error ( 'Command is required and must be a string' ) ;
}
// Check for dangerous characters that could be used for command injection
const dangerousChars = /[;&|`$(){}[\]<>\\]/g ;
if ( dangerousChars . test ( command ) ) {
throw new Error ( 'Command contains potentially dangerous characters: ; & | ` $ ( ) { } [ ] < > \\' ) ;
}
// Limit command length
if ( command . length > 1000 ) {
throw new Error ( 'Command exceeds maximum allowed length (1000 characters)' ) ;
}
return command ;
}
generateSecurePassword ( ) {
// Generate a secure random password using Node.js crypto
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*' ;
let password = '' ;
const randomBytes = crypto . randomBytes ( 16 ) ;
for ( let i = 0 ; i < 16 ; i ++ ) {
password += chars [ randomBytes [ i ] % chars . length ] ;
}
return password ;
}
2025-06-05 14:31:32 -05:00
async proxmoxRequest ( endpoint , method = 'GET' , body = null ) {
const baseUrl = ` https:// ${ this . proxmoxHost } : ${ this . proxmoxPort } /api2/json ` ;
const url = ` ${ baseUrl } ${ endpoint } ` ;
2025-11-06 11:13:58 -06:00
2025-06-05 14:31:32 -05:00
const headers = {
'Authorization' : ` PVEAPIToken= ${ this . proxmoxUser } ! ${ this . proxmoxTokenName } = ${ this . proxmoxTokenValue } ` ,
'Content-Type' : 'application/json'
} ;
const options = {
method ,
headers ,
agent : this . httpsAgent
} ;
if ( body ) {
options . body = JSON . stringify ( body ) ;
}
try {
const response = await fetch ( url , options ) ;
2025-11-06 11:13:58 -06:00
2025-06-05 14:31:32 -05:00
if ( ! response . ok ) {
const errorText = await response . text ( ) ;
throw new Error ( ` Proxmox API error: ${ response . status } - ${ errorText } ` ) ;
}
2025-11-06 11:13:58 -06:00
2025-06-05 14:31:32 -05:00
const textResponse = await response . text ( ) ;
if ( ! textResponse . trim ( ) ) {
throw new Error ( 'Empty response from Proxmox API' ) ;
}
2025-11-06 11:13:58 -06:00
2025-06-05 14:31:32 -05:00
const data = JSON . parse ( textResponse ) ;
return data . data ;
} catch ( error ) {
if ( error . name === 'SyntaxError' ) {
throw new Error ( ` Failed to parse Proxmox API response: ${ error . message } ` ) ;
}
throw new Error ( ` Failed to connect to Proxmox: ${ error . message } ` ) ;
}
}
setupToolHandlers ( ) {
this . server . setRequestHandler ( ListToolsRequestSchema , async ( ) => ( {
tools : [
{
name : 'proxmox_get_nodes' ,
description : 'List all Proxmox cluster nodes with their status and resources' ,
inputSchema : {
type : 'object' ,
properties : { }
}
} ,
{
name : 'proxmox_get_node_status' ,
description : 'Get detailed status information for a specific Proxmox node' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name (e.g., pve1, proxmox-node2)' }
} ,
required : [ 'node' ]
}
} ,
{
name : 'proxmox_get_vms' ,
description : 'List all virtual machines across the cluster with their status' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Optional: filter by specific node' } ,
type : { type : 'string' , enum : [ 'qemu' , 'lxc' , 'all' ] , description : 'VM type filter' , default : 'all' }
}
}
} ,
{
name : 'proxmox_get_vm_status' ,
description : 'Get detailed status information for a specific VM' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
type : { type : 'string' , enum : [ 'qemu' , 'lxc' ] , description : 'VM type' , default : 'qemu' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_execute_vm_command' ,
description : 'Execute a shell command on a virtual machine via Proxmox API' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
command : { type : 'string' , description : 'Shell command to execute' } ,
type : { type : 'string' , enum : [ 'qemu' , 'lxc' ] , description : 'VM type' , default : 'qemu' }
} ,
required : [ 'node' , 'vmid' , 'command' ]
}
} ,
{
name : 'proxmox_get_storage' ,
description : 'List all storage pools and their usage across the cluster' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Optional: filter by specific node' }
}
}
} ,
{
name : 'proxmox_get_cluster_status' ,
description : 'Get overall cluster status including nodes and resource usage' ,
inputSchema : {
type : 'object' ,
properties : { }
}
2025-11-06 11:13:58 -06:00
} ,
{
name : 'proxmox_list_templates' ,
description : 'List available LXC container templates on a storage' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name' } ,
storage : { type : 'string' , description : 'Storage name (e.g., local)' , default : 'local' }
} ,
required : [ 'node' ]
}
} ,
{
name : 'proxmox_create_lxc' ,
description : 'Create a new LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container will be created' } ,
vmid : { type : 'string' , description : 'Container ID number (must be unique, or use proxmox_get_next_vmid)' } ,
ostemplate : { type : 'string' , description : 'OS template (e.g., local:vztmpl/debian-12-standard_12.2-1_amd64.tar.gz)' } ,
hostname : { type : 'string' , description : 'Container hostname' } ,
password : { type : 'string' , description : 'Root password' } ,
memory : { type : 'number' , description : 'RAM in MB' , default : 512 } ,
storage : { type : 'string' , description : 'Storage location' , default : 'local-lvm' } ,
rootfs : { type : 'string' , description : 'Root filesystem size in GB' , default : '8' }
} ,
required : [ 'node' , 'vmid' , 'ostemplate' ]
}
} ,
{
name : 'proxmox_create_vm' ,
description : 'Create a new QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM will be created' } ,
vmid : { type : 'string' , description : 'VM ID number (must be unique, or use proxmox_get_next_vmid)' } ,
name : { type : 'string' , description : 'VM name' } ,
memory : { type : 'number' , description : 'RAM in MB' , default : 512 } ,
cores : { type : 'number' , description : 'Number of CPU cores' , default : 1 } ,
sockets : { type : 'number' , description : 'Number of CPU sockets' , default : 1 } ,
disk _size : { type : 'string' , description : 'Disk size (e.g., "8G", "10G")' , default : '8G' } ,
storage : { type : 'string' , description : 'Storage location for disk' , default : 'local-lvm' } ,
iso : { type : 'string' , description : 'ISO image (e.g., "local:iso/alpine-virt-3.19.1-x86_64.iso"), optional' } ,
ostype : { type : 'string' , description : 'OS type (l26=Linux 2.6+, win10, etc)' , default : 'l26' } ,
net0 : { type : 'string' , description : 'Network interface config' , default : 'virtio,bridge=vmbr0' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_get_next_vmid' ,
description : 'Get the next available VM/Container ID number' ,
inputSchema : {
type : 'object' ,
properties : { }
}
} ,
{
name : 'proxmox_start_lxc' ,
description : 'Start an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_start_vm' ,
description : 'Start a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_stop_lxc' ,
description : 'Stop an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_stop_vm' ,
description : 'Stop a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_delete_lxc' ,
description : 'Delete an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number to delete' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_delete_vm' ,
description : 'Delete a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number to delete' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_reboot_lxc' ,
description : 'Reboot an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_reboot_vm' ,
description : 'Reboot a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_shutdown_lxc' ,
description : 'Gracefully shutdown an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_shutdown_vm' ,
description : 'Gracefully shutdown a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_pause_vm' ,
description : 'Pause a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_resume_vm' ,
description : 'Resume a paused QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_clone_lxc' ,
description : 'Clone an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID to clone from' } ,
newid : { type : 'string' , description : 'New container ID' } ,
hostname : { type : 'string' , description : 'Hostname for cloned container (optional)' }
} ,
required : [ 'node' , 'vmid' , 'newid' ]
}
} ,
{
name : 'proxmox_clone_vm' ,
description : 'Clone a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID to clone from' } ,
newid : { type : 'string' , description : 'New VM ID' } ,
name : { type : 'string' , description : 'Name for cloned VM (optional)' }
} ,
required : [ 'node' , 'vmid' , 'newid' ]
}
} ,
{
name : 'proxmox_resize_lxc' ,
description : 'Resize an LXC container CPU/memory (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
memory : { type : 'number' , description : 'Memory in MB (optional)' } ,
cores : { type : 'number' , description : 'Number of CPU cores (optional)' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_resize_vm' ,
description : 'Resize a QEMU VM CPU/memory (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
memory : { type : 'number' , description : 'Memory in MB (optional)' } ,
cores : { type : 'number' , description : 'Number of CPU cores (optional)' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_create_snapshot_lxc' ,
description : 'Create a snapshot of an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
snapname : { type : 'string' , description : 'Snapshot name' }
} ,
required : [ 'node' , 'vmid' , 'snapname' ]
}
} ,
{
name : 'proxmox_create_snapshot_vm' ,
description : 'Create a snapshot of a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
snapname : { type : 'string' , description : 'Snapshot name' }
} ,
required : [ 'node' , 'vmid' , 'snapname' ]
}
} ,
{
name : 'proxmox_list_snapshots_lxc' ,
description : 'List all snapshots of an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_list_snapshots_vm' ,
description : 'List all snapshots of a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_rollback_snapshot_lxc' ,
description : 'Rollback an LXC container to a snapshot (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
snapname : { type : 'string' , description : 'Snapshot name to rollback to' }
} ,
required : [ 'node' , 'vmid' , 'snapname' ]
}
} ,
{
name : 'proxmox_rollback_snapshot_vm' ,
description : 'Rollback a QEMU virtual machine to a snapshot (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
snapname : { type : 'string' , description : 'Snapshot name to rollback to' }
} ,
required : [ 'node' , 'vmid' , 'snapname' ]
}
} ,
{
name : 'proxmox_delete_snapshot_lxc' ,
description : 'Delete a snapshot of an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
snapname : { type : 'string' , description : 'Snapshot name to delete' }
} ,
required : [ 'node' , 'vmid' , 'snapname' ]
}
} ,
{
name : 'proxmox_delete_snapshot_vm' ,
description : 'Delete a snapshot of a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
snapname : { type : 'string' , description : 'Snapshot name to delete' }
} ,
required : [ 'node' , 'vmid' , 'snapname' ]
}
} ,
{
name : 'proxmox_create_backup_lxc' ,
description : 'Create a backup of an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
storage : { type : 'string' , description : 'Storage location for backup' , default : 'local' } ,
mode : { type : 'string' , enum : [ 'snapshot' , 'suspend' , 'stop' ] , description : 'Backup mode' , default : 'snapshot' } ,
compress : { type : 'string' , enum : [ 'none' , 'lzo' , 'gzip' , 'zstd' ] , description : 'Compression algorithm' , default : 'zstd' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_create_backup_vm' ,
description : 'Create a backup of a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
storage : { type : 'string' , description : 'Storage location for backup' , default : 'local' } ,
mode : { type : 'string' , enum : [ 'snapshot' , 'suspend' , 'stop' ] , description : 'Backup mode' , default : 'snapshot' } ,
compress : { type : 'string' , enum : [ 'none' , 'lzo' , 'gzip' , 'zstd' ] , description : 'Compression algorithm' , default : 'zstd' }
} ,
required : [ 'node' , 'vmid' ]
}
} ,
{
name : 'proxmox_list_backups' ,
description : 'List all backups on a storage (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name' } ,
storage : { type : 'string' , description : 'Storage name' , default : 'local' }
} ,
required : [ 'node' ]
}
} ,
{
name : 'proxmox_restore_backup_lxc' ,
description : 'Restore an LXC container from backup (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container will be restored' } ,
vmid : { type : 'string' , description : 'New container ID for restored container' } ,
archive : { type : 'string' , description : 'Backup archive path (e.g., local:backup/vzdump-lxc-100-2025_11_06-09_00_00.tar.zst)' } ,
storage : { type : 'string' , description : 'Storage location for restored container (optional)' }
} ,
required : [ 'node' , 'vmid' , 'archive' ]
}
} ,
{
name : 'proxmox_restore_backup_vm' ,
description : 'Restore a QEMU virtual machine from backup (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM will be restored' } ,
vmid : { type : 'string' , description : 'New VM ID for restored VM' } ,
archive : { type : 'string' , description : 'Backup archive path (e.g., local:backup/vzdump-qemu-100-2025_11_06-09_00_00.vma.zst)' } ,
storage : { type : 'string' , description : 'Storage location for restored VM (optional)' }
} ,
required : [ 'node' , 'vmid' , 'archive' ]
}
} ,
{
name : 'proxmox_delete_backup' ,
description : 'Delete a backup file from storage (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name' } ,
storage : { type : 'string' , description : 'Storage name (e.g., local)' } ,
volume : { type : 'string' , description : 'Backup volume ID (e.g., local:backup/vzdump-lxc-100-2025_11_06-09_00_00.tar.zst)' }
} ,
required : [ 'node' , 'storage' , 'volume' ]
}
} ,
{
name : 'proxmox_add_disk_vm' ,
description : 'Add a new disk to a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
disk : { type : 'string' , description : 'Disk name (e.g., scsi1, virtio1, sata1, ide1)' } ,
storage : { type : 'string' , description : 'Storage name (e.g., local-lvm)' } ,
size : { type : 'string' , description : 'Disk size in GB (e.g., 10)' }
} ,
required : [ 'node' , 'vmid' , 'disk' , 'storage' , 'size' ]
}
} ,
{
name : 'proxmox_add_mountpoint_lxc' ,
description : 'Add a mount point to an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
mp : { type : 'string' , description : 'Mount point name (e.g., mp0, mp1, mp2)' } ,
storage : { type : 'string' , description : 'Storage name (e.g., local-lvm)' } ,
size : { type : 'string' , description : 'Mount point size in GB (e.g., 10)' }
} ,
required : [ 'node' , 'vmid' , 'mp' , 'storage' , 'size' ]
}
} ,
{
name : 'proxmox_resize_disk_vm' ,
description : 'Resize a QEMU VM disk (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
disk : { type : 'string' , description : 'Disk name (e.g., scsi0, virtio0, sata0, ide0)' } ,
size : { type : 'string' , description : 'New size with + for relative or absolute (e.g., +10G or 50G)' }
} ,
required : [ 'node' , 'vmid' , 'disk' , 'size' ]
}
} ,
{
name : 'proxmox_resize_disk_lxc' ,
description : 'Resize an LXC container disk or mount point (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
disk : { type : 'string' , description : 'Disk name (rootfs, mp0, mp1, etc.)' } ,
size : { type : 'string' , description : 'New size with + for relative or absolute (e.g., +10G or 50G)' }
} ,
required : [ 'node' , 'vmid' , 'disk' , 'size' ]
}
} ,
{
name : 'proxmox_remove_disk_vm' ,
description : 'Remove a disk from a QEMU virtual machine (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
disk : { type : 'string' , description : 'Disk name to remove (e.g., scsi1, virtio1, sata1, ide1)' }
} ,
required : [ 'node' , 'vmid' , 'disk' ]
}
} ,
{
name : 'proxmox_remove_mountpoint_lxc' ,
description : 'Remove a mount point from an LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
mp : { type : 'string' , description : 'Mount point name to remove (e.g., mp0, mp1, mp2)' }
} ,
required : [ 'node' , 'vmid' , 'mp' ]
}
} ,
{
name : 'proxmox_move_disk_vm' ,
description : 'Move a QEMU VM disk to different storage (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
disk : { type : 'string' , description : 'Disk name to move (e.g., scsi0, virtio0, sata0, ide0)' } ,
storage : { type : 'string' , description : 'Target storage name' } ,
delete : { type : 'boolean' , description : 'Delete source disk after move (default: true)' , default : true }
} ,
required : [ 'node' , 'vmid' , 'disk' , 'storage' ]
}
} ,
{
name : 'proxmox_move_disk_lxc' ,
description : 'Move an LXC container disk to different storage (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
disk : { type : 'string' , description : 'Disk/volume name to move (rootfs, mp0, mp1, etc.)' } ,
storage : { type : 'string' , description : 'Target storage name' } ,
delete : { type : 'boolean' , description : 'Delete source disk after move (default: true)' , default : true }
} ,
required : [ 'node' , 'vmid' , 'disk' , 'storage' ]
}
} ,
{
name : 'proxmox_add_network_vm' ,
description : 'Add network interface to QEMU VM (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
net : { type : 'string' , description : 'Network interface name (net0, net1, net2, etc.)' } ,
bridge : { type : 'string' , description : 'Bridge name (e.g., vmbr0, vmbr1)' } ,
model : { type : 'string' , description : 'Network model (virtio, e1000, rtl8139, vmxnet3)' , default : 'virtio' } ,
macaddr : { type : 'string' , description : 'MAC address (XX:XX:XX:XX:XX:XX) - auto-generated if not specified' } ,
vlan : { type : 'number' , description : 'VLAN tag (1-4094)' } ,
firewall : { type : 'boolean' , description : 'Enable firewall on this interface' }
} ,
required : [ 'node' , 'vmid' , 'net' , 'bridge' ]
}
} ,
{
name : 'proxmox_add_network_lxc' ,
description : 'Add network interface to LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
net : { type : 'string' , description : 'Network interface name (net0, net1, net2, etc.)' } ,
bridge : { type : 'string' , description : 'Bridge name (e.g., vmbr0, vmbr1)' } ,
ip : { type : 'string' , description : 'IP address (dhcp, 192.168.1.100/24, auto)' } ,
gw : { type : 'string' , description : 'Gateway IP address' } ,
firewall : { type : 'boolean' , description : 'Enable firewall on this interface' }
} ,
required : [ 'node' , 'vmid' , 'net' , 'bridge' ]
}
} ,
{
name : 'proxmox_update_network_vm' ,
description : 'Update/modify VM network interface configuration (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
net : { type : 'string' , description : 'Network interface name to update (net0, net1, net2, etc.)' } ,
bridge : { type : 'string' , description : 'Bridge name (e.g., vmbr0, vmbr1)' } ,
model : { type : 'string' , description : 'Network model (virtio, e1000, rtl8139, vmxnet3)' } ,
macaddr : { type : 'string' , description : 'MAC address (XX:XX:XX:XX:XX:XX)' } ,
vlan : { type : 'number' , description : 'VLAN tag (1-4094)' } ,
firewall : { type : 'boolean' , description : 'Enable firewall on this interface' }
} ,
required : [ 'node' , 'vmid' , 'net' ]
}
} ,
{
name : 'proxmox_update_network_lxc' ,
description : 'Update/modify LXC network interface configuration (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
net : { type : 'string' , description : 'Network interface name to update (net0, net1, net2, etc.)' } ,
bridge : { type : 'string' , description : 'Bridge name (e.g., vmbr0, vmbr1)' } ,
ip : { type : 'string' , description : 'IP address (dhcp, 192.168.1.100/24, auto)' } ,
gw : { type : 'string' , description : 'Gateway IP address' } ,
firewall : { type : 'boolean' , description : 'Enable firewall on this interface' }
} ,
required : [ 'node' , 'vmid' , 'net' ]
}
} ,
{
name : 'proxmox_remove_network_vm' ,
description : 'Remove network interface from QEMU VM (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where VM is located' } ,
vmid : { type : 'string' , description : 'VM ID number' } ,
net : { type : 'string' , description : 'Network interface name to remove (net0, net1, net2, etc.)' }
} ,
required : [ 'node' , 'vmid' , 'net' ]
}
} ,
{
name : 'proxmox_remove_network_lxc' ,
description : 'Remove network interface from LXC container (requires elevated permissions)' ,
inputSchema : {
type : 'object' ,
properties : {
node : { type : 'string' , description : 'Node name where container is located' } ,
vmid : { type : 'string' , description : 'Container ID number' } ,
net : { type : 'string' , description : 'Network interface name to remove (net0, net1, net2, etc.)' }
} ,
required : [ 'node' , 'vmid' , 'net' ]
}
}
]
} ) ) ;
this . server . setRequestHandler ( CallToolRequestSchema , async ( request ) => {
const { name , arguments : args } = request . params ;
try {
switch ( name ) {
case 'proxmox_get_nodes' :
return await this . getNodes ( ) ;
case 'proxmox_get_node_status' :
return await this . getNodeStatus ( args . node ) ;
case 'proxmox_get_vms' :
return await this . getVMs ( args . node , args . type ) ;
case 'proxmox_get_vm_status' :
return await this . getVMStatus ( args . node , args . vmid , args . type ) ;
case 'proxmox_execute_vm_command' :
return await this . executeVMCommand ( args . node , args . vmid , args . command , args . type ) ;
case 'proxmox_get_storage' :
return await this . getStorage ( args . node ) ;
case 'proxmox_get_cluster_status' :
return await this . getClusterStatus ( ) ;
case 'proxmox_list_templates' :
return await this . listTemplates ( args . node , args . storage ) ;
case 'proxmox_create_lxc' :
return await this . createLXCContainer ( args ) ;
case 'proxmox_create_vm' :
return await this . createVM ( args ) ;
case 'proxmox_get_next_vmid' :
return await this . getNextVMID ( ) ;
case 'proxmox_start_lxc' :
return await this . startVM ( args . node , args . vmid , 'lxc' ) ;
case 'proxmox_start_vm' :
return await this . startVM ( args . node , args . vmid , 'qemu' ) ;
case 'proxmox_stop_lxc' :
return await this . stopVM ( args . node , args . vmid , 'lxc' ) ;
case 'proxmox_stop_vm' :
return await this . stopVM ( args . node , args . vmid , 'qemu' ) ;
case 'proxmox_delete_lxc' :
return await this . deleteVM ( args . node , args . vmid , 'lxc' ) ;
case 'proxmox_delete_vm' :
return await this . deleteVM ( args . node , args . vmid , 'qemu' ) ;
case 'proxmox_reboot_lxc' :
return await this . rebootVM ( args . node , args . vmid , 'lxc' ) ;
case 'proxmox_reboot_vm' :
return await this . rebootVM ( args . node , args . vmid , 'qemu' ) ;
case 'proxmox_shutdown_lxc' :
return await this . shutdownVM ( args . node , args . vmid , 'lxc' ) ;
case 'proxmox_shutdown_vm' :
return await this . shutdownVM ( args . node , args . vmid , 'qemu' ) ;
case 'proxmox_pause_vm' :
return await this . pauseVM ( args . node , args . vmid ) ;
case 'proxmox_resume_vm' :
return await this . resumeVM ( args . node , args . vmid ) ;
case 'proxmox_clone_lxc' :
return await this . cloneVM ( args . node , args . vmid , args . newid , args . hostname , 'lxc' ) ;
case 'proxmox_clone_vm' :
return await this . cloneVM ( args . node , args . vmid , args . newid , args . name , 'qemu' ) ;
case 'proxmox_resize_lxc' :
return await this . resizeVM ( args . node , args . vmid , args . memory , args . cores , 'lxc' ) ;
case 'proxmox_resize_vm' :
return await this . resizeVM ( args . node , args . vmid , args . memory , args . cores , 'qemu' ) ;
case 'proxmox_create_snapshot_lxc' :
return await this . createSnapshot ( args . node , args . vmid , args . snapname , 'lxc' ) ;
case 'proxmox_create_snapshot_vm' :
return await this . createSnapshot ( args . node , args . vmid , args . snapname , 'qemu' ) ;
case 'proxmox_list_snapshots_lxc' :
return await this . listSnapshots ( args . node , args . vmid , 'lxc' ) ;
case 'proxmox_list_snapshots_vm' :
return await this . listSnapshots ( args . node , args . vmid , 'qemu' ) ;
case 'proxmox_rollback_snapshot_lxc' :
return await this . rollbackSnapshot ( args . node , args . vmid , args . snapname , 'lxc' ) ;
case 'proxmox_rollback_snapshot_vm' :
return await this . rollbackSnapshot ( args . node , args . vmid , args . snapname , 'qemu' ) ;
case 'proxmox_delete_snapshot_lxc' :
return await this . deleteSnapshot ( args . node , args . vmid , args . snapname , 'lxc' ) ;
case 'proxmox_delete_snapshot_vm' :
return await this . deleteSnapshot ( args . node , args . vmid , args . snapname , 'qemu' ) ;
case 'proxmox_create_backup_lxc' :
return await this . createBackup ( args . node , args . vmid , args . storage , args . mode , args . compress , 'lxc' ) ;
case 'proxmox_create_backup_vm' :
return await this . createBackup ( args . node , args . vmid , args . storage , args . mode , args . compress , 'qemu' ) ;
case 'proxmox_list_backups' :
return await this . listBackups ( args . node , args . storage ) ;
case 'proxmox_restore_backup_lxc' :
return await this . restoreBackup ( args . node , args . vmid , args . archive , args . storage , 'lxc' ) ;
case 'proxmox_restore_backup_vm' :
return await this . restoreBackup ( args . node , args . vmid , args . archive , args . storage , 'qemu' ) ;
case 'proxmox_delete_backup' :
return await this . deleteBackup ( args . node , args . storage , args . volume ) ;
case 'proxmox_add_disk_vm' :
return await this . addDiskVM ( args . node , args . vmid , args . disk , args . storage , args . size ) ;
case 'proxmox_add_mountpoint_lxc' :
return await this . addMountPointLXC ( args . node , args . vmid , args . mp , args . storage , args . size ) ;
case 'proxmox_resize_disk_vm' :
return await this . resizeDiskVM ( args . node , args . vmid , args . disk , args . size ) ;
case 'proxmox_resize_disk_lxc' :
return await this . resizeDiskLXC ( args . node , args . vmid , args . disk , args . size ) ;
case 'proxmox_remove_disk_vm' :
return await this . removeDiskVM ( args . node , args . vmid , args . disk ) ;
case 'proxmox_remove_mountpoint_lxc' :
return await this . removeMountPointLXC ( args . node , args . vmid , args . mp ) ;
case 'proxmox_move_disk_vm' :
return await this . moveDiskVM ( args . node , args . vmid , args . disk , args . storage , args . delete ) ;
case 'proxmox_move_disk_lxc' :
return await this . moveDiskLXC ( args . node , args . vmid , args . disk , args . storage , args . delete ) ;
case 'proxmox_add_network_vm' :
return await this . addNetworkVM ( args . node , args . vmid , args . net , args . bridge , args . model , args . macaddr , args . vlan , args . firewall ) ;
case 'proxmox_add_network_lxc' :
return await this . addNetworkLXC ( args . node , args . vmid , args . net , args . bridge , args . ip , args . gw , args . firewall ) ;
case 'proxmox_update_network_vm' :
return await this . updateNetworkVM ( args . node , args . vmid , args . net , args . bridge , args . model , args . macaddr , args . vlan , args . firewall ) ;
case 'proxmox_update_network_lxc' :
return await this . updateNetworkLXC ( args . node , args . vmid , args . net , args . bridge , args . ip , args . gw , args . firewall ) ;
case 'proxmox_remove_network_vm' :
return await this . removeNetworkVM ( args . node , args . vmid , args . net ) ;
case 'proxmox_remove_network_lxc' :
return await this . removeNetworkLXC ( args . node , args . vmid , args . net ) ;
default :
throw new Error ( ` Unknown tool: ${ name } ` ) ;
}
} catch ( error ) {
return {
content : [
{
type : 'text' ,
text : ` Error: ${ error . message } `
}
]
} ;
}
} ) ;
}
async getNodes ( ) {
const nodes = await this . proxmoxRequest ( '/nodes' ) ;
let output = '🖥️ **Proxmox Cluster Nodes**\n\n' ;
for ( const node of nodes ) {
const status = node . status === 'online' ? '🟢' : '🔴' ;
const uptime = node . uptime ? this . formatUptime ( node . uptime ) : 'N/A' ;
const cpuUsage = node . cpu ? ` ${ ( node . cpu * 100 ) . toFixed ( 1 ) } % ` : 'N/A' ;
const memUsage = node . mem && node . maxmem ?
` ${ this . formatBytes ( node . mem ) } / ${ this . formatBytes ( node . maxmem ) } ( ${ ( ( node . mem / node . maxmem ) * 100 ) . toFixed ( 1 ) } %) ` : 'N/A' ;
output += ` ${ status } ** ${ node . node } ** \n ` ;
output += ` • Status: ${ node . status } \n ` ;
output += ` • Uptime: ${ uptime } \n ` ;
output += ` • CPU: ${ cpuUsage } \n ` ;
output += ` • Memory: ${ memUsage } \n ` ;
output += ` • Load: ${ node . loadavg ? . [ 0 ] ? . toFixed ( 2 ) || 'N/A' } \n \n ` ;
}
return {
content : [ { type : 'text' , text : output } ]
} ;
}
async getNodeStatus ( node ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Node Status Requires Elevated Permissions** \n \n To view detailed node status, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file and ensure your API token has Sys.Audit permissions. \n \n **Current permissions**: Basic (node listing only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const status = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /status ` ) ;
let output = ` 🖥️ **Node ${ safeNode } Status** \n \n ` ;
output += ` • **Status**: ${ status . uptime ? '🟢 Online' : '🔴 Offline' } \n ` ;
output += ` • **Uptime**: ${ status . uptime ? this . formatUptime ( status . uptime ) : 'N/A' } \n ` ;
output += ` • **Load Average**: ${ status . loadavg ? . join ( ', ' ) || 'N/A' } \n ` ;
output += ` • **CPU Usage**: ${ status . cpu ? ` ${ ( status . cpu * 100 ) . toFixed ( 1 ) } % ` : 'N/A' } \n ` ;
output += ` • **Memory**: ${ status . memory ?
` ${ this . formatBytes ( status . memory . used ) } / ${ this . formatBytes ( status . memory . total ) } ( ${ ( ( status . memory . used / status . memory . total ) * 100 ) . toFixed ( 1 ) } %) ` : 'N/A' } \ n ` ;
output += ` • **Root Disk**: ${ status . rootfs ?
` ${ this . formatBytes ( status . rootfs . used ) } / ${ this . formatBytes ( status . rootfs . total ) } ( ${ ( ( status . rootfs . used / status . rootfs . total ) * 100 ) . toFixed ( 1 ) } %) ` : 'N/A' } \ n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to get node status** \n \n Error: ${ error . message } `
} ]
} ;
}
}
async getVMs ( nodeFilter = null , typeFilter = 'all' ) {
let vms = [ ] ;
if ( nodeFilter ) {
const nodeVMs = await this . proxmoxRequest ( ` /nodes/ ${ nodeFilter } /qemu ` ) ;
const nodeLXCs = await this . proxmoxRequest ( ` /nodes/ ${ nodeFilter } /lxc ` ) ;
if ( typeFilter === 'all' || typeFilter === 'qemu' ) {
vms . push ( ... nodeVMs . map ( vm => ( { ... vm , type : 'qemu' , node : nodeFilter } ) ) ) ;
}
if ( typeFilter === 'all' || typeFilter === 'lxc' ) {
vms . push ( ... nodeLXCs . map ( vm => ( { ... vm , type : 'lxc' , node : nodeFilter } ) ) ) ;
}
} else {
const nodes = await this . proxmoxRequest ( '/nodes' ) ;
for ( const node of nodes ) {
if ( typeFilter === 'all' || typeFilter === 'qemu' ) {
const nodeVMs = await this . proxmoxRequest ( ` /nodes/ ${ node . node } /qemu ` ) ;
vms . push ( ... nodeVMs . map ( vm => ( { ... vm , type : 'qemu' , node : node . node } ) ) ) ;
}
if ( typeFilter === 'all' || typeFilter === 'lxc' ) {
const nodeLXCs = await this . proxmoxRequest ( ` /nodes/ ${ node . node } /lxc ` ) ;
vms . push ( ... nodeLXCs . map ( vm => ( { ... vm , type : 'lxc' , node : vm . node || node . node } ) ) ) ;
}
}
}
let output = '💻 **Virtual Machines**\n\n' ;
if ( vms . length === 0 ) {
output += 'No virtual machines found.\n' ;
} else {
for ( const vm of vms . sort ( ( a , b ) => parseInt ( a . vmid ) - parseInt ( b . vmid ) ) ) {
const status = vm . status === 'running' ? '🟢' : vm . status === 'stopped' ? '🔴' : '🟡' ;
const typeIcon = vm . type === 'qemu' ? '🖥️' : '📦' ;
const uptime = vm . uptime ? this . formatUptime ( vm . uptime ) : 'N/A' ;
const cpuUsage = vm . cpu ? ` ${ ( vm . cpu * 100 ) . toFixed ( 1 ) } % ` : 'N/A' ;
const memUsage = vm . mem && vm . maxmem ?
` ${ this . formatBytes ( vm . mem ) } / ${ this . formatBytes ( vm . maxmem ) } ` : 'N/A' ;
output += ` ${ status } ${ typeIcon } ** ${ vm . name || ` VM- ${ vm . vmid } ` } ** (ID: ${ vm . vmid } ) \n ` ;
output += ` • Node: ${ vm . node } \n ` ;
output += ` • Status: ${ vm . status } \n ` ;
output += ` • Type: ${ vm . type . toUpperCase ( ) } \n ` ;
if ( vm . status === 'running' ) {
output += ` • Uptime: ${ uptime } \n ` ;
output += ` • CPU: ${ cpuUsage } \n ` ;
output += ` • Memory: ${ memUsage } \n ` ;
}
output += '\n' ;
}
}
return {
content : [ { type : 'text' , text : output } ]
} ;
}
async getVMStatus ( node , vmid , type = 'qemu' ) {
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const vmStatus = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } /status/current ` ) ;
const status = vmStatus . status === 'running' ? '🟢' : vmStatus . status === 'stopped' ? '🔴' : '🟡' ;
const typeIcon = type === 'qemu' ? '🖥️' : '📦' ;
let output = ` ${ status } ${ typeIcon } ** ${ vmStatus . name || ` VM- ${ safeVMID } ` } ** (ID: ${ safeVMID } ) \n \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Status**: ${ vmStatus . status } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
if ( vmStatus . status === 'running' ) {
output += ` • **Uptime**: ${ vmStatus . uptime ? this . formatUptime ( vmStatus . uptime ) : 'N/A' } \n ` ;
output += ` • **CPU Usage**: ${ vmStatus . cpu ? ` ${ ( vmStatus . cpu * 100 ) . toFixed ( 1 ) } % ` : 'N/A' } \n ` ;
output += ` • **Memory**: ${ vmStatus . mem && vmStatus . maxmem ?
` ${ this . formatBytes ( vmStatus . mem ) } / ${ this . formatBytes ( vmStatus . maxmem ) } ( ${ ( ( vmStatus . mem / vmStatus . maxmem ) * 100 ) . toFixed ( 1 ) } %) ` : 'N/A' } \ n ` ;
output += ` • **Disk Read**: ${ vmStatus . diskread ? this . formatBytes ( vmStatus . diskread ) : 'N/A' } \n ` ;
output += ` • **Disk Write**: ${ vmStatus . diskwrite ? this . formatBytes ( vmStatus . diskwrite ) : 'N/A' } \n ` ;
output += ` • **Network In**: ${ vmStatus . netin ? this . formatBytes ( vmStatus . netin ) : 'N/A' } \n ` ;
output += ` • **Network Out**: ${ vmStatus . netout ? this . formatBytes ( vmStatus . netout ) : 'N/A' } \n ` ;
}
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ { type : 'text' , text : ` ❌ Failed to get VM status: ${ error . message } ` } ] ,
isError : true
} ;
}
}
async executeVMCommand ( node , vmid , command , type = 'qemu' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **VM Command Execution Requires Elevated Permissions** \n \n To execute commands on VMs, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file and ensure your API token has appropriate VM permissions. \n \n **Current permissions**: Basic (VM listing only) \n **Requested command**: \` ${ command } \` `
} ]
} ;
}
try {
// Validate inputs to prevent injection attacks
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const safeCommand = this . validateCommand ( command ) ;
// For QEMU VMs, we need to use the guest agent
if ( type === 'qemu' ) {
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu/ ${ safeVMID } /agent/exec ` , 'POST' , {
command : safeCommand
} ) ;
let output = ` 💻 **Command executed on VM ${ safeVMID } ** \n \n ` ;
output += ` **Command**: \` ${ safeCommand } \` \n ` ;
output += ` **Result**: Command submitted to guest agent \n ` ;
output += ` **PID**: ${ result . pid || 'N/A' } \n \n ` ;
output += ` *Note: Use guest agent status to check command completion* ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} else {
// For LXC containers, we can execute directly
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /lxc/ ${ safeVMID } /exec ` , 'POST' , {
command : safeCommand
} ) ;
let output = ` 📦 **Command executed on LXC ${ safeVMID } ** \n \n ` ;
output += ` **Command**: \` ${ safeCommand } \` \n ` ;
output += ` **Output**: \n \` \` \` \n ${ result || 'Command executed successfully' } \n \` \` \` ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
}
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to execute command on VM ${ vmid } ** \n \n Error: ${ error . message } \n \n *Note: Make sure the VM has guest agent installed and running* `
} ]
} ;
}
}
async getStorage ( nodeFilter = null ) {
let storages = [ ] ;
if ( nodeFilter ) {
storages = await this . proxmoxRequest ( ` /nodes/ ${ nodeFilter } /storage ` ) ;
storages = storages . map ( storage => ( { ... storage , node : nodeFilter } ) ) ;
} else {
const nodes = await this . proxmoxRequest ( '/nodes' ) ;
for ( const node of nodes ) {
const nodeStorages = await this . proxmoxRequest ( ` /nodes/ ${ node . node } /storage ` ) ;
storages . push ( ... nodeStorages . map ( storage => ( { ... storage , node : node . node } ) ) ) ;
}
}
2025-06-05 14:31:32 -05:00
let output = '💾 **Storage Pools**\n\n' ;
if ( storages . length === 0 ) {
output += 'No storage found.\n' ;
} else {
const uniqueStorages = [ ] ;
const seen = new Set ( ) ;
for ( const storage of storages ) {
const key = ` ${ storage . storage } - ${ storage . node } ` ;
if ( ! seen . has ( key ) ) {
seen . add ( key ) ;
uniqueStorages . push ( storage ) ;
}
}
2025-11-06 11:13:58 -06:00
for ( const storage of uniqueStorages . sort ( ( a , b ) => a . storage . localeCompare ( b . storage ) ) ) {
const enabled = storage . enabled ? '🟢' : '🔴' ;
const usagePercent = storage . total && storage . used ?
( ( storage . used / storage . total ) * 100 ) . toFixed ( 1 ) : 'N/A' ;
output += ` ${ enabled } ** ${ storage . storage } ** \n ` ;
output += ` • Node: ${ storage . node } \n ` ;
output += ` • Type: ${ storage . type || 'N/A' } \n ` ;
output += ` • Content: ${ storage . content || 'N/A' } \n ` ;
if ( storage . total && storage . used ) {
output += ` • Usage: ${ this . formatBytes ( storage . used ) } / ${ this . formatBytes ( storage . total ) } ( ${ usagePercent } %) \n ` ;
}
output += ` • Status: ${ storage . enabled ? 'Enabled' : 'Disabled' } \n \n ` ;
}
}
return {
content : [ { type : 'text' , text : output } ]
} ;
}
async getClusterStatus ( ) {
try {
const nodes = await this . proxmoxRequest ( '/nodes' ) ;
// Try to get cluster status, but fall back gracefully if permissions are insufficient
let clusterStatus = null ;
if ( this . allowElevated ) {
try {
clusterStatus = await this . proxmoxRequest ( '/cluster/status' ) ;
} catch ( error ) {
// Ignore cluster status errors for elevated permissions
}
}
let output = '🏗️ **Proxmox Cluster Status**\n\n' ;
// Cluster overview
const onlineNodes = nodes . filter ( n => n . status === 'online' ) . length ;
const totalNodes = nodes . length ;
output += ` **Cluster Health**: ${ onlineNodes === totalNodes ? '🟢 Healthy' : '🟡 Warning' } \n ` ;
output += ` **Nodes**: ${ onlineNodes } / ${ totalNodes } online \n \n ` ;
if ( this . allowElevated ) {
// Resource summary (only available with elevated permissions)
let totalCpu = 0 , usedCpu = 0 ;
let totalMem = 0 , usedMem = 0 ;
for ( const node of nodes ) {
if ( node . status === 'online' ) {
totalCpu += node . maxcpu || 0 ;
usedCpu += ( node . cpu || 0 ) * ( node . maxcpu || 0 ) ;
totalMem += node . maxmem || 0 ;
usedMem += node . mem || 0 ;
}
}
const cpuPercent = totalCpu > 0 ? ( ( usedCpu / totalCpu ) * 100 ) . toFixed ( 1 ) : 'N/A' ;
const memPercent = totalMem > 0 ? ( ( usedMem / totalMem ) * 100 ) . toFixed ( 1 ) : 'N/A' ;
output += ` **Resource Usage**: \n ` ;
output += ` • CPU: ${ cpuPercent } % ( ${ usedCpu . toFixed ( 1 ) } / ${ totalCpu } cores) \n ` ;
output += ` • Memory: ${ memPercent } % ( ${ this . formatBytes ( usedMem ) } / ${ this . formatBytes ( totalMem ) } ) \n \n ` ;
} else {
output += ` ⚠️ **Limited Information**: Resource usage requires elevated permissions \n \n ` ;
}
// Node status
output += ` **Node Details**: \n ` ;
for ( const node of nodes . sort ( ( a , b ) => a . node . localeCompare ( b . node ) ) ) {
const status = node . status === 'online' ? '🟢' : '🔴' ;
output += ` ${ status } ${ node . node } - ${ node . status } \n ` ;
}
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to get cluster status** \n \n Error: ${ error . message } `
} ]
} ;
}
}
async listTemplates ( node , storage = 'local' ) {
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const templates = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /storage/ ${ storage } /content?content=vztmpl ` ) ;
let output = '📦 **Available LXC Templates**\n\n' ;
if ( ! templates || templates . length === 0 ) {
output += ` No templates found on storage \` ${ storage } \` . \n \n ` ;
output += ` **Tip**: Download templates in Proxmox: \n ` ;
output += ` 1. Go to your node → Storage → ${ storage } \n ` ;
output += ` 2. Click "CT Templates" \n ` ;
output += ` 3. Download a template (e.g., Debian, Ubuntu) \n ` ;
} else {
for ( const template of templates ) {
const size = template . size ? this . formatBytes ( template . size ) : 'N/A' ;
output += ` • ** ${ template . volid } ** \n ` ;
output += ` Size: ${ size } \n \n ` ;
}
}
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to list templates** \n \n Error: ${ error . message } \n \n **Note**: Make sure the storage exists and contains LXC templates. `
} ]
} ;
}
}
async createLXCContainer ( args ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Container Creation Requires Elevated Permissions** \n \n To create containers, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file and ensure your API token has VM.Allocate permissions. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( args . node ) ;
const safeVMID = this . validateVMID ( args . vmid ) ;
// Generate secure password if not provided
const generatedPassword = args . password || this . generateSecurePassword ( ) ;
const isPasswordGenerated = ! args . password ;
// Build the request body
const body = {
vmid : safeVMID ,
ostemplate : args . ostemplate ,
hostname : args . hostname || ` ct ${ safeVMID } ` ,
password : generatedPassword ,
memory : args . memory || 512 ,
storage : args . storage || 'local-lvm' ,
rootfs : ` ${ args . storage || 'local-lvm' } : ${ args . rootfs || 8 } `
} ;
// Make the API request
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /lxc ` , 'POST' , body ) ;
let output = ` ✅ **LXC Container Creation Started** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Hostname**: ${ body . hostname } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Template**: ${ args . ostemplate } \n ` ;
output += ` • **Memory**: ${ body . memory } MB \n ` ;
output += ` • **Storage**: ${ body . storage } \n ` ;
if ( isPasswordGenerated ) {
output += ` • **🔐 Generated Password**: \` ${ generatedPassword } \` \n ` ;
output += ` ⚠️ **SAVE THIS PASSWORD** - it will not be shown again! \n ` ;
}
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Next steps**: \n ` ;
output += ` 1. Wait a moment for container to be created \n ` ;
output += ` 2. Start it with \` proxmox_start_lxc \` \n ` ;
output += ` 3. View status with \` proxmox_get_vm_status \` \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to create container** \n \n Error: ${ error . message } \n \n **Common issues**: \n - VM ID already in use \n - Invalid template path \n - Insufficient permissions \n - Storage doesn't exist `
} ]
} ;
}
}
async createVM ( args ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **VM Creation Requires Elevated Permissions** \n \n To create VMs, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file and ensure your API token has VM.Allocate permissions. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( args . node ) ;
const safeVMID = this . validateVMID ( args . vmid ) ;
// Build the request body for QEMU VM creation
const body = {
vmid : safeVMID ,
name : args . name || ` vm ${ safeVMID } ` ,
memory : args . memory || 512 ,
cores : args . cores || 1 ,
sockets : args . sockets || 1 ,
ostype : args . ostype || 'l26' ,
net0 : args . net0 || 'virtio,bridge=vmbr0'
} ;
// Add disk configuration
// Format: storage:size (size in GB, no suffix)
const storage = args . storage || 'local-lvm' ;
const diskSize = args . disk _size || '8G' ;
// Extract numeric value from disk size (e.g., "8G" -> "8")
const sizeValue = diskSize . replace ( /[^0-9]/g , '' ) ;
body . scsi0 = ` ${ storage } : ${ sizeValue } ` ;
// Add ISO if provided
if ( args . iso ) {
body . ide2 = ` ${ args . iso } ,media=cdrom ` ;
body . boot = 'order=ide2;scsi0' ;
}
// Make the API request
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu ` , 'POST' , body ) ;
let output = ` ✅ **QEMU VM Creation Started** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Name**: ${ body . name } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Memory**: ${ body . memory } MB \n ` ;
output += ` • **CPU**: ${ body . sockets } socket(s), ${ body . cores } core(s) \n ` ;
output += ` • **Disk**: ${ body . scsi0 } \n ` ;
output += ` • **Network**: ${ body . net0 } \n ` ;
if ( args . iso ) {
output += ` • **ISO**: ${ args . iso } \n ` ;
}
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Next steps**: \n ` ;
output += ` 1. Wait a moment for VM to be created \n ` ;
output += ` 2. Start it with \` proxmox_start_vm \` \n ` ;
output += ` 3. View status with \` proxmox_get_vm_status \` \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to create VM** \n \n Error: ${ error . message } \n \n **Common issues**: \n - VM ID already in use \n - Invalid ISO path \n - Insufficient permissions \n - Storage doesn't exist `
} ]
} ;
}
}
async startVM ( node , vmid , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **VM Control Requires Elevated Permissions** \n \n To start/stop VMs, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } /status/start ` , 'POST' , { } ) ;
let output = ` ▶️ **VM/Container Start Command Sent** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Tip**: Use \` proxmox_get_vm_status \` to check if it's running. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to start VM/Container** \n \n Error: ${ error . message } `
} ]
} ;
}
}
async stopVM ( node , vmid , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **VM Control Requires Elevated Permissions** \n \n To start/stop VMs, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } /status/stop ` , 'POST' , { } ) ;
let output = ` ⏹️ **VM/Container Stop Command Sent** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Tip**: Use \` proxmox_get_vm_status \` to confirm it's stopped. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to stop VM/Container** \n \n Error: ${ error . message } `
} ]
} ;
}
}
async getNextVMID ( ) {
try {
const result = await this . proxmoxRequest ( '/cluster/nextid' ) ;
return {
content : [ { type : 'text' , text : ` **Next Available VM/Container ID**: ${ result } ` } ]
} ;
} catch ( error ) {
return {
content : [ { type : 'text' , text : ` ❌ **Failed to get next VMID** \n \n Error: ${ error . message } ` } ]
} ;
}
}
async deleteVM ( node , vmid , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **VM/Container Deletion Requires Elevated Permissions** \n \n To delete VMs/containers, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } ` , 'DELETE' ) ;
let output = ` 🗑️ **VM/Container Deletion Started** \n \n ` ;
output += ` • **VM/Container ID**: ${ safeVMID } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Note**: Deletion may take a moment to complete. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to delete VM/Container** \n \n Error: ${ error . message } \n \n **Note**: Make sure the VM/container is stopped first. `
} ]
} ;
}
}
async rebootVM ( node , vmid , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **VM Reboot Requires Elevated Permissions** \n \n To reboot VMs/containers, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } /status/reboot ` , 'POST' , { } ) ;
let output = ` 🔄 **VM/Container Reboot Command Sent** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Tip**: The VM/container will restart momentarily. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to reboot VM/Container** \n \n Error: ${ error . message } `
} ]
} ;
}
}
async shutdownVM ( node , vmid , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **VM Shutdown Requires Elevated Permissions** \n \n To shutdown VMs/containers, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } /status/shutdown ` , 'POST' , { } ) ;
let output = ` ⏸️ **VM/Container Shutdown Command Sent** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Note**: This is a graceful shutdown. Use \` proxmox_stop_vm \` for forceful stop. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to shutdown VM/Container** \n \n Error: ${ error . message } `
} ]
} ;
}
}
async pauseVM ( node , vmid ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **VM Pause Requires Elevated Permissions** \n \n To pause VMs, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu/ ${ safeVMID } /status/suspend ` , 'POST' , { } ) ;
let output = ` ⏸️ **VM Pause Command Sent** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Type**: QEMU \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Note**: VM is now paused. Use \` proxmox_resume_vm \` to resume. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to pause VM** \n \n Error: ${ error . message } \n \n **Note**: Pause is only available for QEMU VMs, not LXC containers. `
} ]
} ;
}
}
async resumeVM ( node , vmid ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **VM Resume Requires Elevated Permissions** \n \n To resume VMs, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu/ ${ safeVMID } /status/resume ` , 'POST' , { } ) ;
let output = ` ▶️ **VM Resume Command Sent** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Type**: QEMU \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Note**: VM is now resuming from paused state. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to resume VM** \n \n Error: ${ error . message } \n \n **Note**: Resume is only available for QEMU VMs, not LXC containers. `
} ]
} ;
}
}
async cloneVM ( node , vmid , newid , nameOrHostname , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **VM Clone Requires Elevated Permissions** \n \n To clone VMs/containers, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const safeNewID = this . validateVMID ( newid ) ;
const body = {
newid : safeNewID
} ;
// For LXC, use 'hostname', for QEMU use 'name'
if ( type === 'lxc' ) {
body . hostname = nameOrHostname || ` clone- ${ safeNewID } ` ;
} else {
body . name = nameOrHostname || ` clone- ${ safeNewID } ` ;
}
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } /clone ` , 'POST' , body ) ;
let output = ` 📋 **VM/Container Clone Started** \n \n ` ;
output += ` • **Source VM ID**: ${ safeVMID } \n ` ;
output += ` • **New VM ID**: ${ safeNewID } \n ` ;
output += ` • **New Name**: ${ nameOrHostname || ` clone- ${ safeNewID } ` } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Note**: Clone operation may take several minutes. Check task status in Proxmox. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to clone VM/Container** \n \n Error: ${ error . message } \n \n **Common issues**: \n - New VM ID already in use \n - Insufficient storage space \n - Source VM is running (some storage types require stopped VM) `
} ]
} ;
}
}
async resizeVM ( node , vmid , memory , cores , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **VM Resize Requires Elevated Permissions** \n \n To resize VMs/containers, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
// Build body with only provided parameters
const body = { } ;
if ( memory !== undefined ) {
body . memory = memory ;
}
if ( cores !== undefined ) {
body . cores = cores ;
}
if ( Object . keys ( body ) . length === 0 ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **No Resize Parameters Provided** \n \n Please specify at least one parameter: \n - \` memory \` : Memory in MB \n - \` cores \` : Number of CPU cores `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } /config ` , 'PUT' , body ) ;
let output = ` 📏 **VM/Container Resize Command Sent** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
if ( memory !== undefined ) {
output += ` • **New Memory**: ${ memory } MB \n ` ;
}
if ( cores !== undefined ) {
output += ` • **New Cores**: ${ cores } \n ` ;
}
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Note**: Some changes may require a reboot to take effect. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to resize VM/Container** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Memory/CPU values exceed node capacity \n - VM is locked or in use \n - Invalid parameter values `
} ]
} ;
}
}
async createSnapshot ( node , vmid , snapname , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Snapshot Creation Requires Elevated Permissions** \n \n To create snapshots, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } /snapshot ` , 'POST' , {
snapname : snapname
} ) ;
let output = ` 📸 **Snapshot Creation Started** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Snapshot Name**: ${ snapname } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Tip**: Use \` proxmox_list_snapshots_ ${ type === 'lxc' ? 'lxc' : 'vm' } \` to view all snapshots. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to create snapshot** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Snapshot name already exists \n - Insufficient disk space \n - VM is locked or in use `
} ]
} ;
}
}
async listSnapshots ( node , vmid , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Snapshot Listing Requires Elevated Permissions** \n \n To list snapshots, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const snapshots = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } /snapshot ` ) ;
let output = ` 📋 **Snapshots for ${ type . toUpperCase ( ) } ${ safeVMID } ** \n \n ` ;
if ( ! snapshots || snapshots . length === 0 ) {
output += ` No snapshots found. \n \n ` ;
output += ` **Tip**: Create a snapshot with \` proxmox_create_snapshot_ ${ type === 'lxc' ? 'lxc' : 'vm' } \` . \n ` ;
} else {
// Filter out 'current' pseudo-snapshot that Proxmox includes
const realSnapshots = snapshots . filter ( snap => snap . name !== 'current' ) ;
if ( realSnapshots . length === 0 ) {
output += ` No snapshots found. \n \n ` ;
output += ` **Tip**: Create a snapshot with \` proxmox_create_snapshot_ ${ type === 'lxc' ? 'lxc' : 'vm' } \` . \n ` ;
} else {
for ( const snapshot of realSnapshots ) {
output += ` • ** ${ snapshot . name } ** \n ` ;
if ( snapshot . description ) {
output += ` Description: ${ snapshot . description } \n ` ;
}
if ( snapshot . snaptime ) {
const date = new Date ( snapshot . snaptime * 1000 ) ;
output += ` Created: ${ date . toLocaleString ( ) } \n ` ;
}
output += ` \n ` ;
}
output += ` **Total**: ${ realSnapshots . length } snapshot(s) \n ` ;
}
}
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to list snapshots** \n \n Error: ${ error . message } `
} ]
} ;
}
}
async rollbackSnapshot ( node , vmid , snapname , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Snapshot Rollback Requires Elevated Permissions** \n \n To rollback snapshots, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } /snapshot/ ${ snapname } /rollback ` , 'POST' , { } ) ;
let output = ` ⏮️ **Snapshot Rollback Started** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Snapshot Name**: ${ snapname } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Warning**: This will restore the VM/container to the state of the snapshot. \n ` ;
output += ` **Tip**: Any changes made after the snapshot was created will be lost. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to rollback snapshot** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Snapshot doesn't exist \n - VM is running (stop it first) \n - VM is locked or in use `
} ]
} ;
}
}
async deleteSnapshot ( node , vmid , snapname , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Snapshot Deletion Requires Elevated Permissions** \n \n To delete snapshots, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } / ${ safeVMID } /snapshot/ ${ snapname } ` , 'DELETE' ) ;
let output = ` 🗑️ **Snapshot Deletion Started** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Snapshot Name**: ${ snapname } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Note**: Snapshot deletion may take a moment to complete. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to delete snapshot** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Snapshot doesn't exist \n - VM is locked or in use \n - Insufficient permissions `
} ]
} ;
}
}
async createBackup ( node , vmid , storage = 'local' , mode = 'snapshot' , compress = 'zstd' , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Backup Creation Requires Elevated Permissions** \n \n To create backups, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /vzdump ` , 'POST' , {
vmid : safeVMID ,
storage : storage ,
mode : mode ,
compress : compress
} ) ;
let output = ` 💾 **Backup Creation Started** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Storage**: ${ storage } \n ` ;
output += ` • **Mode**: ${ mode } \n ` ;
output += ` • **Compression**: ${ compress } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Tip**: Backup runs in the background. Use \` proxmox_list_backups \` to view all backups. \n ` ;
output += ` **Note**: Backup modes: \n ` ;
output += ` - snapshot: Quick backup using snapshots (recommended) \n ` ;
output += ` - suspend: Suspends VM during backup \n ` ;
output += ` - stop: Stops VM during backup \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to create backup** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Insufficient disk space on storage \n - VM is locked or in use \n - Invalid storage name \n - Insufficient permissions `
} ]
} ;
}
}
async listBackups ( node , storage = 'local' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Backup Listing Requires Elevated Permissions** \n \n To list backups, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const backups = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /storage/ ${ storage } /content?content=backup ` ) ;
let output = ` 📦 **Backups on ${ storage } ** \n \n ` ;
if ( ! backups || backups . length === 0 ) {
output += ` No backups found on storage \` ${ storage } \` . \n \n ` ;
output += ` **Tip**: Create a backup with \` proxmox_create_backup_lxc \` or \` proxmox_create_backup_vm \` . \n ` ;
} else {
// Sort by creation time (newest first)
backups . sort ( ( a , b ) => ( b . ctime || 0 ) - ( a . ctime || 0 ) ) ;
for ( const backup of backups ) {
// Parse backup filename to extract VM type and ID
const filename = backup . volid . split ( '/' ) . pop ( ) ;
const match = filename . match ( /vzdump-(lxc|qemu)-(\d+)-/ ) ;
const vmType = match ? match [ 1 ] . toUpperCase ( ) : 'UNKNOWN' ;
const vmId = match ? match [ 2 ] : 'N/A' ;
output += ` • ** ${ filename } ** \n ` ;
output += ` VM ID: ${ vmId } ( ${ vmType } ) \n ` ;
output += ` Size: ${ backup . size ? this . formatBytes ( backup . size ) : 'N/A' } \n ` ;
if ( backup . ctime ) {
const date = new Date ( backup . ctime * 1000 ) ;
output += ` Created: ${ date . toLocaleString ( ) } \n ` ;
}
output += ` Volume: ${ backup . volid } \n ` ;
output += ` \n ` ;
}
output += ` **Total**: ${ backups . length } backup(s) \n ` ;
}
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to list backups** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Storage doesn't exist \n - Storage is not accessible \n - Insufficient permissions `
} ]
} ;
}
}
async restoreBackup ( node , vmid , archive , storage , type = 'lxc' ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Backup Restore Requires Elevated Permissions** \n \n To restore backups, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const body = {
vmid : safeVMID ,
archive : archive ,
restore : 1
} ;
if ( storage ) {
body . storage = storage ;
}
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } / ${ type } ` , 'POST' , body ) ;
let output = ` ♻️ **Backup Restore Started** \n \n ` ;
output += ` • **New VM ID**: ${ safeVMID } \n ` ;
output += ` • **Type**: ${ type . toUpperCase ( ) } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Archive**: ${ archive } \n ` ;
if ( storage ) {
output += ` • **Storage**: ${ storage } \n ` ;
}
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Note**: Restore operation may take several minutes depending on backup size. \n ` ;
output += ` **Tip**: Use \` proxmox_get_vm_status \` to check the restored VM status after completion. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to restore backup** \n \n Error: ${ error . message } \n \n **Common issues**: \n - VM ID already in use \n - Backup archive doesn't exist \n - Insufficient storage space \n - Invalid archive path \n - Insufficient permissions `
} ]
} ;
}
}
async deleteBackup ( node , storage , volume ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Backup Deletion Requires Elevated Permissions** \n \n To delete backups, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const encodedVolume = encodeURIComponent ( volume ) ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /storage/ ${ storage } /content/ ${ encodedVolume } ` , 'DELETE' ) ;
let output = ` 🗑️ **Backup Deletion Started** \n \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Storage**: ${ storage } \n ` ;
output += ` • **Volume**: ${ volume } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Note**: Backup file will be permanently deleted from storage. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to delete backup** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Backup doesn't exist \n - Invalid volume path \n - Backup is in use \n - Insufficient permissions `
} ]
} ;
}
}
async addDiskVM ( node , vmid , disk , storage , size ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Disk Management Requires Elevated Permissions** \n \n To add disks to VMs, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const body = {
[ disk ] : ` ${ storage } : ${ size } `
} ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu/ ${ safeVMID } /config ` , 'PUT' , body ) ;
let output = ` 💿 **VM Disk Addition Started** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Disk**: ${ disk } \n ` ;
output += ` • **Storage**: ${ storage } \n ` ;
output += ` • **Size**: ${ size } GB \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Disk naming conventions**: \n ` ;
output += ` - SCSI: scsi0-15 \n ` ;
output += ` - VirtIO: virtio0-15 \n ` ;
output += ` - SATA: sata0-5 \n ` ;
output += ` - IDE: ide0-3 \n \n ` ;
output += ` **Note**: The VM may need to be stopped for this operation depending on configuration. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to add disk to VM** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Disk name already in use \n - VM is running (may need to be stopped) \n - Invalid disk name format \n - Insufficient storage space \n - Storage doesn't exist `
} ]
} ;
}
}
async addMountPointLXC ( node , vmid , mp , storage , size ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Disk Management Requires Elevated Permissions** \n \n To add mount points to containers, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const body = {
[ mp ] : ` ${ storage } : ${ size } `
} ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /lxc/ ${ safeVMID } /config ` , 'PUT' , body ) ;
let output = ` 💿 **LXC Mount Point Addition Started** \n \n ` ;
output += ` • **Container ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Mount Point**: ${ mp } \n ` ;
output += ` • **Storage**: ${ storage } \n ` ;
output += ` • **Size**: ${ size } GB \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Mount point naming**: mp0-255 \n \n ` ;
output += ` **Note**: The container may need to be stopped for this operation. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to add mount point to container** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Mount point name already in use \n - Container is running (may need to be stopped) \n - Invalid mount point name \n - Insufficient storage space \n - Storage doesn't exist `
} ]
} ;
}
}
async resizeDiskVM ( node , vmid , disk , size ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Disk Management Requires Elevated Permissions** \n \n To resize VM disks, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const body = {
disk : disk ,
size : size
} ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu/ ${ safeVMID } /resize ` , 'PUT' , body ) ;
let output = ` 📏 **VM Disk Resize Started** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Disk**: ${ disk } \n ` ;
output += ` • **New Size**: ${ size } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Size format examples**: \n ` ;
output += ` - +10G: Add 10GB to current size \n ` ;
output += ` - 50G: Set absolute size to 50GB \n \n ` ;
output += ` **Note**: Disks can only be expanded, not shrunk. Some configurations allow online resizing. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to resize VM disk** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Disk doesn't exist \n - Trying to shrink disk (not supported) \n - Insufficient storage space \n - Invalid size format \n - VM is locked or in use `
} ]
} ;
}
}
async resizeDiskLXC ( node , vmid , disk , size ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Disk Management Requires Elevated Permissions** \n \n To resize LXC disks, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const body = {
disk : disk ,
size : size
} ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /lxc/ ${ safeVMID } /resize ` , 'PUT' , body ) ;
let output = ` 📏 **LXC Disk Resize Started** \n \n ` ;
output += ` • **Container ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Disk**: ${ disk } \n ` ;
output += ` • **New Size**: ${ size } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Size format examples**: \n ` ;
output += ` - +10G: Add 10GB to current size \n ` ;
output += ` - 50G: Set absolute size to 50GB \n \n ` ;
output += ` **Valid disk names**: rootfs, mp0, mp1, mp2, etc. \n \n ` ;
output += ` **Note**: Disks can only be expanded, not shrunk. Container may need to be stopped. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to resize LXC disk** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Disk doesn't exist \n - Trying to shrink disk (not supported) \n - Insufficient storage space \n - Invalid size format \n - Container is locked or in use `
} ]
} ;
}
}
async removeDiskVM ( node , vmid , disk ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Disk Management Requires Elevated Permissions** \n \n To remove disks from VMs, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const body = {
delete : disk
} ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu/ ${ safeVMID } /config ` , 'PUT' , body ) ;
let output = ` ➖ **VM Disk Removal Started**\n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Disk**: ${ disk } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Warning**: This will permanently delete the disk and all its data. \n ` ;
output += ` **Note**: The VM should be stopped for this operation. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to remove disk from VM** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Disk doesn't exist \n - VM is running (must be stopped) \n - Cannot remove boot disk \n - VM is locked or in use `
} ]
} ;
}
}
async removeMountPointLXC ( node , vmid , mp ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Disk Management Requires Elevated Permissions** \n \n To remove mount points from containers, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const body = {
delete : mp
} ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /lxc/ ${ safeVMID } /config ` , 'PUT' , body ) ;
let output = ` ➖ **LXC Mount Point Removal Started**\n \n ` ;
output += ` • **Container ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Mount Point**: ${ mp } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Warning**: This will permanently delete the mount point and all its data. \n ` ;
output += ` **Note**: The container should be stopped for this operation. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to remove mount point from container** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Mount point doesn't exist \n - Container is running (must be stopped) \n - Cannot remove rootfs \n - Container is locked or in use `
} ]
} ;
}
}
async moveDiskVM ( node , vmid , disk , storage , deleteSource = true ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Disk Management Requires Elevated Permissions** \n \n To move VM disks, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const body = {
disk : disk ,
storage : storage ,
delete : deleteSource ? 1 : 0
} ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu/ ${ safeVMID } /move_disk ` , 'POST' , body ) ;
let output = ` 📦 **VM Disk Move Started** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Disk**: ${ disk } \n ` ;
output += ` • **Target Storage**: ${ storage } \n ` ;
output += ` • **Delete Source**: ${ deleteSource ? 'Yes' : 'No' } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Note**: Disk move operation may take several minutes depending on disk size. \n ` ;
output += ` **Tip**: The VM should be stopped for this operation in most configurations. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to move VM disk** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Disk doesn't exist \n - Target storage doesn't exist \n - Insufficient space on target storage \n - VM is running (may need to be stopped) \n - VM is locked or in use `
} ]
} ;
}
}
async moveDiskLXC ( node , vmid , disk , storage , deleteSource = true ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Disk Management Requires Elevated Permissions** \n \n To move LXC disks, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const body = {
volume : disk ,
storage : storage ,
delete : deleteSource ? 1 : 0
} ;
const result = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /lxc/ ${ safeVMID } /move_volume ` , 'POST' , body ) ;
let output = ` 📦 **LXC Disk Move Started** \n \n ` ;
output += ` • **Container ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Volume**: ${ disk } \n ` ;
output += ` • **Target Storage**: ${ storage } \n ` ;
output += ` • **Delete Source**: ${ deleteSource ? 'Yes' : 'No' } \n ` ;
output += ` • **Task ID**: ${ result || 'N/A' } \n \n ` ;
output += ` **Valid volumes**: rootfs, mp0, mp1, mp2, etc. \n \n ` ;
output += ` **Note**: Volume move operation may take several minutes depending on volume size. \n ` ;
output += ` **Tip**: The container should be stopped for this operation. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to move LXC volume** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Volume doesn't exist \n - Target storage doesn't exist \n - Insufficient space on target storage \n - Container is running (may need to be stopped) \n - Container is locked or in use `
} ]
} ;
}
}
async addNetworkVM ( node , vmid , net , bridge , model = 'virtio' , macaddr , vlan , firewall ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Network Management Requires Elevated Permissions** \n \n To add VM network interfaces, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
// Build network configuration string
let netConfig = ` ${ model || 'virtio' } ,bridge= ${ bridge } ` ;
if ( macaddr ) {
netConfig += ` ,macaddr= ${ macaddr } ` ;
}
if ( vlan !== undefined && vlan !== null ) {
netConfig += ` ,tag= ${ vlan } ` ;
2025-06-05 14:31:32 -05:00
}
2025-11-06 11:13:58 -06:00
if ( firewall ) {
netConfig += ` ,firewall=1 ` ;
}
const body = {
[ net ] : netConfig
} ;
await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu/ ${ safeVMID } /config ` , 'PUT' , body ) ;
let output = ` 🌐 **VM Network Interface Added** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Interface**: ${ net } \n ` ;
output += ` • **Bridge**: ${ bridge } \n ` ;
output += ` • **Model**: ${ model || 'virtio' } \n ` ;
if ( macaddr ) output += ` • **MAC Address**: ${ macaddr } \n ` ;
if ( vlan !== undefined && vlan !== null ) output += ` • **VLAN Tag**: ${ vlan } \n ` ;
if ( firewall ) output += ` • **Firewall**: Enabled \n ` ;
output += ` \n **Valid interfaces**: net0, net1, net2, etc. \n ` ;
output += ` **Valid models**: virtio (recommended), e1000, rtl8139, vmxnet3 \n ` ;
output += ` **Valid bridges**: vmbr0, vmbr1, vmbr2, etc. \n \n ` ;
output += ` **Tip**: Use virtio model for best performance with modern guests. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to add VM network interface** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Network interface already exists \n - Bridge doesn't exist \n - Invalid MAC address format \n - Invalid VLAN tag (must be 1-4094) \n - VM is locked or in use `
} ]
} ;
2025-06-05 14:31:32 -05:00
}
}
2025-11-06 11:13:58 -06:00
async addNetworkLXC ( node , vmid , net , bridge , ip , gw , firewall ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Network Management Requires Elevated Permissions** \n \n To add LXC network interfaces, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
2025-06-05 14:31:32 -05:00
try {
2025-11-06 11:13:58 -06:00
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
// Extract interface number (e.g., net0 -> 0, net1 -> 1)
const netNum = net . replace ( 'net' , '' ) ;
// Build network configuration string
let netConfig = ` name=eth ${ netNum } ,bridge= ${ bridge } ` ;
if ( ip ) {
netConfig += ` ,ip= ${ ip } ` ;
2025-06-05 14:31:32 -05:00
}
2025-11-06 11:13:58 -06:00
if ( gw ) {
netConfig += ` ,gw= ${ gw } ` ;
}
if ( firewall ) {
netConfig += ` ,firewall=1 ` ;
}
const body = {
[ net ] : netConfig
} ;
await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /lxc/ ${ safeVMID } /config ` , 'PUT' , body ) ;
let output = ` 🌐 **LXC Network Interface Added** \n \n ` ;
output += ` • **Container ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Interface**: ${ net } (eth ${ netNum } ) \n ` ;
output += ` • **Bridge**: ${ bridge } \n ` ;
if ( ip ) output += ` • **IP Address**: ${ ip } \n ` ;
if ( gw ) output += ` • **Gateway**: ${ gw } \n ` ;
if ( firewall ) output += ` • **Firewall**: Enabled \n ` ;
output += ` \n **Valid interfaces**: net0, net1, net2, etc. \n ` ;
output += ` **Valid bridges**: vmbr0, vmbr1, vmbr2, etc. \n ` ;
output += ` **IP formats**: dhcp, 192.168.1.100/24, auto \n \n ` ;
output += ` **Tip**: Use DHCP for automatic IP assignment or specify static IP with CIDR notation. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to add LXC network interface** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Network interface already exists \n - Bridge doesn't exist \n - Invalid IP address format \n - Invalid gateway address \n - Container is locked or in use `
} ]
} ;
}
}
async updateNetworkVM ( node , vmid , net , bridge , model , macaddr , vlan , firewall ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Network Management Requires Elevated Permissions** \n \n To update VM network interfaces, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
// Get current VM configuration
const config = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu/ ${ safeVMID } /config ` , 'GET' ) ;
if ( ! config [ net ] ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Network interface ${ net } does not exist** \n \n Please add the interface first using proxmox_add_network_vm. \n \n **Existing interfaces**: ${ Object . keys ( config ) . filter ( k => k . startsWith ( 'net' ) ) . join ( ', ' ) || 'None' } `
} ]
} ;
}
// Parse current configuration
const currentConfig = config [ net ] ;
const configParts = { } ;
currentConfig . split ( ',' ) . forEach ( part => {
const [ key , value ] = part . split ( '=' ) ;
configParts [ key ] = value ;
} ) ;
// Update only provided parameters
if ( model !== undefined ) {
// Extract MAC if present, remove old model
const mac = configParts . macaddr || configParts [ Object . keys ( configParts ) . find ( k => k . match ( /^[0-9A-F]{2}:/i ) ) ] ;
configParts [ model ] = mac || '' ;
// Remove old model keys
[ 'virtio' , 'e1000' , 'rtl8139' , 'vmxnet3' ] . forEach ( m => {
if ( m !== model ) delete configParts [ m ] ;
} ) ;
}
if ( bridge !== undefined ) {
configParts . bridge = bridge ;
}
if ( macaddr !== undefined ) {
configParts . macaddr = macaddr ;
}
if ( vlan !== undefined && vlan !== null ) {
configParts . tag = vlan ;
} else if ( vlan === null ) {
delete configParts . tag ;
}
if ( firewall !== undefined ) {
if ( firewall ) {
configParts . firewall = '1' ;
} else {
delete configParts . firewall ;
2025-06-05 14:31:32 -05:00
}
}
2025-11-06 11:13:58 -06:00
// Rebuild configuration string
const netConfig = Object . entries ( configParts )
. map ( ( [ key , value ] ) => value ? ` ${ key } = ${ value } ` : key )
. join ( ',' ) ;
const body = {
[ net ] : netConfig
} ;
await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu/ ${ safeVMID } /config ` , 'PUT' , body ) ;
let output = ` 🔧 **VM Network Interface Updated** \n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Interface**: ${ net } \n ` ;
output += ` • **New Configuration**: ${ netConfig } \n \n ` ;
output += ` **Changes applied**: \n ` ;
if ( bridge !== undefined ) output += ` - Bridge: ${ bridge } \n ` ;
if ( model !== undefined ) output += ` - Model: ${ model } \n ` ;
if ( macaddr !== undefined ) output += ` - MAC Address: ${ macaddr } \n ` ;
if ( vlan !== undefined ) output += ` - VLAN Tag: ${ vlan !== null ? vlan : 'Removed' } \n ` ;
if ( firewall !== undefined ) output += ` - Firewall: ${ firewall ? 'Enabled' : 'Disabled' } \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to update VM network interface** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Network interface doesn't exist \n - Bridge doesn't exist \n - Invalid MAC address format \n - Invalid VLAN tag (must be 1-4094) \n - VM is locked or in use `
} ]
} ;
}
}
async updateNetworkLXC ( node , vmid , net , bridge , ip , gw , firewall ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Network Management Requires Elevated Permissions** \n \n To update LXC network interfaces, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
// Get current container configuration
const config = await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /lxc/ ${ safeVMID } /config ` , 'GET' ) ;
if ( ! config [ net ] ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Network interface ${ net } does not exist** \n \n Please add the interface first using proxmox_add_network_lxc. \n \n **Existing interfaces**: ${ Object . keys ( config ) . filter ( k => k . startsWith ( 'net' ) ) . join ( ', ' ) || 'None' } `
} ]
} ;
2025-06-05 14:31:32 -05:00
}
2025-11-06 11:13:58 -06:00
// Parse current configuration
const currentConfig = config [ net ] ;
const configParts = { } ;
currentConfig . split ( ',' ) . forEach ( part => {
const [ key , value ] = part . split ( '=' ) ;
configParts [ key ] = value ;
} ) ;
// Update only provided parameters
if ( bridge !== undefined ) {
configParts . bridge = bridge ;
}
if ( ip !== undefined ) {
configParts . ip = ip ;
}
if ( gw !== undefined ) {
configParts . gw = gw ;
}
if ( firewall !== undefined ) {
if ( firewall ) {
configParts . firewall = '1' ;
} else {
delete configParts . firewall ;
}
}
// Rebuild configuration string
const netConfig = Object . entries ( configParts )
. map ( ( [ key , value ] ) => ` ${ key } = ${ value } ` )
. join ( ',' ) ;
const body = {
[ net ] : netConfig
} ;
await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /lxc/ ${ safeVMID } /config ` , 'PUT' , body ) ;
let output = ` 🔧 **LXC Network Interface Updated** \n \n ` ;
output += ` • **Container ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Interface**: ${ net } \n ` ;
output += ` • **New Configuration**: ${ netConfig } \n \n ` ;
output += ` **Changes applied**: \n ` ;
if ( bridge !== undefined ) output += ` - Bridge: ${ bridge } \n ` ;
if ( ip !== undefined ) output += ` - IP Address: ${ ip } \n ` ;
if ( gw !== undefined ) output += ` - Gateway: ${ gw } \n ` ;
if ( firewall !== undefined ) output += ` - Firewall: ${ firewall ? 'Enabled' : 'Disabled' } \n ` ;
2025-06-05 14:31:32 -05:00
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
2025-11-06 11:13:58 -06:00
content : [ {
type : 'text' ,
text : ` ❌ **Failed to update LXC network interface** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Network interface doesn't exist \n - Bridge doesn't exist \n - Invalid IP address format \n - Invalid gateway address \n - Container is locked or in use `
} ]
} ;
}
}
async removeNetworkVM ( node , vmid , net ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Network Management Requires Elevated Permissions** \n \n To remove VM network interfaces, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const body = {
delete : net
} ;
await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /qemu/ ${ safeVMID } /config ` , 'PUT' , body ) ;
let output = ` ➖ **VM Network Interface Removed**\n \n ` ;
output += ` • **VM ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Interface Removed**: ${ net } \n \n ` ;
output += ` **Note**: The network interface has been removed from the VM configuration. \n ` ;
output += ` **Tip**: If the VM is running, you may need to restart it for changes to take effect. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to remove VM network interface** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Network interface doesn't exist \n - VM is locked or in use \n - Invalid interface name `
} ]
} ;
}
}
async removeNetworkLXC ( node , vmid , net ) {
if ( ! this . allowElevated ) {
return {
content : [ {
type : 'text' ,
text : ` ⚠️ **Network Management Requires Elevated Permissions** \n \n To remove LXC network interfaces, set \` PROXMOX_ALLOW_ELEVATED=true \` in your .env file. \n \n **Current permissions**: Basic (read-only) `
} ]
} ;
}
try {
// Validate inputs
const safeNode = this . validateNodeName ( node ) ;
const safeVMID = this . validateVMID ( vmid ) ;
const body = {
delete : net
} ;
await this . proxmoxRequest ( ` /nodes/ ${ safeNode } /lxc/ ${ safeVMID } /config ` , 'PUT' , body ) ;
let output = ` ➖ **LXC Network Interface Removed**\n \n ` ;
output += ` • **Container ID**: ${ safeVMID } \n ` ;
output += ` • **Node**: ${ safeNode } \n ` ;
output += ` • **Interface Removed**: ${ net } \n \n ` ;
output += ` **Note**: The network interface has been removed from the container configuration. \n ` ;
output += ` **Tip**: If the container is running, you may need to restart it for changes to take effect. \n ` ;
return {
content : [ { type : 'text' , text : output } ]
} ;
} catch ( error ) {
return {
content : [ {
type : 'text' ,
text : ` ❌ **Failed to remove LXC network interface** \n \n Error: ${ error . message } \n \n **Common issues**: \n - Network interface doesn't exist \n - Container is locked or in use \n - Invalid interface name `
2025-06-05 14:31:32 -05:00
} ]
} ;
}
}
formatUptime ( seconds ) {
const days = Math . floor ( seconds / 86400 ) ;
const hours = Math . floor ( ( seconds % 86400 ) / 3600 ) ;
const minutes = Math . floor ( ( seconds % 3600 ) / 60 ) ;
if ( days > 0 ) {
return ` ${ days } d ${ hours } h ${ minutes } m ` ;
} else if ( hours > 0 ) {
return ` ${ hours } h ${ minutes } m ` ;
} else {
return ` ${ minutes } m ` ;
}
}
formatBytes ( bytes ) {
if ( bytes === 0 ) return '0 B' ;
const k = 1024 ;
const sizes = [ 'B' , 'KB' , 'MB' , 'GB' , 'TB' ] ;
const i = Math . floor ( Math . log ( bytes ) / Math . log ( k ) ) ;
return parseFloat ( ( bytes / Math . pow ( k , i ) ) . toFixed ( 2 ) ) + ' ' + sizes [ i ] ;
}
async run ( ) {
const transport = new StdioServerTransport ( ) ;
await this . server . connect ( transport ) ;
console . error ( 'Proxmox MCP server running on stdio' ) ;
}
}
const server = new ProxmoxServer ( ) ;
server . run ( ) . catch ( console . error ) ;