listAzureResourceGroups
Retrieve resource groups from your Azure subscription to manage cloud resources and organize infrastructure components effectively.
Instructions
List resource groups in a selected Azure subscription using MCP elicitation
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| subscriptionId | No | Azure subscription ID (will be elicited if not provided) |
Implementation Reference
- src/server.ts:435-598 (registration)Complete registration of the 'listAzureResourceGroups' MCP tool, including schema and inline handler function. The handler uses Azure CLI to list resource groups after eliciting subscription if needed.server.registerTool( "listAzureResourceGroups", { title: "List Azure Resource Groups", description: "List resource groups in a selected Azure subscription using MCP elicitation", inputSchema: { subscriptionId: z.string().optional().describe("Azure subscription ID (will be elicited if not provided)") } }, async ({ subscriptionId }, context) => { try { let targetSubscriptionId = subscriptionId; // If no subscription ID provided, use MCP elicitation to get it if (!targetSubscriptionId) { try { // Get available subscriptions first const subscriptionOptions = await getSubscriptionOptions(); if (subscriptionOptions.length === 0) { return { content: [{ type: "text", text: "❌ No Azure subscriptions found. Please run 'az login' first." }], isError: true }; } // Create elicitation request according to MCP spec const elicitationRequest = await server.server.elicitInput({ message: "Please select an Azure subscription to list resource groups from:", requestedSchema: { type: "object", properties: { subscriptionId: { type: "string", title: "Azure Subscription", description: "Select the subscription to use", enum: subscriptionOptions.map(sub => sub.id), enumNames: subscriptionOptions.map(sub => sub.label) } }, required: ["subscriptionId"] } }); // Handle elicitation response if (elicitationRequest.action === "accept" && elicitationRequest.content?.subscriptionId) { targetSubscriptionId = elicitationRequest.content.subscriptionId as string; } else if (elicitationRequest.action === "cancel") { return { content: [{ type: "text", text: "❌ Operation cancelled by user." }] }; } else if (elicitationRequest.action === "decline") { return { content: [{ type: "text", text: "❌ User declined to provide subscription information." }] }; } else { return { content: [{ type: "text", text: "❌ No subscription selected." }], isError: true }; } } catch (elicitationError: any) { return { content: [{ type: "text", text: `❌ Elicitation failed: ${elicitationError.message}\n\nPlease provide subscriptionId parameter directly.` }], isError: true }; } } // Now get resource groups using Azure CLI const { stdout, stderr } = await execAsync(`az group list --subscription "${targetSubscriptionId}" --output json`, { timeout: 30000, maxBuffer: 1024 * 1024 }); if (stderr) { return { content: [{ type: "text", text: `Azure CLI error: ${stderr}` }], isError: true }; } const resourceGroups = JSON.parse(stdout); if (!resourceGroups || resourceGroups.length === 0) { return { content: [{ type: "text", text: `📦 No resource groups found in the selected subscription.\n\n💡 Create one at: https://portal.azure.com` }] }; } // Get subscription name for display const { stdout: subStdout } = await execAsync(`az account show --subscription "${targetSubscriptionId}" --output json`); const subscriptionInfo = JSON.parse(subStdout); let result = `📦 Resource Groups in "${subscriptionInfo.name}" (${resourceGroups.length} found):\n\n`; resourceGroups.forEach((rg: any) => { const location = rg.location || 'Unknown'; const state = rg.properties?.provisioningState || 'Unknown'; const tags = rg.tags ? Object.keys(rg.tags).length : 0; result += `🏷️ ${rg.name}\n`; result += ` 📍 Location: ${location}\n`; result += ` 📊 State: ${state}\n`; result += ` 🏷️ Tags: ${tags} tag${tags !== 1 ? 's' : ''}\n`; if (rg.tags && Object.keys(rg.tags).length > 0) { const tagList = Object.entries(rg.tags).map(([k, v]) => `${k}=${v}`).slice(0, 3).join(', '); result += ` 🔖 ${tagList}${Object.keys(rg.tags).length > 3 ? '...' : ''}\n`; } result += `\n`; }); result += `💡 Subscription: ${subscriptionInfo.name}\n`; result += `🆔 Subscription ID: ${targetSubscriptionId}`; return { content: [{ type: "text", text: result }] }; } catch (err: any) { let errorMessage = `Failed to list resource groups: ${err.message}`; if (err.message.includes("az: command not found") || err.message.includes("'az' is not recognized")) { errorMessage += "\n\n🔧 Azure CLI is not installed. Please install it from: https://aka.ms/azure-cli"; } else if (err.message.includes("Please run 'az login'") || err.message.includes("not logged in")) { errorMessage += "\n\n🔐 Please authenticate first: az login"; } else if (err.message.includes("Subscription") && err.message.includes("not found")) { errorMessage += "\n\n🔍 Invalid subscription ID. Use listAzureSubscriptions to see available subscriptions."; } return { content: [{ type: "text", text: errorMessage }], isError: true }; } } );
- src/server.ts:444-597 (handler)The core handler function that executes the tool: elicits subscription ID if not provided using MCP, runs 'az group list', parses JSON, formats detailed output with locations, states, tags, and handles errors.async ({ subscriptionId }, context) => { try { let targetSubscriptionId = subscriptionId; // If no subscription ID provided, use MCP elicitation to get it if (!targetSubscriptionId) { try { // Get available subscriptions first const subscriptionOptions = await getSubscriptionOptions(); if (subscriptionOptions.length === 0) { return { content: [{ type: "text", text: "❌ No Azure subscriptions found. Please run 'az login' first." }], isError: true }; } // Create elicitation request according to MCP spec const elicitationRequest = await server.server.elicitInput({ message: "Please select an Azure subscription to list resource groups from:", requestedSchema: { type: "object", properties: { subscriptionId: { type: "string", title: "Azure Subscription", description: "Select the subscription to use", enum: subscriptionOptions.map(sub => sub.id), enumNames: subscriptionOptions.map(sub => sub.label) } }, required: ["subscriptionId"] } }); // Handle elicitation response if (elicitationRequest.action === "accept" && elicitationRequest.content?.subscriptionId) { targetSubscriptionId = elicitationRequest.content.subscriptionId as string; } else if (elicitationRequest.action === "cancel") { return { content: [{ type: "text", text: "❌ Operation cancelled by user." }] }; } else if (elicitationRequest.action === "decline") { return { content: [{ type: "text", text: "❌ User declined to provide subscription information." }] }; } else { return { content: [{ type: "text", text: "❌ No subscription selected." }], isError: true }; } } catch (elicitationError: any) { return { content: [{ type: "text", text: `❌ Elicitation failed: ${elicitationError.message}\n\nPlease provide subscriptionId parameter directly.` }], isError: true }; } } // Now get resource groups using Azure CLI const { stdout, stderr } = await execAsync(`az group list --subscription "${targetSubscriptionId}" --output json`, { timeout: 30000, maxBuffer: 1024 * 1024 }); if (stderr) { return { content: [{ type: "text", text: `Azure CLI error: ${stderr}` }], isError: true }; } const resourceGroups = JSON.parse(stdout); if (!resourceGroups || resourceGroups.length === 0) { return { content: [{ type: "text", text: `📦 No resource groups found in the selected subscription.\n\n💡 Create one at: https://portal.azure.com` }] }; } // Get subscription name for display const { stdout: subStdout } = await execAsync(`az account show --subscription "${targetSubscriptionId}" --output json`); const subscriptionInfo = JSON.parse(subStdout); let result = `📦 Resource Groups in "${subscriptionInfo.name}" (${resourceGroups.length} found):\n\n`; resourceGroups.forEach((rg: any) => { const location = rg.location || 'Unknown'; const state = rg.properties?.provisioningState || 'Unknown'; const tags = rg.tags ? Object.keys(rg.tags).length : 0; result += `🏷️ ${rg.name}\n`; result += ` 📍 Location: ${location}\n`; result += ` 📊 State: ${state}\n`; result += ` 🏷️ Tags: ${tags} tag${tags !== 1 ? 's' : ''}\n`; if (rg.tags && Object.keys(rg.tags).length > 0) { const tagList = Object.entries(rg.tags).map(([k, v]) => `${k}=${v}`).slice(0, 3).join(', '); result += ` 🔖 ${tagList}${Object.keys(rg.tags).length > 3 ? '...' : ''}\n`; } result += `\n`; }); result += `💡 Subscription: ${subscriptionInfo.name}\n`; result += `🆔 Subscription ID: ${targetSubscriptionId}`; return { content: [{ type: "text", text: result }] }; } catch (err: any) { let errorMessage = `Failed to list resource groups: ${err.message}`; if (err.message.includes("az: command not found") || err.message.includes("'az' is not recognized")) { errorMessage += "\n\n🔧 Azure CLI is not installed. Please install it from: https://aka.ms/azure-cli"; } else if (err.message.includes("Please run 'az login'") || err.message.includes("not logged in")) { errorMessage += "\n\n🔐 Please authenticate first: az login"; } else if (err.message.includes("Subscription") && err.message.includes("not found")) { errorMessage += "\n\n🔍 Invalid subscription ID. Use listAzureSubscriptions to see available subscriptions."; } return { content: [{ type: "text", text: errorMessage }], isError: true }; } }
- src/server.ts:437-443 (schema)Input schema using Zod for the tool, defining optional subscriptionId parameter.{ title: "List Azure Resource Groups", description: "List resource groups in a selected Azure subscription using MCP elicitation", inputSchema: { subscriptionId: z.string().optional().describe("Azure subscription ID (will be elicited if not provided)") } },
- src/server.ts:419-432 (helper)Helper function to retrieve and format Azure subscriptions as typed options for use in MCP input elicitation enum.async function getSubscriptionOptions(): Promise<SubscriptionOption[]> { const { stdout } = await execAsync('az account list --output json', { timeout: 30000, maxBuffer: 1024 * 1024 }); const subscriptions = JSON.parse(stdout); return subscriptions.map((sub: any): SubscriptionOption => ({ id: sub.id, name: sub.name, label: `${sub.name}${sub.isDefault ? ' (ACTIVE)' : ''}`, isDefault: sub.isDefault })); }
- src/server.ts:411-416 (schema)TypeScript interface defining the shape of subscription options used in the helper and elicitation schema.interface SubscriptionOption { id: string; name: string; label: string; isDefault: boolean; }