Skip to main content
Glama

vm-count-by-tenant

Count virtual machines across Azure tenants and subscriptions using Azure Resource Graph queries to track VM inventory in multi-tenant environments.

Instructions

Count virtual machines per configured tenant (uses all subscriptions when available).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
tenant_namesNoOptional subset of tenant names to include
use_all_subscriptionsNoTry to include all subscriptions in each tenant (fallbacks to default)

Implementation Reference

  • Handler logic for the 'vm-count-by-tenant' tool within the call_tool function. Loops over tenants, scopes subscriptions or management group, executes vm_count_kql query, aggregates and formats VM counts.
    if name == "vm-count-by-tenant":
        # Optional: restrict to certain tenants
        wanted = set(arguments.get("tenant_names", []) or [])
        try_all = bool(arguments.get("use_all_subscriptions", True))
    
        tenants = AZURE_CONFIG.get_tenants()
        if wanted:
            tenants = [t for t in tenants if t.get("name") in wanted]
            if not tenants:
                return [types.TextContent(type="text", text="No matching tenants found.")]
    
        rows: List[str] = []
        total = 0
        for t in tenants:
            tname = t.get("name") or t.get("id")
            cred, default_subs = AZURE_CONFIG.get_credentials(tname)
            mg = AZURE_CONFIG.get_management_group_id(tname)
            subs = list(default_subs)
            if try_all and mg:
                subs = []  # force MG usage below
            elif try_all:
                discovered = _enumerate_subscriptions_for_credential(cred)
                if discovered:
                    subs = discovered
    
            if not subs:
                rows.append(f"- {tname}: 0 (no subscriptions)")
                continue
    
            kql = vm_count_kql()
            if try_all and mg and not subs:
                result = execute_kql(cred, None, kql, top=1, management_groups=[mg])
            else:
                result = execute_kql(cred, subs, kql, top=1)
            if result["status"] != "success" or not result.get("results"):
                rows.append(f"- {tname}: error ({result.get('error','query failed')})")
                continue
            count = int(result["results"][0].get("VMCount", 0))
            total += count
            rows.append(f"- {tname}: {count}")
    
        body = [
            "VM counts by tenant:",
            *rows,
            "",
            f"Total VMs across tenants: {total}",
        ]
        return [types.TextContent(type="text", text="\n".join(body))]
  • Tool registration in @server.list_tools() decorator, defining name, description, and input schema for 'vm-count-by-tenant'.
    types.Tool(
        name="vm-count-by-tenant",
        description="Count virtual machines per configured tenant (uses all subscriptions when available).",
        inputSchema={
            "type": "object",
            "properties": {
                "tenant_names": {"type": "array", "items": {"type": "string"}, "description": "Optional subset of tenant names to include"},
                "use_all_subscriptions": {"type": "boolean", "description": "Try to include all subscriptions in each tenant (fallbacks to default)", "default": True}
            },
            "required": [],
        },
    ),
  • Input schema definition for the 'vm-count-by-tenant' tool, specifying optional tenant_names array and use_all_subscriptions boolean.
    inputSchema={
        "type": "object",
        "properties": {
            "tenant_names": {"type": "array", "items": {"type": "string"}, "description": "Optional subset of tenant names to include"},
            "use_all_subscriptions": {"type": "boolean", "description": "Try to include all subscriptions in each tenant (fallbacks to default)", "default": True}
        },
        "required": [],
  • Helper function that loads and returns the KQL template for counting virtual machines (via kql_loader.load_kql_template('vm_count')).
    def vm_count_kql() -> str:
        return _tmpl("vm_count")
Behavior2/5

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

No annotations are provided, so the description carries full burden. It mentions 'uses all subscriptions when available' which implies some fallback behavior, but doesn't disclose critical details like authentication requirements, rate limits, error handling, or what 'configured tenant' means operationally. For a tool with no annotations, this leaves significant behavioral gaps.

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 a single, efficient sentence that front-loads the core purpose. Every word earns its place with no redundancy or unnecessary elaboration, making it easy to parse quickly.

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

Completeness3/5

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

Given no annotations and no output schema, the description provides basic purpose but lacks sufficient context for effective use. It doesn't explain what 'count' returns (e.g., numbers, aggregated data), how tenants are identified, or error scenarios. For a tool with two parameters and no structured safety hints, this is minimally adequate but has clear gaps.

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 description coverage is 100%, so the schema already documents both parameters thoroughly. The description adds no additional parameter information beyond what's in the schema. It mentions 'uses all subscriptions when available' which relates to the use_all_subscriptions parameter's default behavior, but this is already covered in the schema's description and default value.

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

Purpose4/5

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

The description clearly states the verb ('Count') and resource ('virtual machines per configured tenant'), making the purpose unambiguous. It distinguishes from siblings by focusing on counting rather than listing or querying, though it doesn't explicitly name alternatives. The mention of 'uses all subscriptions when available' adds useful scope context.

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?

No explicit guidance on when to use this tool versus alternatives is provided. The description mentions 'uses all subscriptions when available' which hints at a default behavior, but doesn't specify scenarios where this tool is preferred over sibling tools like list-tenants or run-kql-template for similar purposes.

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/andrewstephenson-v1/Azure-Assistant-MCP'

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