fix: remove broken SSL client helpers; use target verify_ssl from connect
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
from proxmox_mcp import __version__
|
||||
@@ -16,144 +13,18 @@ mcp = FastMCP(
|
||||
"proxmox",
|
||||
instructions=(
|
||||
"Proxmox VE 9 MCP. Saved targets load from PROXMOX_TARGETS_PATH. "
|
||||
"Call proxmox_connect(url, token_id, token_secret, name=...) or proxmox_use_target. "
|
||||
"Exec tools require PROXMOX_ALLOW_WRITES=true."
|
||||
"Call proxmox_connect(url, token_id, token_secret, verify_ssl=false, name=...) "
|
||||
"or proxmox_use_target. Exec tools require PROXMOX_ALLOW_WRITES=true."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _get_active_client() -> httpx.Client | None:
|
||||
"""Return an authenticated httpx client for the active target, or None."""
|
||||
target = registry.active()
|
||||
if not target:
|
||||
return None
|
||||
verify = os.environ.get("PROXMOX_VERIFY_SSL", "true").lower() in ("1", "true", "yes")
|
||||
client = httpx.Client(
|
||||
base_url=target.url,
|
||||
verify=verify,
|
||||
headers={"Authorization": f"PVEAPIToken={target.token_id}={target.token_secret}"},
|
||||
timeout=30.0,
|
||||
)
|
||||
return client
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def proxmox_list_all_guests() -> str:
|
||||
"""List all QEMU VMs and LXC containers across all Proxmox cluster nodes.
|
||||
|
||||
This tool uses the cluster resources endpoint, which does not require a node
|
||||
parameter. It returns a combined list of all VMs and containers regardless
|
||||
of which node they reside on.
|
||||
"""
|
||||
client = _get_active_client()
|
||||
if not client:
|
||||
return json.dumps({"error": "No active target. Call proxmox_connect or proxmox_use_target first."})
|
||||
|
||||
try:
|
||||
# Query cluster resources for both VMs and containers
|
||||
resp = client.get("/api2/json/cluster/resources", params={"type": "vm"})
|
||||
resp.raise_for_status()
|
||||
data = resp.json().get("data", [])
|
||||
|
||||
# Filter: vmid with status and type
|
||||
guests = []
|
||||
for entry in data:
|
||||
if entry.get("type") in ("qemu", "lxc"):
|
||||
guests.append({
|
||||
"vmid": entry.get("vmid"),
|
||||
"node": entry.get("node"),
|
||||
"name": entry.get("name"),
|
||||
"type": entry.get("type"),
|
||||
"status": entry.get("status"),
|
||||
"mem": entry.get("mem"),
|
||||
"maxmem": entry.get("maxmem"),
|
||||
"cpu": entry.get("cpu"),
|
||||
"maxcpu": entry.get("maxcpu"),
|
||||
"disk": entry.get("disk"),
|
||||
"maxdisk": entry.get("maxdisk"),
|
||||
"uptime": entry.get("uptime"),
|
||||
})
|
||||
return json.dumps(guests, indent=2)
|
||||
except Exception as exc:
|
||||
return json.dumps({"error": str(exc)})
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def proxmox_debug_guest_inventory(node: str | None = None) -> str:
|
||||
"""Return the raw JSON from the Proxmox API for debugging guest listing.
|
||||
|
||||
If a node is given, queries /nodes/{node}/qemu and /nodes/{node}/lxc separately.
|
||||
Otherwise, queries the cluster resources endpoint.
|
||||
"""
|
||||
client = _get_active_client()
|
||||
if not client:
|
||||
return json.dumps({"error": "No active target. Call proxmox_connect or proxmox_use_target first."})
|
||||
|
||||
try:
|
||||
result = {}
|
||||
if node:
|
||||
# Per-node endpoints
|
||||
for gtype in ("qemu", "lxc"):
|
||||
resp = client.get(f"/api2/json/nodes/{node}/{gtype}")
|
||||
resp.raise_for_status()
|
||||
result[gtype] = resp.json().get("data", [])
|
||||
else:
|
||||
# Cluster resources
|
||||
resp = client.get("/api2/json/cluster/resources", params={"type": "vm"})
|
||||
resp.raise_for_status()
|
||||
result = resp.json()
|
||||
return json.dumps(result, indent=2, default=str)
|
||||
except Exception as exc:
|
||||
return json.dumps({"error": str(exc)})
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def proxmox_list_qemu(node: str) -> str:
|
||||
"""List all QEMU VMs on a specific Proxmox node."""
|
||||
client = _get_active_client()
|
||||
if not client:
|
||||
return json.dumps({"error": "No active target. Call proxmox_connect or proxmox_use_target first."})
|
||||
|
||||
try:
|
||||
resp = client.get(f"/api2/json/nodes/{node}/qemu")
|
||||
resp.raise_for_status()
|
||||
vms = resp.json().get("data", [])
|
||||
return json.dumps(vms, indent=2)
|
||||
except Exception as exc:
|
||||
return json.dumps({"error": str(exc)})
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def proxmox_list_lxc(node: str) -> str:
|
||||
"""List all LXC containers on a specific Proxmox node."""
|
||||
client = _get_active_client()
|
||||
if not client:
|
||||
return json.dumps({"error": "No active target. Call proxmox_connect or proxmox_use_target first."})
|
||||
|
||||
try:
|
||||
resp = client.get(f"/api2/json/nodes/{node}/lxc")
|
||||
resp.raise_for_status()
|
||||
lxcs = resp.json().get("data", [])
|
||||
return json.dumps(lxcs, indent=2)
|
||||
except Exception as exc:
|
||||
return json.dumps({"error": str(exc)})
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def proxmox_get_config() -> str:
|
||||
from proxmox_mcp.targets import registry
|
||||
|
||||
return json.dumps(
|
||||
{
|
||||
"allow_writes": settings.allow_writes,
|
||||
"verify_ssl_default": settings.verify_ssl,
|
||||
"active_target": registry.active().name if registry.active() else None,
|
||||
"targets": registry.list(),
|
||||
"version": __version__,
|
||||
|
||||
Reference in New Issue
Block a user