Skip to main content
Glama

Google Cloud MCP Server

by krzko
tools.ts21.7 kB
/** * Google Cloud IAM tools for MCP */ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { getProjectId } from "../../utils/auth.js"; import { getResourceManagerClient, formatIamPolicy, IamPolicy, getDeploymentPermissionSet, getAllDeploymentPermissionSets, DEPLOYMENT_PERMISSION_SETS, } from "./types.js"; import { logger } from "../../utils/logger.js"; /** * Registers Google Cloud IAM tools with the MCP server * * @param server The MCP server instance */ export function registerIamTools(server: McpServer): void { // Tool to get project-level IAM policy server.registerTool( "gcp-iam-get-project-policy", { title: "Get Project IAM Policy", description: "Retrieve the IAM policy for a Google Cloud project", inputSchema: { project: z .string() .optional() .describe("Project ID (defaults to current project)"), requestedPolicyVersion: z .number() .min(1) .max(3) .default(3) .describe("The policy format version (1, 2, or 3)"), }, }, async ({ project, requestedPolicyVersion }) => { try { const projectId = project || (await getProjectId()); const resourceManager = getResourceManagerClient(); const [policy] = await resourceManager.getIamPolicy({ resource: `projects/${projectId}`, options: { requestedPolicyVersion, }, }); if (!policy) { return { content: [ { type: "text", text: `# Project IAM Policy Not Found\n\nNo IAM policy found for project: ${projectId}`, }, ], }; } const formattedPolicy = formatIamPolicy(policy as IamPolicy); return { content: [ { type: "text", text: `# Project IAM Policy\n\nProject: ${projectId}\nPolicy Version: ${requestedPolicyVersion}\n\n${formattedPolicy}`, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; logger.error(`Error getting project IAM policy: ${errorMessage}`); return { content: [ { type: "text", text: `# Error Getting Project IAM Policy\n\nFailed to retrieve IAM policy for project "${project || "current"}": ${errorMessage}\n\nPlease ensure:\n- The project ID is correct\n- You have the required permissions (resourcemanager.projects.getIamPolicy)\n- The project exists and is accessible`, }, ], isError: true, }; } }, ); // Tool to test IAM permissions on a project server.registerTool( "gcp-iam-test-project-permissions", { title: "Test Project IAM Permissions", description: "Test which permissions the current caller has on a Google Cloud project", inputSchema: { project: z .string() .optional() .describe("Project ID (defaults to current project)"), permissions: z .array(z.string()) .describe( 'List of permissions to test (e.g., ["resourcemanager.projects.get", "compute.instances.list"])', ), }, }, async ({ project, permissions }) => { try { const projectId = project || (await getProjectId()); const resourceManager = getResourceManagerClient(); const [response] = await resourceManager.testIamPermissions({ resource: `projects/${projectId}`, permissions, }); const grantedPermissions = response.permissions || []; const deniedPermissions = permissions.filter( (p) => !grantedPermissions.includes(p), ); let result = `# Project IAM Permissions Test\n\nProject: ${projectId}\n\n`; result += `## ✅ Granted Permissions (${grantedPermissions.length})\n\n`; if (grantedPermissions.length > 0) { grantedPermissions.forEach((permission) => { result += `- ${permission}\n`; }); } else { result += `*No permissions granted*\n`; } result += `\n## ❌ Denied Permissions (${deniedPermissions.length})\n\n`; if (deniedPermissions.length > 0) { deniedPermissions.forEach((permission) => { result += `- ${permission}\n`; }); } else { result += `*All permissions granted*\n`; } result += `\n**Summary:** ${grantedPermissions.length}/${permissions.length} permissions granted on project ${projectId}\n`; return { content: [ { type: "text", text: result, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; logger.error(`Error testing project IAM permissions: ${errorMessage}`); return { content: [ { type: "text", text: `# Error Testing Project IAM Permissions\n\nFailed to test IAM permissions on project "${project || "current"}": ${errorMessage}\n\nPlease ensure the project ID is correct and accessible.`, }, ], isError: true, }; } }, ); // Tool to test permissions on specific resources server.registerTool( "gcp-iam-test-resource-permissions", { title: "Test Resource-Specific IAM Permissions", description: "Test which permissions the current caller has on specific Google Cloud resources", inputSchema: { resource: z .string() .describe( 'The full resource name (e.g., "projects/my-project/buckets/my-bucket", "projects/my-project/zones/us-central1-a/instances/my-instance")', ), permissions: z .array(z.string()) .describe("List of permissions to test on the resource"), }, }, async ({ resource, permissions }) => { try { const resourceManager = getResourceManagerClient(); const [response] = await resourceManager.testIamPermissions({ resource, permissions, }); const grantedPermissions = response.permissions || []; const deniedPermissions = permissions.filter( (p) => !grantedPermissions.includes(p), ); let result = `# Resource IAM Permissions Test\n\nResource: ${resource}\n\n`; result += `## ✅ Granted Permissions (${grantedPermissions.length})\n\n`; if (grantedPermissions.length > 0) { grantedPermissions.forEach((permission) => { result += `- ${permission}\n`; }); } else { result += `*No permissions granted*\n`; } result += `\n## ❌ Denied Permissions (${deniedPermissions.length})\n\n`; if (deniedPermissions.length > 0) { deniedPermissions.forEach((permission) => { result += `- ${permission}\n`; }); } else { result += `*All permissions granted*\n`; } result += `\n**Summary:** ${grantedPermissions.length}/${permissions.length} permissions granted on resource ${resource}\n`; return { content: [ { type: "text", text: result, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; logger.error(`Error testing resource IAM permissions: ${errorMessage}`); return { content: [ { type: "text", text: `# Error Testing Resource IAM Permissions\n\nFailed to test IAM permissions on resource "${resource}": ${errorMessage}\n\nPlease ensure:\n- The resource name is correct and properly formatted\n- The resource exists and is accessible\n- You have the required permissions to test IAM permissions on this resource`, }, ], isError: true, }; } }, ); // Tool to validate deployment permissions for common GCP services server.registerTool( "gcp-iam-validate-deployment-permissions", { title: "Validate Deployment Permissions", description: "Check if current caller has required permissions for deploying to common GCP services", inputSchema: { service: z .string() .describe( "GCP service to validate (cloud-run, gke, compute-engine, cloud-functions, app-engine, cloud-storage, cloud-sql)", ), project: z .string() .optional() .describe("Project ID (defaults to current project)"), includeOptional: z .boolean() .default(false) .describe("Include optional permissions in the validation"), }, }, async ({ service, project, includeOptional }) => { try { const projectId = project || (await getProjectId()); const resourceManager = getResourceManagerClient(); const permissionSet = getDeploymentPermissionSet(service); if (!permissionSet) { const availableServices = getAllDeploymentPermissionSets() .map((s) => s.service.toLowerCase()) .join(", "); return { content: [ { type: "text", text: `# Invalid Service\n\nService "${service}" not found.\n\n**Available services:** ${availableServices}`, }, ], isError: true, }; } const allPermissions = [...permissionSet.requiredPermissions]; if (includeOptional && permissionSet.optionalPermissions) { allPermissions.push(...permissionSet.optionalPermissions); } const [response] = await resourceManager.testIamPermissions({ resource: `projects/${projectId}`, permissions: allPermissions, }); const grantedPermissions = response.permissions || []; const missingRequired = permissionSet.requiredPermissions.filter( (p) => !grantedPermissions.includes(p), ); const missingOptional = includeOptional && permissionSet.optionalPermissions ? permissionSet.optionalPermissions.filter( (p) => !grantedPermissions.includes(p), ) : []; let result = `# ${permissionSet.service} Deployment Validation\n\n`; result += `**Project:** ${projectId}\n`; result += `**Service:** ${permissionSet.service}\n`; result += `**Description:** ${permissionSet.description}\n\n`; // Overall status const canDeploy = missingRequired.length === 0; result += `## 🎯 Deployment Status: ${canDeploy ? "✅ READY" : "❌ BLOCKED"}\n\n`; if (canDeploy) { result += `✅ You have all required permissions to deploy ${permissionSet.service}\n\n`; } else { result += `❌ Missing ${missingRequired.length} required permission(s) for ${permissionSet.service} deployment\n\n`; } // Required permissions analysis result += `## Required Permissions (${permissionSet.requiredPermissions.length})\n\n`; permissionSet.requiredPermissions.forEach((permission) => { const status = grantedPermissions.includes(permission) ? "✅" : "❌"; result += `${status} ${permission}\n`; }); // Optional permissions analysis (if requested) if ( includeOptional && permissionSet.optionalPermissions && permissionSet.optionalPermissions.length > 0 ) { result += `\n## Optional Permissions (${permissionSet.optionalPermissions.length})\n\n`; permissionSet.optionalPermissions.forEach((permission) => { const status = grantedPermissions.includes(permission) ? "✅" : "❌"; result += `${status} ${permission}\n`; }); } // Common resources if (permissionSet.commonResources.length > 0) { result += `\n## Common Resource Patterns\n\n`; permissionSet.commonResources.forEach((resource) => { result += `- ${resource}\n`; }); } // Summary and next steps result += `\n## Summary\n\n`; result += `- **Required:** ${permissionSet.requiredPermissions.length - missingRequired.length}/${permissionSet.requiredPermissions.length} granted\n`; if (includeOptional && permissionSet.optionalPermissions) { result += `- **Optional:** ${permissionSet.optionalPermissions.length - missingOptional.length}/${permissionSet.optionalPermissions.length} granted\n`; } if (!canDeploy) { result += `\n### ⚠️ Missing Required Permissions\n\n`; missingRequired.forEach((permission) => { result += `- ${permission}\n`; }); result += `\n**Next Steps:** Contact your GCP administrator to grant the missing required permissions.\n`; } return { content: [ { type: "text", text: result, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; logger.error( `Error validating deployment permissions: ${errorMessage}`, ); return { content: [ { type: "text", text: `# Error Validating Deployment Permissions\n\nFailed to validate permissions for ${service}: ${errorMessage}\n\nPlease ensure the project ID is correct and accessible.`, }, ], isError: true, }; } }, ); // Tool to list all available deployment permission sets server.registerTool( "gcp-iam-list-deployment-services", { title: "List Available Deployment Services", description: "List all GCP services with pre-defined deployment permission sets", inputSchema: {}, }, async () => { try { const permissionSets = getAllDeploymentPermissionSets(); let result = `# Available Deployment Services\n\n`; result += `The following GCP services have pre-defined permission sets for deployment validation:\n\n`; permissionSets.forEach((set) => { // Get the service key from the original keys const serviceKey = Object.keys(DEPLOYMENT_PERMISSION_SETS).find( (key) => DEPLOYMENT_PERMISSION_SETS[key] === set, ) || set.service.toLowerCase().replace(/\s+/g, "-"); result += `## ${set.service}\n\n`; result += `**Service Key:** \`${serviceKey}\`\n`; result += `**Description:** ${set.description}\n`; result += `**Required Permissions:** ${set.requiredPermissions.length}\n`; result += `**Optional Permissions:** ${set.optionalPermissions?.length || 0}\n\n`; }); result += `## Usage\n\n`; result += `Use the \`validate-deployment-permissions\` tool with the service key to check your permissions for deploying to any of these services.\n\n`; result += `**Example:** \`validate-deployment-permissions\` with service="cloud-run"\n`; return { content: [ { type: "text", text: result, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; logger.error(`Error listing deployment services: ${errorMessage}`); return { content: [ { type: "text", text: `# Error Listing Deployment Services\n\nFailed to list deployment services: ${errorMessage}`, }, ], isError: true, }; } }, ); // Tool to analyse permission gaps for a specific resource and operation server.registerTool( "gcp-iam-analyse-permission-gaps", { title: "Analyse Permission Gaps", description: "Compare current permissions against required permissions for specific operations and identify gaps", inputSchema: { project: z .string() .optional() .describe("Project ID (defaults to current project)"), requiredPermissions: z .array(z.string()) .describe("List of permissions required for the intended operation"), operationDescription: z .string() .optional() .describe( "Description of the operation being attempted (for context)", ), }, }, async ({ project, requiredPermissions, operationDescription }) => { try { const projectId = project || (await getProjectId()); const resourceManager = getResourceManagerClient(); const [response] = await resourceManager.testIamPermissions({ resource: `projects/${projectId}`, permissions: requiredPermissions, }); const grantedPermissions = response.permissions || []; const missingPermissions = requiredPermissions.filter( (p) => !grantedPermissions.includes(p), ); let result = `# Permission Gap Analysis\n\n`; result += `**Project:** ${projectId}\n`; if (operationDescription) { result += `**Operation:** ${operationDescription}\n`; } result += `**Total Required Permissions:** ${requiredPermissions.length}\n\n`; // Overall status const hasAllPermissions = missingPermissions.length === 0; result += `## 🎯 Status: ${hasAllPermissions ? "✅ AUTHORISED" : "❌ INSUFFICIENT PERMISSIONS"}\n\n`; if (hasAllPermissions) { result += `✅ You have all required permissions for this operation.\n\n`; } else { result += `❌ Missing ${missingPermissions.length} permission(s). Operation will likely fail.\n\n`; } // Detailed breakdown result += `## Permission Analysis\n\n`; result += `### ✅ Granted Permissions (${grantedPermissions.length})\n\n`; if (grantedPermissions.length > 0) { grantedPermissions.forEach((permission) => { result += `- ${permission}\n`; }); } else { result += `*No permissions granted*\n`; } result += `\n### ❌ Missing Permissions (${missingPermissions.length})\n\n`; if (missingPermissions.length > 0) { missingPermissions.forEach((permission) => { result += `- ${permission}\n`; }); } else { result += `*No missing permissions*\n`; } // Recommendations if (missingPermissions.length > 0) { result += `\n## 📋 Recommendations\n\n`; result += `1. **Contact your GCP administrator** to request the missing permissions\n`; result += `2. **Use predefined roles** that include these permissions:\n`; // Suggest some common roles that might contain these permissions const suggestedRoles = []; if (missingPermissions.some((p) => p.startsWith("compute."))) { suggestedRoles.push( "roles/compute.admin", "roles/compute.instanceAdmin", ); } if (missingPermissions.some((p) => p.startsWith("storage."))) { suggestedRoles.push( "roles/storage.admin", "roles/storage.objectAdmin", ); } if (missingPermissions.some((p) => p.startsWith("run."))) { suggestedRoles.push("roles/run.admin", "roles/run.developer"); } if (missingPermissions.some((p) => p.startsWith("container."))) { suggestedRoles.push( "roles/container.admin", "roles/container.developer", ); } if (missingPermissions.some((p) => p.startsWith("iam."))) { suggestedRoles.push( "roles/iam.serviceAccountUser", "roles/iam.serviceAccountAdmin", ); } if (suggestedRoles.length > 0) { suggestedRoles.forEach((role) => { result += ` - ${role}\n`; }); } else { result += ` - Contact administrator for custom role assignment\n`; } result += `3. **Create a custom role** with exactly these permissions if predefined roles are too broad\n`; } return { content: [ { type: "text", text: result, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; logger.error(`Error analysing permission gaps: ${errorMessage}`); return { content: [ { type: "text", text: `# Error Analysing Permission Gaps\n\nFailed to analyse permissions: ${errorMessage}\n\nPlease ensure the project ID is correct and accessible.`, }, ], isError: true, }; } }, ); }

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