gcp-profiler-analyse-performance
Analyze application performance on Google Cloud using profiler data to identify bottlenecks, optimize resource usage, and improve efficiency through actionable insights.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/services/profiler/tools.ts:182-332 (registration)Registers the 'gcp-profiler-analyse-performance' tool with the MCP server using server.tool(). Includes title, description, input schema with Zod validation for profileType, target, pageSize, and the inline async handler function.server.tool( "gcp-profiler-analyse-performance", { title: "Analyse Profile Performance", description: "Analyse profiles to identify performance patterns, bottlenecks, and optimisation opportunities", inputSchema: { profileType: z .enum([ ProfileType.CPU, ProfileType.WALL, ProfileType.HEAP, ProfileType.THREADS, ProfileType.CONTENTION, ProfileType.PEAK_HEAP, ProfileType.HEAP_ALLOC, ]) .optional() .describe("Focus analysis on specific profile type"), target: z .string() .optional() .describe("Focus analysis on specific deployment target"), pageSize: z .number() .min(1) .max(1000) .default(100) .describe( "Number of profiles to analyse (more profiles = better insights)", ), }, }, async ({ profileType, target, pageSize }) => { 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 || 100; // Build query parameters for maximum data collection 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 analysis: ${errorText}`, "FAILED_PRECONDITION", response.status, ); } const data: ListProfilesResponse = await response.json(); let profiles = data.profiles || []; // Apply 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 = "No profiles found for analysis"; if (profileType) filterText += ` with profile type: ${profileType}`; if (target) filterText += ` and target: ${target}`; return { content: [ { type: "text", text: `# Profile Performance Analysis\n\nProject: ${projectId}\n\n${filterText}.`, }, ], }; } // Generate comprehensive analysis let content = `# Profile Performance Analysis\n\nProject: ${projectId}\n`; if (profileType) content += `Focus: ${getProfileTypeDescription(profileType)}\n`; if (target) content += `Target: ${target}\n`; content += `Analysed: ${profiles.length} profiles\n\n`; // Get detailed analysis const analysis = analyseProfilePatterns(profiles); content += analysis; // Add performance insights specific to the analysis content += `\n## Performance Insights\n\n`; // Analyse profile collection patterns const timeDistribution = analyseProfileTimeDistribution(profiles); content += timeDistribution; // Analyse deployment patterns const deploymentAnalysis = analyseDeploymentPatterns(profiles); content += deploymentAnalysis; // Add actionable recommendations content += `\n## Actionable Recommendations\n\n`; content += getActionableRecommendations(profiles, profileType); 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 performance: ${errorMessage}`, "INTERNAL_ERROR", 500, ); } }, );
- src/services/profiler/tools.ts:215-331 (handler)The core handler function that implements the tool logic. Authenticates with Google Cloud, lists profiles from the Profiler API, applies filters, performs multi-faceted analysis (patterns, time distribution, deployments, recommendations), and returns a comprehensive Markdown report on performance insights.async ({ profileType, target, pageSize }) => { 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 || 100; // Build query parameters for maximum data collection 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 analysis: ${errorText}`, "FAILED_PRECONDITION", response.status, ); } const data: ListProfilesResponse = await response.json(); let profiles = data.profiles || []; // Apply 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 = "No profiles found for analysis"; if (profileType) filterText += ` with profile type: ${profileType}`; if (target) filterText += ` and target: ${target}`; return { content: [ { type: "text", text: `# Profile Performance Analysis\n\nProject: ${projectId}\n\n${filterText}.`, }, ], }; } // Generate comprehensive analysis let content = `# Profile Performance Analysis\n\nProject: ${projectId}\n`; if (profileType) content += `Focus: ${getProfileTypeDescription(profileType)}\n`; if (target) content += `Target: ${target}\n`; content += `Analysed: ${profiles.length} profiles\n\n`; // Get detailed analysis const analysis = analyseProfilePatterns(profiles); content += analysis; // Add performance insights specific to the analysis content += `\n## Performance Insights\n\n`; // Analyse profile collection patterns const timeDistribution = analyseProfileTimeDistribution(profiles); content += timeDistribution; // Analyse deployment patterns const deploymentAnalysis = analyseDeploymentPatterns(profiles); content += deploymentAnalysis; // Add actionable recommendations content += `\n## Actionable Recommendations\n\n`; content += getActionableRecommendations(profiles, profileType); 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 performance: ${errorMessage}`, "INTERNAL_ERROR", 500, ); } },
- Input schema using Zod for validating tool parameters: profileType (optional ProfileType enum), target (optional string), pageSize (optional number, 1-1000, default 100).inputSchema: { profileType: z .enum([ ProfileType.CPU, ProfileType.WALL, ProfileType.HEAP, ProfileType.THREADS, ProfileType.CONTENTION, ProfileType.PEAK_HEAP, ProfileType.HEAP_ALLOC, ]) .optional() .describe("Focus analysis on specific profile type"), target: z .string() .optional() .describe("Focus analysis on specific deployment target"), pageSize: z .number() .min(1) .max(1000) .default(100) .describe( "Number of profiles to analyse (more profiles = better insights)", ), },
- Key helper function called by the handler. Analyzes a list of profiles to produce detailed markdown analysis including statistics, distributions, recent activity, profile-type specific insights, and performance 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 in handler for temporal analysis of profile collection: sorts by startTime, categorizes into recent/this week/older, reports counts and spans.function analyseProfileTimeDistribution(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 === 0) { return "No time-stamped profiles available for temporal analysis.\n\n"; } let analysis = "### Profile Collection Timeline\n\n"; // Group by time buckets (last 24 hours, last week, older) const now = new Date().getTime(); const oneDayAgo = now - 24 * 60 * 60 * 1000; const oneWeekAgo = now - 7 * 24 * 60 * 60 * 1000; const recent = profilesByTime.filter( (p) => new Date(p.startTime).getTime() > oneDayAgo, ); const thisWeek = profilesByTime.filter((p) => { const time = new Date(p.startTime).getTime(); return time <= oneDayAgo && time > oneWeekAgo; }); const older = profilesByTime.filter( (p) => new Date(p.startTime).getTime() <= oneWeekAgo, ); analysis += `- **Last 24 hours:** ${recent.length} profiles\n`; analysis += `- **Last week:** ${thisWeek.length} profiles\n`; analysis += `- **Older:** ${older.length} profiles\n\n`; if (recent.length > 0) { const oldestRecent = new Date(recent[0].startTime).toLocaleString(); const newestRecent = new Date( recent[recent.length - 1].startTime, ).toLocaleString(); analysis += `Recent activity spans from ${oldestRecent} to ${newestRecent}\n\n`; } return analysis; }