Skip to main content
Glama
krzko

Google Cloud MCP Server

by krzko

Get Service Cost Breakdown

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>;
    }
Behavior2/5

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

No annotations are provided, so the description carries the full burden. It states the tool 'Get detailed cost breakdown' but does not disclose behavioral traits such as required permissions, rate limits, pagination, or response format. This leaves significant gaps for a tool that likely involves data retrieval and potential API constraints.

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 without unnecessary words. It directly communicates the tool's function, making it easy to parse and understand quickly.

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

Completeness2/5

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

Given the lack of annotations and output schema, the description is incomplete for a tool with three parameters and no behavioral disclosure. It does not cover aspects like authentication needs, error handling, or return data structure, which are crucial for effective use in a complex environment like GCP billing.

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%, providing clear documentation for all three parameters (billingAccountName, projectId, timeRange). The description adds minimal value beyond the schema, mentioning 'detailed cost breakdown' but not elaborating on parameter interactions or semantics, so the baseline score of 3 is appropriate.

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 ('Get') and resource ('detailed cost breakdown by Google Cloud service'), specifying it includes usage and SKU information. It distinguishes from some siblings like 'gcp-billing-list-services' or 'gcp-billing-analyse-costs' by focusing on breakdown details, though not explicitly contrasting them.

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 like 'gcp-billing-analyse-costs' or 'gcp-billing-list-services' is provided. The description implies usage for detailed cost analysis but lacks context on prerequisites, exclusions, or comparisons with sibling tools.

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

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