gcp-billing-analyse-costs
Analyze Google Cloud billing data to identify cost trends, filter expenses by project or service, and group costs by time, SKU, project, or service for informed budgeting and optimization.
Instructions
Perform detailed cost analysis with trends and insights for Google Cloud billing data
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| billingAccountName | Yes | Billing account name (e.g., 'billingAccounts/123456-789ABC-DEF012') | |
| endDate | Yes | End date for cost analysis (ISO format: YYYY-MM-DD) | |
| groupBy | No | Group costs by project, service, SKU, or time | service |
| projectId | No | Optional project ID to filter costs | |
| serviceId | No | Optional service ID to filter costs | |
| startDate | Yes | Start date for cost analysis (ISO format: YYYY-MM-DD) |
Implementation Reference
- src/services/billing/tools.ts:681-779 (handler)The handler function that implements the tool logic: logs the request, generates mock CostData, calculates trends with percentage changes, formats output with helper functions, and returns a markdown analysis report. Note: uses mock data as real implementation requires BigQuery billing export.async ({ billingAccountName, startDate, endDate, projectId, serviceId, groupBy, }) => { try { logger.debug( `Analysing costs for billing account: ${billingAccountName}`, ); // Use project hierarchy: provided -> state manager -> auth default const actualProjectId = projectId || stateManager.getCurrentProjectId() || (await getProjectId()); // Note: This is a mock implementation since BigQuery billing export or // Cloud Billing Report API would be needed for actual cost data const mockCostData: CostData[] = [ { billingAccountName, projectId: actualProjectId || "example-project-1", serviceId: serviceId || "compute.googleapis.com", cost: { amount: 1250.5, currency: "USD" }, usage: { amount: 100, unit: "hours" }, period: { startTime: startDate, endTime: endDate }, labels: { environment: "production" }, }, { billingAccountName, projectId: actualProjectId || "example-project-2", serviceId: serviceId || "storage.googleapis.com", cost: { amount: 89.25, currency: "USD" }, usage: { amount: 500, unit: "GB" }, period: { startTime: startDate, endTime: endDate }, labels: { environment: "development" }, }, ]; // Calculate trends using our percentage change function const previousMonthCosts = [ { amount: 1150.25, currency: "USD" }, { amount: 95.75, currency: "USD" }, ]; let trendsAnalysis = `\n## Cost Trends Analysis\n\n`; mockCostData.forEach((cost, index) => { const previousCost = previousMonthCosts[index]?.amount || 0; const percentageChange = calculatePercentageChange( cost.cost.amount, previousCost, ); const changeDirection = percentageChange > 0 ? "📈" : "📉"; const changeIcon = Math.abs(percentageChange) > 20 ? "⚠️" : "✅"; trendsAnalysis += `**${cost.projectId} (${cost.serviceId}):** `; trendsAnalysis += `${changeIcon} ${changeDirection} ${percentageChange.toFixed(1)}% `; trendsAnalysis += `(${formatCurrency(previousCost)} → ${formatCurrency(cost.cost.amount)})\n`; }); let response = `# Cost Analysis\n\n`; response += `**Billing Account:** ${billingAccountName}\n`; response += `**Period:** ${startDate} to ${endDate}\n`; response += `**Grouped By:** ${groupBy}\n\n`; response += `⚠️ **Note:** This is a demonstration with mock data. `; response += `For actual cost analysis, you would need to:\n`; response += `1. Enable BigQuery billing export\n`; response += `2. Use Cloud Billing Report API\n`; response += `3. Query the billing export dataset\n\n`; response += formatCostData(mockCostData); response += trendsAnalysis; const totalCost = mockCostData.reduce( (sum, cost) => sum + cost.cost.amount, 0, ); response += `\n**Total Cost:** ${formatCurrency(totalCost)}\n`; return { content: [ { type: "text", text: response, }, ], }; } catch (error: any) { logger.error(`Error analysing costs: ${error.message}`); throw new GcpMcpError( `Failed to analyse costs: ${error.message}`, error.code || "UNKNOWN", error.status || 500, ); }
- Zod schema for tool inputs: requires billing account name and date range, optional project/service filters and grouping (default: service).inputSchema: { billingAccountName: z .string() .describe( "Billing account name (e.g., 'billingAccounts/123456-789ABC-DEF012')", ), startDate: z .string() .describe("Start date for cost analysis (ISO format: YYYY-MM-DD)"), endDate: z .string() .describe("End date for cost analysis (ISO format: YYYY-MM-DD)"), projectId: z .string() .optional() .describe("Optional project ID to filter costs"), serviceId: z .string() .optional() .describe("Optional service ID to filter costs"), groupBy: z .enum(["project", "service", "sku", "time"]) .default("service") .describe("Group costs by project, service, SKU, or time"), },
- src/services/billing/tools.ts:649-781 (registration)MCP tool registration within registerBillingTools function: defines tool name, title, description, input schema, and inline handler function.server.registerTool( "gcp-billing-analyse-costs", { title: "Analyse Billing Costs", description: "Perform detailed cost analysis with trends and insights for Google Cloud billing data", inputSchema: { billingAccountName: z .string() .describe( "Billing account name (e.g., 'billingAccounts/123456-789ABC-DEF012')", ), startDate: z .string() .describe("Start date for cost analysis (ISO format: YYYY-MM-DD)"), endDate: z .string() .describe("End date for cost analysis (ISO format: YYYY-MM-DD)"), projectId: z .string() .optional() .describe("Optional project ID to filter costs"), serviceId: z .string() .optional() .describe("Optional service ID to filter costs"), groupBy: z .enum(["project", "service", "sku", "time"]) .default("service") .describe("Group costs by project, service, SKU, or time"), }, }, async ({ billingAccountName, startDate, endDate, projectId, serviceId, groupBy, }) => { try { logger.debug( `Analysing costs for billing account: ${billingAccountName}`, ); // Use project hierarchy: provided -> state manager -> auth default const actualProjectId = projectId || stateManager.getCurrentProjectId() || (await getProjectId()); // Note: This is a mock implementation since BigQuery billing export or // Cloud Billing Report API would be needed for actual cost data const mockCostData: CostData[] = [ { billingAccountName, projectId: actualProjectId || "example-project-1", serviceId: serviceId || "compute.googleapis.com", cost: { amount: 1250.5, currency: "USD" }, usage: { amount: 100, unit: "hours" }, period: { startTime: startDate, endTime: endDate }, labels: { environment: "production" }, }, { billingAccountName, projectId: actualProjectId || "example-project-2", serviceId: serviceId || "storage.googleapis.com", cost: { amount: 89.25, currency: "USD" }, usage: { amount: 500, unit: "GB" }, period: { startTime: startDate, endTime: endDate }, labels: { environment: "development" }, }, ]; // Calculate trends using our percentage change function const previousMonthCosts = [ { amount: 1150.25, currency: "USD" }, { amount: 95.75, currency: "USD" }, ]; let trendsAnalysis = `\n## Cost Trends Analysis\n\n`; mockCostData.forEach((cost, index) => { const previousCost = previousMonthCosts[index]?.amount || 0; const percentageChange = calculatePercentageChange( cost.cost.amount, previousCost, ); const changeDirection = percentageChange > 0 ? "📈" : "📉"; const changeIcon = Math.abs(percentageChange) > 20 ? "⚠️" : "✅"; trendsAnalysis += `**${cost.projectId} (${cost.serviceId}):** `; trendsAnalysis += `${changeIcon} ${changeDirection} ${percentageChange.toFixed(1)}% `; trendsAnalysis += `(${formatCurrency(previousCost)} → ${formatCurrency(cost.cost.amount)})\n`; }); let response = `# Cost Analysis\n\n`; response += `**Billing Account:** ${billingAccountName}\n`; response += `**Period:** ${startDate} to ${endDate}\n`; response += `**Grouped By:** ${groupBy}\n\n`; response += `⚠️ **Note:** This is a demonstration with mock data. `; response += `For actual cost analysis, you would need to:\n`; response += `1. Enable BigQuery billing export\n`; response += `2. Use Cloud Billing Report API\n`; response += `3. Query the billing export dataset\n\n`; response += formatCostData(mockCostData); response += trendsAnalysis; const totalCost = mockCostData.reduce( (sum, cost) => sum + cost.cost.amount, 0, ); response += `\n**Total Cost:** ${formatCurrency(totalCost)}\n`; return { content: [ { type: "text", text: response, }, ], }; } catch (error: any) { logger.error(`Error analysing costs: ${error.message}`); throw new GcpMcpError( `Failed to analyse costs: ${error.message}`, error.code || "UNKNOWN", error.status || 500, ); } }, );