Skip to main content
Glama
krzko

Google Cloud MCP Server

by krzko

gcp-billing-service-breakdown

Analyze Google Cloud costs by service with usage and SKU details to identify spending patterns and optimize resource allocation.

Instructions

Get detailed cost breakdown by Google Cloud service with usage and SKU information

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
billingAccountNameYesBilling account name (e.g., 'billingAccounts/123456-789ABC-DEF012')
projectIdNoOptional project ID to filter costs
timeRangeNoTime range for analysis (7d, 30d, 90d, 1y)30d

Implementation Reference

  • The handler function for 'gcp-billing-service-breakdown' tool. It generates a detailed mock cost breakdown by Google Cloud service, including project, SKU, cost, usage, labels, grouped by service with tables and summary statistics.
    try { // Use project hierarchy: provided -> state manager -> auth default const actualProjectId = projectId || stateManager.getCurrentProjectId() || (await getProjectId()); logger.debug( `Getting service breakdown for billing account: ${billingAccountName}, project: ${actualProjectId || "all"}, range: ${timeRange}`, ); // Mock detailed service cost data const serviceBreakdown: CostData[] = [ { billingAccountName, projectId: actualProjectId || "production-project", serviceId: "compute.googleapis.com", skuId: "services/6F81-5844-456A/skus/CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-1", cost: { amount: 1847.25, currency: "USD" }, usage: { amount: 744, unit: "hours" }, period: { startTime: new Date( Date.now() - parseInt(timeRange.slice(0, -1)) * 24 * 60 * 60 * 1000, ).toISOString(), endTime: new Date().toISOString(), }, labels: { environment: "production", team: "backend", instance_type: "n1-standard-1", zone: "us-central1-a", }, }, { billingAccountName, projectId: actualProjectId || "production-project", serviceId: "storage.googleapis.com", skuId: "services/95FF-2EF5-5EA1/skus/9E26-D0CA-7C08", cost: { amount: 426.8, currency: "USD" }, usage: { amount: 2500, unit: "GB" }, period: { startTime: new Date( Date.now() - parseInt(timeRange.slice(0, -1)) * 24 * 60 * 60 * 1000, ).toISOString(), endTime: new Date().toISOString(), }, labels: { environment: "production", team: "data", storage_class: "standard", location: "us-central1", }, }, { billingAccountName, projectId: actualProjectId || "development-project", serviceId: "bigquery.googleapis.com", skuId: "services/24E6-581D-38E5/skus/1145-49C5-8000", cost: { amount: 189.45, currency: "USD" }, usage: { amount: 1250, unit: "TB" }, period: { startTime: new Date( Date.now() - parseInt(timeRange.slice(0, -1)) * 24 * 60 * 60 * 1000, ).toISOString(), endTime: new Date().toISOString(), }, labels: { environment: "development", team: "analytics", query_type: "on_demand", }, }, ]; let response = `# Service Cost Breakdown\n\n`; response += `**Billing Account:** ${billingAccountName}\n`; response += `**Project:** ${actualProjectId || "All projects"}\n`; response += `**Time Range:** ${timeRange}\n`; response += `**Services Analysed:** ${new Set(serviceBreakdown.map((c) => c.serviceId)).size}\n\n`; response += `⚠️ **Note:** This is demonstration data with detailed service and SKU breakdown.\n\n`; // Group by service for detailed analysis const serviceMap = new Map<string, CostData[]>(); serviceBreakdown.forEach((costData: CostData) => { if (!serviceMap.has(costData.serviceId!)) { serviceMap.set(costData.serviceId!, []); } serviceMap.get(costData.serviceId!)!.push(costData); }); // Detailed breakdown by service for (const [serviceId, costs] of serviceMap) { const serviceName = serviceId .replace(".googleapis.com", "") .toUpperCase(); const serviceTotal = costs.reduce( (sum, cost) => sum + cost.cost.amount, 0, ); response += `## ${serviceName}\n\n`; response += `**Service ID:** ${serviceId}\n`; response += `**Total Cost:** ${formatCurrency(serviceTotal)}\n`; response += `**SKUs:** ${costs.length}\n\n`; response += "| Project | SKU ID | Cost | Usage | Labels |\n"; response += "|---------|--------|------|-------|--------|\n"; costs.forEach((costData: CostData) => { const project = costData.projectId || "Unknown"; const skuId = costData.skuId ? costData.skuId.split("/").pop() : "Unknown"; const cost = formatCurrency(costData.cost.amount); const usage = `${costData.usage.amount} ${costData.usage.unit}`; const labels = costData.labels ? Object.entries(costData.labels) .map(([k, v]) => `${k}:${v}`) .join(", ") : "None"; response += `| ${project} | ${skuId} | ${cost} | ${usage} | ${labels} |\n`; }); response += "\n"; } // Summary const totalCost = serviceBreakdown.reduce( (sum, cost) => sum + cost.cost.amount, 0, ); response += `## Summary\n\n`; response += `**Total Cost:** ${formatCurrency(totalCost)}\n`; response += `**Average Cost per Service:** ${formatCurrency(totalCost / serviceMap.size)}\n`; // Top cost drivers const sortedServices = Array.from(serviceMap.entries()) .map(([serviceId, costs]) => ({ serviceId, total: costs.reduce((sum, cost) => sum + cost.cost.amount, 0), })) .sort((a, b) => b.total - a.total); response += `**Top Cost Driver:** ${sortedServices[0].serviceId} (${formatCurrency(sortedServices[0].total)})\n`; return { content: [ { type: "text", text: response, }, ], }; } catch (error: any) { logger.error(`Error getting service breakdown: ${error.message}`); throw new GcpMcpError( `Failed to get service breakdown: ${error.message}`, error.code || "UNKNOWN", error.status || 500, ); } },
  • Zod input schema defining parameters for the tool: billingAccountName (required string), projectId (optional string), timeRange (enum with default '30d').
    billingAccountName: z .string() .describe( "Billing account name (e.g., 'billingAccounts/123456-789ABC-DEF012')", ), projectId: z .string() .optional() .describe("Optional project ID to filter costs"), timeRange: z .enum(["7d", "30d", "90d", "1y"]) .default("30d") .describe("Time range for analysis (7d, 30d, 90d, 1y)"), }, },
  • Registration of the 'gcp-billing-service-breakdown' tool using server.registerTool, including title, description, inputSchema, and handler function.
    { title: "Get Service Cost Breakdown", description: "Get detailed cost breakdown by Google Cloud service with usage and SKU information", inputSchema: { billingAccountName: z .string() .describe( "Billing account name (e.g., 'billingAccounts/123456-789ABC-DEF012')", ), projectId: z .string() .optional() .describe("Optional project ID to filter costs"), timeRange: z .enum(["7d", "30d", "90d", "1y"]) .default("30d") .describe("Time range for analysis (7d, 30d, 90d, 1y)"), }, }, async ({ billingAccountName, projectId, timeRange }) => { try { // Use project hierarchy: provided -> state manager -> auth default const actualProjectId = projectId || stateManager.getCurrentProjectId() || (await getProjectId()); logger.debug( `Getting service breakdown for billing account: ${billingAccountName}, project: ${actualProjectId || "all"}, range: ${timeRange}`, ); // Mock detailed service cost data const serviceBreakdown: CostData[] = [ { billingAccountName, projectId: actualProjectId || "production-project", serviceId: "compute.googleapis.com", skuId: "services/6F81-5844-456A/skus/CP-COMPUTEENGINE-VMIMAGE-N1-STANDARD-1", cost: { amount: 1847.25, currency: "USD" }, usage: { amount: 744, unit: "hours" }, period: { startTime: new Date( Date.now() - parseInt(timeRange.slice(0, -1)) * 24 * 60 * 60 * 1000, ).toISOString(), endTime: new Date().toISOString(), }, labels: { environment: "production", team: "backend", instance_type: "n1-standard-1", zone: "us-central1-a", }, }, { billingAccountName, projectId: actualProjectId || "production-project", serviceId: "storage.googleapis.com", skuId: "services/95FF-2EF5-5EA1/skus/9E26-D0CA-7C08", cost: { amount: 426.8, currency: "USD" }, usage: { amount: 2500, unit: "GB" }, period: { startTime: new Date( Date.now() - parseInt(timeRange.slice(0, -1)) * 24 * 60 * 60 * 1000, ).toISOString(), endTime: new Date().toISOString(), }, labels: { environment: "production", team: "data", storage_class: "standard", location: "us-central1", }, }, { billingAccountName, projectId: actualProjectId || "development-project", serviceId: "bigquery.googleapis.com", skuId: "services/24E6-581D-38E5/skus/1145-49C5-8000", cost: { amount: 189.45, currency: "USD" }, usage: { amount: 1250, unit: "TB" }, period: { startTime: new Date( Date.now() - parseInt(timeRange.slice(0, -1)) * 24 * 60 * 60 * 1000, ).toISOString(), endTime: new Date().toISOString(), }, labels: { environment: "development", team: "analytics", query_type: "on_demand", }, }, ]; let response = `# Service Cost Breakdown\n\n`; response += `**Billing Account:** ${billingAccountName}\n`; response += `**Project:** ${actualProjectId || "All projects"}\n`; response += `**Time Range:** ${timeRange}\n`; response += `**Services Analysed:** ${new Set(serviceBreakdown.map((c) => c.serviceId)).size}\n\n`; response += `⚠️ **Note:** This is demonstration data with detailed service and SKU breakdown.\n\n`; // Group by service for detailed analysis const serviceMap = new Map<string, CostData[]>(); serviceBreakdown.forEach((costData: CostData) => { if (!serviceMap.has(costData.serviceId!)) { serviceMap.set(costData.serviceId!, []); } serviceMap.get(costData.serviceId!)!.push(costData); }); // Detailed breakdown by service for (const [serviceId, costs] of serviceMap) { const serviceName = serviceId .replace(".googleapis.com", "") .toUpperCase(); const serviceTotal = costs.reduce( (sum, cost) => sum + cost.cost.amount, 0, ); response += `## ${serviceName}\n\n`; response += `**Service ID:** ${serviceId}\n`; response += `**Total Cost:** ${formatCurrency(serviceTotal)}\n`; response += `**SKUs:** ${costs.length}\n\n`; response += "| Project | SKU ID | Cost | Usage | Labels |\n"; response += "|---------|--------|------|-------|--------|\n"; costs.forEach((costData: CostData) => { const project = costData.projectId || "Unknown"; const skuId = costData.skuId ? costData.skuId.split("/").pop() : "Unknown"; const cost = formatCurrency(costData.cost.amount); const usage = `${costData.usage.amount} ${costData.usage.unit}`; const labels = costData.labels ? Object.entries(costData.labels) .map(([k, v]) => `${k}:${v}`) .join(", ") : "None"; response += `| ${project} | ${skuId} | ${cost} | ${usage} | ${labels} |\n`; }); response += "\n"; } // Summary const totalCost = serviceBreakdown.reduce( (sum, cost) => sum + cost.cost.amount, 0, ); response += `## Summary\n\n`; response += `**Total Cost:** ${formatCurrency(totalCost)}\n`; response += `**Average Cost per Service:** ${formatCurrency(totalCost / serviceMap.size)}\n`; // Top cost drivers const sortedServices = Array.from(serviceMap.entries()) .map(([serviceId, costs]) => ({ serviceId, total: costs.reduce((sum, cost) => sum + cost.cost.amount, 0), })) .sort((a, b) => b.total - a.total); response += `**Top Cost Driver:** ${sortedServices[0].serviceId} (${formatCurrency(sortedServices[0].total)})\n`; return { content: [ { type: "text", text: response, }, ], }; } catch (error: any) { logger.error(`Error getting service breakdown: ${error.message}`); throw new GcpMcpError( `Failed to get service breakdown: ${error.message}`, error.code || "UNKNOWN", error.status || 500, ); } }, );
  • Helper function formatCurrency used in the handler to format monetary amounts for display in the response.
    export function formatCurrency( amount: number, currency: string = "USD", ): string { return new Intl.NumberFormat("en-AU", { style: "currency", currency: currency, minimumFractionDigits: 2, maximumFractionDigits: 4, }).format(amount); }
  • TypeScript interface CostData used to type the mock cost data structures in the handler.
    export interface CostData { billingAccountName: string; projectId?: string; serviceId?: string; skuId?: string; cost: { amount: number; currency: string; }; usage: { amount: number; unit: string; }; period: { startTime: string; endTime: string; }; labels?: Record<string, string>; }

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/krzko/google-cloud-mcp'

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