Skip to main content
Glama
krzko

Google Cloud MCP Server

by krzko

gcp-profiler-list-profiles

List profiling data from Google Cloud Profiler to analyze application performance and identify optimization opportunities.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Core handler that authenticates, calls the GCP Cloud Profiler API to list profiles, applies optional filters for profileType and target, uses helper functions for analysis and formatting, and returns a comprehensive Markdown report with summaries, insights, and pagination information.
    async ({ pageSize, pageToken, profileType, target }) => { try { const projectId = await getProjectId(); // Initialize Google Auth client (same pattern as error reporting) 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(); // Parse parameters const actualPageSize = pageSize || 50; // Build query parameters const params = new URLSearchParams({ pageSize: actualPageSize.toString(), }); // Add page token if provided if (pageToken) { params.set("pageToken", pageToken); } // 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: ${errorText}`, "FAILED_PRECONDITION", response.status, ); } const data: ListProfilesResponse = await response.json(); let profiles = data.profiles || []; // Apply client-side filtering if specified 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) { let filterText = ""; if (profileType) filterText += `Profile Type: ${profileType}\n`; if (target) filterText += `Target: ${target}\n`; return { content: [ { type: "text", text: `# Profiles\n\nProject: ${projectId}\n${filterText}${data.nextPageToken ? `Page Token: ${pageToken || "first"}\n` : ""}No profiles found.`, }, ], }; } // Generate analysis and insights const analysis = analyseProfilePatterns(profiles); let content = `# Profiler Analysis\n\nProject: ${projectId}\n`; if (profileType) content += `Profile Type Filter: ${getProfileTypeDescription(profileType)}\n`; if (target) content += `Target Filter: ${target}\n`; if (data.nextPageToken) content += `Next Page Available: Use token "${data.nextPageToken}"\n`; if (data.skippedProfiles) content += `Skipped Profiles: ${data.skippedProfiles}\n`; content += `\n${analysis}\n\n`; content += `## Detailed Profile List\n\n`; profiles.forEach((profile, index) => { content += `### ${index + 1}. ${formatProfileSummary(profile)}\n`; }); // Add pagination info if available if (data.nextPageToken) { content += `\n---\n\n**Pagination:** Use page token "${data.nextPageToken}" to get the next ${actualPageSize} results.\n`; } return { content: [ { type: "text", text: content, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; throw new GcpMcpError( `Failed to list profiles: ${errorMessage}`, "INTERNAL_ERROR", 500, ); } },
  • Zod-based input schema defining optional parameters for pagination (pageSize, pageToken) and filtering (profileType, target).
    inputSchema: { pageSize: z .number() .min(1) .max(1000) .default(50) .describe("Maximum number of profiles to return (1-1000)"), pageToken: z .string() .optional() .describe("Token for pagination to get next page of results"), profileType: z .enum([ ProfileType.CPU, ProfileType.WALL, ProfileType.HEAP, ProfileType.THREADS, ProfileType.CONTENTION, ProfileType.PEAK_HEAP, ProfileType.HEAP_ALLOC, ]) .optional() .describe("Filter by specific profile type"), target: z .string() .optional() .describe("Filter by deployment target (service name)"), },
  • MCP server.tool registration for the 'gcp-profiler-list-profiles' tool, including title, description, input schema, and inline handler function.
    "gcp-profiler-list-profiles", { title: "List Profiles", description: "List profiles from Google Cloud Profiler with optional filtering and pagination", inputSchema: { pageSize: z .number() .min(1) .max(1000) .default(50) .describe("Maximum number of profiles to return (1-1000)"), pageToken: z .string() .optional() .describe("Token for pagination to get next page of results"), profileType: z .enum([ ProfileType.CPU, ProfileType.WALL, ProfileType.HEAP, ProfileType.THREADS, ProfileType.CONTENTION, ProfileType.PEAK_HEAP, ProfileType.HEAP_ALLOC, ]) .optional() .describe("Filter by specific profile type"), target: z .string() .optional() .describe("Filter by deployment target (service name)"), }, }, async ({ pageSize, pageToken, profileType, target }) => { try { const projectId = await getProjectId(); // Initialize Google Auth client (same pattern as error reporting) 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(); // Parse parameters const actualPageSize = pageSize || 50; // Build query parameters const params = new URLSearchParams({ pageSize: actualPageSize.toString(), }); // Add page token if provided if (pageToken) { params.set("pageToken", pageToken); } // 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: ${errorText}`, "FAILED_PRECONDITION", response.status, ); } const data: ListProfilesResponse = await response.json(); let profiles = data.profiles || []; // Apply client-side filtering if specified 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) { let filterText = ""; if (profileType) filterText += `Profile Type: ${profileType}\n`; if (target) filterText += `Target: ${target}\n`; return { content: [ { type: "text", text: `# Profiles\n\nProject: ${projectId}\n${filterText}${data.nextPageToken ? `Page Token: ${pageToken || "first"}\n` : ""}No profiles found.`, }, ], }; } // Generate analysis and insights const analysis = analyseProfilePatterns(profiles); let content = `# Profiler Analysis\n\nProject: ${projectId}\n`; if (profileType) content += `Profile Type Filter: ${getProfileTypeDescription(profileType)}\n`; if (target) content += `Target Filter: ${target}\n`; if (data.nextPageToken) content += `Next Page Available: Use token "${data.nextPageToken}"\n`; if (data.skippedProfiles) content += `Skipped Profiles: ${data.skippedProfiles}\n`; content += `\n${analysis}\n\n`; content += `## Detailed Profile List\n\n`; profiles.forEach((profile, index) => { content += `### ${index + 1}. ${formatProfileSummary(profile)}\n`; }); // Add pagination info if available if (data.nextPageToken) { content += `\n---\n\n**Pagination:** Use page token "${data.nextPageToken}" to get the next ${actualPageSize} results.\n`; } return { content: [ { type: "text", text: content, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; throw new GcpMcpError( `Failed to list profiles: ${errorMessage}`, "INTERNAL_ERROR", 500, ); } }, );
  • Key helper function called by the handler to analyze profile patterns, producing markdown sections with statistics, distributions, recent activity, type-specific analysis, and recommendations.
    export function analyseProfilePatterns(profiles: Profile[]): string { if (!profiles || profiles.length === 0) { return "No profiles found in the specified criteria."; } let analysis = `# Profile Analysis and Performance Insights\n\n`; // Profile summary statistics const totalProfiles = profiles.length; const profileTypes = [...new Set(profiles.map((p) => p.profileType))]; const targets = [ ...new Set(profiles.map((p) => p.deployment?.target).filter(Boolean)), ]; analysis += `## Summary\n\n`; analysis += `- **Total Profiles:** ${totalProfiles}\n`; analysis += `- **Profile Types:** ${profileTypes.length} (${profileTypes.join(", ")})\n`; analysis += `- **Targets:** ${targets.length} (${targets.join(", ")})\n\n`; // Profile type distribution const typeDistribution = profiles.reduce( (acc, profile) => { acc[profile.profileType] = (acc[profile.profileType] || 0) + 1; return acc; }, {} as Record<string, number>, ); analysis += `## Profile Type Distribution\n\n`; Object.entries(typeDistribution) .sort(([, a], [, b]) => b - a) .forEach(([type, count]) => { const percentage = Math.round((count / totalProfiles) * 100); analysis += `- **${getProfileTypeDescription(type)}:** ${count} profiles (${percentage}%)\n`; }); analysis += `\n`; // Recent activity analysis const recentProfiles = profiles .filter((p) => p.startTime) .sort( (a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime(), ) .slice(0, 5); if (recentProfiles.length > 0) { analysis += `## Recent Profile Activity\n\n`; recentProfiles.forEach((profile, index) => { const timeAgo = getTimeAgo(profile.startTime); analysis += `${index + 1}. **${profile.deployment?.target || "Unknown Target"}** - ${getProfileTypeDescription(profile.profileType)} (${timeAgo})\n`; }); analysis += `\n`; } // Performance analysis by profile type analysis += `## Performance Analysis by Profile Type\n\n`; profileTypes.forEach((type) => { const typeProfiles = profiles.filter((p) => p.profileType === type); analysis += `### ${getProfileTypeDescription(type)}\n\n`; analysis += getProfileTypeAnalysis(type, typeProfiles); analysis += `\n`; }); // Recommendations analysis += `## Recommendations\n\n`; analysis += getPerformanceRecommendations(profiles, typeDistribution); return analysis; }
  • Helper function used to format each profile summary in the list output.
    export function formatProfileSummary(profile: Profile): string { const profileName = profile.name || "Unknown"; const profileType = profile.profileType || "Unknown"; const deployment = profile.deployment || {}; const target = deployment.target || "Unknown"; const projectId = deployment.projectId || "Unknown"; const startTime = profile.startTime ? new Date(profile.startTime).toLocaleString() : "Unknown"; const duration = profile.duration || "Unknown"; let summary = `## Profile: ${profileName.split("/").pop()}\n\n`; summary += `**Type:** ${getProfileTypeDescription(profileType)}\n`; summary += `**Target:** ${target}\n`; summary += `**Project:** ${projectId}\n`; summary += `**Start Time:** ${startTime}\n`; summary += `**Duration:** ${formatDuration(duration)}\n`; // Add labels if available if (profile.labels && Object.keys(profile.labels).length > 0) { summary += `**Labels:**\n`; Object.entries(profile.labels).forEach(([key, value]) => { summary += ` - ${key}: ${value}\n`; }); } // Add deployment labels if available if (deployment.labels && Object.keys(deployment.labels).length > 0) { summary += `**Deployment Labels:**\n`; Object.entries(deployment.labels).forEach(([key, value]) => { summary += ` - ${key}: ${value}\n`; }); } return summary; }
  • src/index.ts:224-224 (registration)
    Top-level call to register all Profiler tools (including gcp-profiler-list-profiles) on the MCP server.
    registerProfilerTools(server);

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