gcp-profiler-list-profiles
Retrieve and list profiling data from Google Cloud services using natural language queries to optimize application performance and debug issues.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/services/profiler/tools.ts:59-178 (handler)Core handler that authenticates with GCP, fetches profiles list via Cloud Profiler v2 REST API, applies optional filters for type and target, generates comprehensive Markdown analysis including patterns, summaries, and pagination info.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, ); } },
- src/services/profiler/tools.ts:30-57 (schema)Zod input schema defining optional parameters for pagination (pageSize, pageToken), filtering by profileType (enum) and target (string).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)"), },
- src/services/profiler/tools.ts:24-179 (registration)MCP server.tool registration defining the tool name, title, description, input schema, and handler function.server.tool( "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, ); } }, );
- Helper to format individual profile details into readable Markdown summary used in the tool 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; }
- Key helper function called by handler to analyze profile patterns, distributions, and generate performance insights Markdown.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; }
- src/services/profiler/types.ts:55-62 (schema)TypeScript type definitions for API response (ListProfilesResponse), Profile interface, and ProfileType enum used throughout the implementation.export interface ListProfilesResponse { /** List of profiles found */ profiles: Profile[]; /** Token for pagination to next page */ nextPageToken?: string; /** Number of profiles that couldn't be fetched */ skippedProfiles?: number; }