Skip to main content
Glama
krzko

Google Cloud MCP Server

by krzko

gcp-profiler-compare-trends

Analyze and compare performance trends in Google Cloud Profiler data to identify patterns and optimize application performance.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Main handler function that authenticates to GCP Profiler API, fetches and filters profiles based on input parameters, generates trend analysis using helper function, and returns markdown report.
    async ({ target, profileType, pageSize }) => {
      try {
        const projectId = await getProjectId();
    
        // Initialize Google Auth client
        const auth = await initGoogleAuth(true);
        if (!auth) {
          throw new GcpMcpError(
            "Google Cloud authentication not available. Please configure authentication to access profiler data.",
            "UNAUTHENTICATED",
            401,
          );
        }
        const client = await auth.getClient();
        const token = await client.getAccessToken();
    
        const actualPageSize = pageSize || 200;
    
        // Build query parameters
        const params = new URLSearchParams({
          pageSize: actualPageSize.toString(),
        });
    
        // Make REST API call to list profiles
        const apiUrl = `https://cloudprofiler.googleapis.com/v2/projects/${projectId}/profiles?${params}`;
    
        const response = await fetch(apiUrl, {
          method: "GET",
          headers: {
            Authorization: `Bearer ${token.token}`,
            Accept: "application/json",
          },
        });
    
        if (!response.ok) {
          const errorText = await response.text();
          throw new GcpMcpError(
            `Failed to fetch profiles for trend analysis: ${errorText}`,
            "FAILED_PRECONDITION",
            response.status,
          );
        }
    
        const data: ListProfilesResponse = await response.json();
        let profiles = data.profiles || [];
    
        // Apply filtering
        if (profileType) {
          profiles = profiles.filter((p) => p.profileType === profileType);
        }
    
        if (target) {
          profiles = profiles.filter((p) =>
            p.deployment?.target?.toLowerCase().includes(target.toLowerCase()),
          );
        }
    
        if (!profiles || profiles.length === 0) {
          return {
            content: [
              {
                type: "text",
                text: `# Profile Trend Analysis\n\nProject: ${projectId}\n\nNo profiles found for trend analysis.`,
              },
            ],
          };
        }
    
        // Generate trend analysis
        let content = `# Profile Trend Analysis\n\nProject: ${projectId}\n`;
        if (profileType)
          content += `Profile Type: ${getProfileTypeDescription(profileType)}\n`;
        if (target) content += `Target: ${target}\n`;
        content += `Analysed: ${profiles.length} profiles\n\n`;
    
        // Analyse trends over time
        const trendAnalysis = analyseProfileTrends(profiles);
        content += trendAnalysis;
    
        return {
          content: [
            {
              type: "text",
              text: content,
            },
          ],
        };
      } catch (error: unknown) {
        const errorMessage =
          error instanceof Error ? error.message : "Unknown error";
        throw new GcpMcpError(
          `Failed to analyse profile trends: ${errorMessage}`,
          "INTERNAL_ERROR",
          500,
        );
      }
    },
  • Registers the 'gcp-profiler-compare-trends' tool with the MCP server, including title, description, Zod input schema, and handler reference.
      "gcp-profiler-compare-trends",
      {
        title: "Compare Profile Trends",
        description:
          "Compare profiles over time to identify performance trends, regressions, and improvements",
        inputSchema: {
          target: z
            .string()
            .optional()
            .describe("Focus comparison on specific deployment target"),
          profileType: z
            .enum([
              ProfileType.CPU,
              ProfileType.WALL,
              ProfileType.HEAP,
              ProfileType.THREADS,
              ProfileType.CONTENTION,
              ProfileType.PEAK_HEAP,
              ProfileType.HEAP_ALLOC,
            ])
            .optional()
            .describe("Focus comparison on specific profile type"),
          pageSize: z
            .number()
            .min(1)
            .max(1000)
            .default(200)
            .describe("Number of profiles to analyse for trends"),
        },
      },
      async ({ target, profileType, pageSize }) => {
        try {
          const projectId = await getProjectId();
    
          // Initialize Google Auth client
          const auth = await initGoogleAuth(true);
          if (!auth) {
            throw new GcpMcpError(
              "Google Cloud authentication not available. Please configure authentication to access profiler data.",
              "UNAUTHENTICATED",
              401,
            );
          }
          const client = await auth.getClient();
          const token = await client.getAccessToken();
    
          const actualPageSize = pageSize || 200;
    
          // Build query parameters
          const params = new URLSearchParams({
            pageSize: actualPageSize.toString(),
          });
    
          // Make REST API call to list profiles
          const apiUrl = `https://cloudprofiler.googleapis.com/v2/projects/${projectId}/profiles?${params}`;
    
          const response = await fetch(apiUrl, {
            method: "GET",
            headers: {
              Authorization: `Bearer ${token.token}`,
              Accept: "application/json",
            },
          });
    
          if (!response.ok) {
            const errorText = await response.text();
            throw new GcpMcpError(
              `Failed to fetch profiles for trend analysis: ${errorText}`,
              "FAILED_PRECONDITION",
              response.status,
            );
          }
    
          const data: ListProfilesResponse = await response.json();
          let profiles = data.profiles || [];
    
          // Apply filtering
          if (profileType) {
            profiles = profiles.filter((p) => p.profileType === profileType);
          }
    
          if (target) {
            profiles = profiles.filter((p) =>
              p.deployment?.target?.toLowerCase().includes(target.toLowerCase()),
            );
          }
    
          if (!profiles || profiles.length === 0) {
            return {
              content: [
                {
                  type: "text",
                  text: `# Profile Trend Analysis\n\nProject: ${projectId}\n\nNo profiles found for trend analysis.`,
                },
              ],
            };
          }
    
          // Generate trend analysis
          let content = `# Profile Trend Analysis\n\nProject: ${projectId}\n`;
          if (profileType)
            content += `Profile Type: ${getProfileTypeDescription(profileType)}\n`;
          if (target) content += `Target: ${target}\n`;
          content += `Analysed: ${profiles.length} profiles\n\n`;
    
          // Analyse trends over time
          const trendAnalysis = analyseProfileTrends(profiles);
          content += trendAnalysis;
    
          return {
            content: [
              {
                type: "text",
                text: content,
              },
            ],
          };
        } catch (error: unknown) {
          const errorMessage =
            error instanceof Error ? error.message : "Unknown error";
          throw new GcpMcpError(
            `Failed to analyse profile trends: ${errorMessage}`,
            "INTERNAL_ERROR",
            500,
          );
        }
      },
    );
  • Zod schema defining the input parameters for the tool: target (optional string), profileType (optional enum), pageSize (number default 200).
    inputSchema: {
      target: z
        .string()
        .optional()
        .describe("Focus comparison on specific deployment target"),
      profileType: z
        .enum([
          ProfileType.CPU,
          ProfileType.WALL,
          ProfileType.HEAP,
          ProfileType.THREADS,
          ProfileType.CONTENTION,
          ProfileType.PEAK_HEAP,
          ProfileType.HEAP_ALLOC,
        ])
        .optional()
        .describe("Focus comparison on specific profile type"),
      pageSize: z
        .number()
        .min(1)
        .max(1000)
        .default(200)
        .describe("Number of profiles to analyse for trends"),
    },
  • Supporting helper function called by the handler to analyze profile trends over time, calculating frequency, type distribution per day, and providing recommendations based on data coverage.
    function analyseProfileTrends(profiles: Profile[]): string {
      const profilesByTime = profiles
        .filter((p) => p.startTime)
        .sort(
          (a, b) =>
            new Date(a.startTime).getTime() - new Date(b.startTime).getTime(),
        );
    
      if (profilesByTime.length < 2) {
        return "Insufficient time-series data for trend analysis. Need at least 2 time-stamped profiles.\n";
      }
    
      let analysis = "## Trend Analysis\n\n";
    
      // Analyse profile frequency over time
      analysis += "### Profile Collection Frequency\n\n";
    
      const earliest = new Date(profilesByTime[0].startTime);
      const latest = new Date(profilesByTime[profilesByTime.length - 1].startTime);
      const timeSpan = latest.getTime() - earliest.getTime();
      const timeSpanDays = timeSpan / (1000 * 60 * 60 * 24);
    
      analysis += `- **Time Span:** ${Math.round(timeSpanDays)} days (from ${earliest.toLocaleDateString()} to ${latest.toLocaleDateString()})\n`;
      analysis += `- **Collection Frequency:** ${Math.round(profiles.length / timeSpanDays)} profiles per day\n\n`;
    
      // Analyse profile type trends
      analysis += "### Profile Type Trends\n\n";
    
      const typesByTime = profilesByTime.reduce(
        (acc, profile) => {
          const day = new Date(profile.startTime).toDateString();
          if (!acc[day]) acc[day] = {};
          acc[day][profile.profileType] = (acc[day][profile.profileType] || 0) + 1;
          return acc;
        },
        {} as Record<string, Record<string, number>>,
      );
    
      const days = Object.keys(typesByTime).sort();
      if (days.length > 1) {
        analysis += `Collected profiles across ${days.length} different days:\n\n`;
        days.slice(-5).forEach((day) => {
          // Show last 5 days
          const typeCounts = typesByTime[day];
          const totalForDay = Object.values(typeCounts).reduce(
            (sum, count) => sum + count,
            0,
          );
          analysis += `**${day}:** ${totalForDay} profiles (`;
          analysis += Object.entries(typeCounts)
            .map(([type, count]) => `${type}: ${count}`)
            .join(", ");
          analysis += `)\n`;
        });
        analysis += `\n`;
      }
    
      // Recommendations based on trends
      analysis += "### Trend-Based Recommendations\n\n";
    
      if (timeSpanDays < 1) {
        analysis += `- **Short timeframe:** Consider collecting profiles over a longer period for better trend analysis\n`;
      } else if (profiles.length / timeSpanDays < 1) {
        analysis += `- **Low frequency:** Consider increasing profile collection frequency for better insights\n`;
      } else {
        analysis += `- **Good coverage:** Profile collection frequency appears adequate for trend analysis\n`;
      }
    
      analysis += `- **Pattern monitoring:** Set up alerts for unusual changes in profile patterns\n`;
      analysis += `- **Performance baseline:** Use this trend data to establish performance baselines\n`;
    
      return analysis;
    }

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