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
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
from proxmox_mcp import __version__
|
from proxmox_mcp import __version__
|
||||||
@@ -16,144 +13,18 @@ mcp = FastMCP(
|
|||||||
"proxmox",
|
"proxmox",
|
||||||
instructions=(
|
instructions=(
|
||||||
"Proxmox VE 9 MCP. Saved targets load from PROXMOX_TARGETS_PATH. "
|
"Proxmox VE 9 MCP. Saved targets load from PROXMOX_TARGETS_PATH. "
|
||||||
"Call proxmox_connect(url, token_id, token_secret, name=...) or proxmox_use_target. "
|
"Call proxmox_connect(url, token_id, token_secret, verify_ssl=false, name=...) "
|
||||||
"Exec tools require PROXMOX_ALLOW_WRITES=true."
|
"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()
|
@mcp.tool()
|
||||||
def proxmox_get_config() -> str:
|
def proxmox_get_config() -> str:
|
||||||
from proxmox_mcp.targets import registry
|
|
||||||
|
|
||||||
return json.dumps(
|
return json.dumps(
|
||||||
{
|
{
|
||||||
"allow_writes": settings.allow_writes,
|
"allow_writes": settings.allow_writes,
|
||||||
|
"verify_ssl_default": settings.verify_ssl,
|
||||||
"active_target": registry.active().name if registry.active() else None,
|
"active_target": registry.active().name if registry.active() else None,
|
||||||
"targets": registry.list(),
|
"targets": registry.list(),
|
||||||
"version": __version__,
|
"version": __version__,
|
||||||
|
|||||||
Reference in New Issue
Block a user