Skip to main content
Glama
jkmills

Nutanix MCP Server

by jkmills

generate_vm_report

Generate detailed As-Built reports for one or more VMs, covering configuration, compute, disks, NICs, categories, and host placement, with an optional Excalidraw diagram.

Instructions

Generate a detailed As-Built report for one or more VMs. Covers VM configuration, compute resources, disks, NICs, categories, and host placement. Returns Markdown with an Excalidraw VM layout diagram.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
vm_uuidsYesList of VM UUIDs to report on (required).
include_diagramNoInclude Excalidraw VM diagram JSON (default: true)

Implementation Reference

  • Main handler function for generate_vm_report. Accepts vm_uuids (required) and include_diagram (optional). Fetches VM details via v4_get for each UUID, builds Markdown report via _build_vm_markdown, and optionally generates Excalidraw diagram via _generate_vm_diagram.
    async def handle_generate_vm_report(
        client: NutanixClient, arguments: dict[str, Any]
    ) -> dict[str, Any]:
        """Generate detailed VM As-Built report."""
        vm_uuids = arguments["vm_uuids"]
        include_diagram = arguments.get("include_diagram", True)
    
        # Fetch detailed info for each VM
        vms = []
        for vm_uuid in vm_uuids:
            try:
                vm_result = await client.v4_get(
                    namespace="vmm", path=f"ahv/config/vms/{vm_uuid}"
                )
                vms.append(vm_result.get("data", {}))
            except Exception as e:
                vms.append({"name": f"Error: {vm_uuid}", "extId": vm_uuid, "error": str(e)})
    
        # Build markdown
        markdown = _build_vm_markdown(vms)
    
        result: dict[str, Any] = {"report_markdown": markdown}
    
        # Build diagram
        if include_diagram:
            diagram_elements = _generate_vm_diagram(vms)
            result["excalidraw_diagram"] = {
                "type": "excalidraw",
                "version": 2,
                "elements": diagram_elements,
                "appState": {"viewBackgroundColor": "#ffffff"},
            }
    
        return result
  • Tool definition and input schema for generate_vm_report. Defines the 'vm_uuids' (required array of strings) and 'include_diagram' (optional boolean, default true) input parameters.
    {
        "name": "generate_vm_report",
        "description": (
            "Generate a detailed As-Built report for one or more VMs. "
            "Covers VM configuration, compute resources, disks, NICs, categories, "
            "and host placement. Returns Markdown with an Excalidraw VM layout diagram."
        ),
        "inputSchema": {
            "type": "object",
            "properties": {
                "vm_uuids": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "List of VM UUIDs to report on (required).",
                },
                "include_diagram": {
                    "type": "boolean",
                    "description": "Include Excalidraw VM diagram JSON (default: true)",
                    "default": True,
                },
            },
            "required": ["vm_uuids"],
        },
    },
  • Handler dispatch dict REPORT_HANDLERS that maps 'generate_vm_report' to handle_generate_vm_report function.
    # ─── Handler Dispatch ─────────────────────────────────────────────────────────
    
    REPORT_HANDLERS: dict[str, Any] = {
        "generate_environment_report": handle_generate_environment_report,
        "generate_cluster_report": handle_generate_cluster_report,
        "generate_vm_report": handle_generate_vm_report,
    }
  • Tool registry that imports REPORT_TOOLS from report.py and exposes them via get_all_tools() aggregation function.
    """Tool registry and definitions."""
    
    from nutanix_mcp.tools.vm import VM_TOOLS
    from nutanix_mcp.tools.cluster import CLUSTER_TOOLS
    from nutanix_mcp.tools.prism_element import PE_TOOLS
    from nutanix_mcp.tools.report import REPORT_TOOLS
    from nutanix_mcp.tools.networking import NETWORKING_TOOLS
    
    
    def get_all_tools() -> list[dict]:
        """Return all registered tool definitions."""
        return VM_TOOLS + CLUSTER_TOOLS + PE_TOOLS + REPORT_TOOLS + NETWORKING_TOOLS
  • _build_vm_markdown helper that builds the detailed Markdown report for VMs, covering configuration, compute, disks, NICs, GPUs, boot config, and categories.
    def _build_vm_markdown(vms: list[dict]) -> str:
        """Build detailed VM Markdown report."""
        lines = []
        lines.append("# Nutanix As-Built Report — Virtual Machines")
        lines.append(f"\n**Generated:** {_now_iso()}")
        lines.append(f"**VMs in Report:** {len(vms)}")
        lines.append("")
    
        for vm in vms:
            vm_name = vm.get("name", "N/A")
            ext_id = vm.get("extId", "N/A")
            power_state = vm.get("powerState", "N/A")
    
            lines.append(f"## {vm_name}")
            lines.append("")
    
            # Basic info
            lines.append("### Configuration")
            lines.append("")
            lines.append("| Property | Value |")
            lines.append("|----------|-------|")
            lines.append(f"| Name | {vm_name} |")
            lines.append(f"| UUID | `{ext_id}` |")
            lines.append(f"| Power State | {power_state} |")
            lines.append(f"| Description | {vm.get('description', 'N/A')} |")
            lines.append(f"| Guest OS | {vm.get('guestCustomization', {}).get('config', {}).get('sysprep', {}).get('installType', 'N/A')} |")
            lines.append(f"| Machine Type | {vm.get('machineType', 'N/A')} |")
            lines.append(f"| Hardware Clock Timezone | {vm.get('hardwareClockTimezone', 'N/A')} |")
    
            # Compute
            num_sockets = vm.get("numSockets", 1)
            cores_per_socket = vm.get("numCoresPerSocket", 1)
            threads_per_core = vm.get("numThreadsPerCore", 1)
            total_vcpus = num_sockets * cores_per_socket
            mem_bytes = vm.get("memorySizeBytes", 0)
    
            lines.append(f"| CPU Sockets | {num_sockets} |")
            lines.append(f"| Cores per Socket | {cores_per_socket} |")
            lines.append(f"| Threads per Core | {threads_per_core} |")
            lines.append(f"| Total vCPUs | {total_vcpus} |")
            lines.append(f"| Memory | {_format_bytes(mem_bytes)} |")
    
            # Cluster/Host
            cluster_info = vm.get("cluster", {})
            host_info = vm.get("host", {})
            lines.append(f"| Cluster | {cluster_info.get('name', cluster_info.get('extId', 'N/A'))} |")
            lines.append(f"| Host | {host_info.get('name', host_info.get('extId', 'N/A'))} |")
    
            # Categories
            categories = vm.get("categories", [])
            if categories:
                cat_str = ", ".join(f"{c.get('key', '')}:{c.get('value', '')}" for c in categories)
                lines.append(f"| Categories | {cat_str} |")
    
            lines.append("")
    
            # Disks
            disks = vm.get("disks", [])
            if disks:
                lines.append("### Disks")
                lines.append("")
                lines.append("| # | Type | Size | Bus | Controller | Storage Container |")
                lines.append("|---|------|------|-----|------------|-------------------|")
                for idx, disk in enumerate(disks):
                    d_type = disk.get("diskType", "N/A")
                    d_size = _format_bytes(disk.get("diskSizeBytes"))
                    bus = disk.get("busType", "N/A")
                    addr = disk.get("deviceAddress", {})
                    controller = f"{addr.get('busType', '')}{addr.get('index', '')}" if addr else "N/A"
                    sc_ref = _safe_get(disk, "storageConfig", "storageContainerReference", "extId")
                    lines.append(f"| {idx+1} | {d_type} | {d_size} | {bus} | {controller} | `{sc_ref}` |")
                lines.append("")
    
            # NICs
            nics = vm.get("nics", [])
            if nics:
                lines.append("### Network Interfaces")
                lines.append("")
                lines.append("| # | Type | Subnet | MAC | IP Address | VLAN Mode |")
                lines.append("|---|------|--------|-----|------------|-----------|")
                for idx, nic in enumerate(nics):
                    n_type = nic.get("nicType", "N/A")
                    subnet = _safe_get(nic, "subnet", "name")
                    mac = nic.get("macAddress", "N/A")
                    ip = _safe_get(nic, "ipAddress", "ip")
                    vlan_mode = nic.get("vlanMode", "N/A")
                    lines.append(f"| {idx+1} | {n_type} | {subnet} | {mac} | {ip} | {vlan_mode} |")
                lines.append("")
    
            # GPU
            gpus = vm.get("gpus", [])
            if gpus:
                lines.append("### GPUs")
                lines.append("")
                lines.append("| Mode | Vendor | Device |")
                lines.append("|------|--------|--------|")
                for gpu in gpus:
                    lines.append(f"| {gpu.get('mode', 'N/A')} | {gpu.get('vendor', 'N/A')} | {gpu.get('deviceName', 'N/A')} |")
                lines.append("")
    
            # Boot config
            boot_config = vm.get("bootConfig", {})
            if boot_config:
                lines.append("### Boot Configuration")
                lines.append("")
                lines.append(f"- Boot Type: {boot_config.get('bootType', 'N/A')}")
                boot_device = boot_config.get("bootDeviceOrder", [])
                if boot_device:
                    lines.append(f"- Boot Device Order: {', '.join(boot_device)}")
                lines.append("")
    
            lines.append("---")
            lines.append("")
    
        return "\n".join(lines)
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full responsibility. It discloses that the tool returns Markdown with an Excalidraw diagram, but does not state whether the operation is read-only, requires special authorization, has rate limits, or any potential side effects. The behavioral profile is minimal.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise with two well-structured sentences. The first sentence states the purpose and scope, the second specifies the output format. No unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the moderate complexity (generates a detailed report with a diagram) and lack of output schema, the description covers key aspects: what the report includes, the output format. It does not address error handling, limitations on VM count, or performance, but remains reasonably complete for typical usage.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with clear descriptions for both parameters. The description adds context about the report's content (e.g., compute, disks, NICs), which helps understand the output but does not elaborate on parameter constraints or usage beyond the schema. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool generates a detailed As-Built report for one or more VMs, covering configuration, compute, disks, NICs, categories, and host placement. This distinguishes it from sibling tools like generate_cluster_report or get_vm.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives such as get_vm or generate_cluster_report. It does not specify prerequisites, context, or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jkmills/nutanix-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server