Skip to main content
Glama

Google Cloud MCP Server

by andyl25
index.js11.9 kB
/** * Google Cloud MCP Server * * This server provides Model Context Protocol resources and tools for interacting * with Google Cloud services (Logging, Spanner, and Monitoring). */ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { HttpTransport } from './transport/http.js'; import dotenv from 'dotenv'; // Import service modules import { registerLoggingResources, registerLoggingTools } from './services/logging/index.js'; import { registerSpannerResources, registerSpannerTools, registerSpannerQueryCountTool } from './services/spanner/index.js'; import { registerMonitoringResources, registerMonitoringTools } from './services/monitoring/index.js'; import { registerTraceService } from './services/trace/index.js'; import { registerPrompts } from './prompts/index.js'; import { initGoogleAuth, authClient } from './utils/auth.js'; import { registerResourceDiscovery } from './utils/resource-discovery.js'; import { registerProjectTools } from './utils/project-tools.js'; // Load environment variables dotenv.config(); // Transport configuration const TRANSPORT_TYPE = process.env.MCP_TRANSPORT || 'stdio'; // 'stdio' or 'http' const HTTP_PORT = parseInt(process.env.MCP_HTTP_PORT || '3000'); /** * Custom logger that writes to stderr (won't interfere with stdio protocol) */ const logger = { debug: (message, ...args) => { if (process.env.DEBUG) { console.error(`[DEBUG] ${message}`, ...args); } }, info: (message, ...args) => { console.error(`[INFO] ${message}`, ...args); }, warn: (message, ...args) => { console.error(`[WARN] ${message}`, ...args); }, error: (message, ...args) => { console.error(`[ERROR] ${message}`, ...args); } }; /** * Main function to start the MCP server */ async function main() { // Set up unhandled error handlers to prevent silent crashes process.on('uncaughtException', (error) => { logger.error(`Uncaught exception: ${error.message}`, error.stack); // Don't exit, just log the error }); process.on('unhandledRejection', (reason, promise) => { logger.error(`Unhandled rejection at: ${promise}, reason: ${reason}`); // Don't exit, just log the error }); // Add signal handlers to ensure clean shutdown process.on('SIGINT', () => { logger.info('Received SIGINT signal, shutting down gracefully'); process.exit(0); }); process.on('SIGTERM', () => { logger.info('Received SIGTERM signal, shutting down gracefully'); process.exit(0); }); // Debug environment variables if (process.env.DEBUG) { logger.debug('Environment variables:'); logger.debug(`GOOGLE_APPLICATION_CREDENTIALS: ${process.env.GOOGLE_APPLICATION_CREDENTIALS || 'not set'}`); logger.debug(`GOOGLE_CLOUD_PROJECT: ${process.env.GOOGLE_CLOUD_PROJECT || 'not set'}`); logger.debug(`GOOGLE_CLIENT_EMAIL: ${process.env.GOOGLE_CLIENT_EMAIL ? 'set' : 'not set'}`); logger.debug(`GOOGLE_PRIVATE_KEY: ${process.env.GOOGLE_PRIVATE_KEY ? 'set' : 'not set'}`); logger.debug(`LAZY_AUTH: ${process.env.LAZY_AUTH || 'not set'}`); logger.debug(`DEBUG: ${process.env.DEBUG || 'not set'}`); logger.debug(`MCP_TRANSPORT: ${TRANSPORT_TYPE}`); logger.debug(`MCP_HTTP_PORT: ${HTTP_PORT}`); } try { logger.info('Starting Google Cloud MCP server...'); // Create the MCP server first to ensure it's ready to handle requests // even if authentication is still initializing logger.info('Creating MCP server instance'); const server = new McpServer({ name: 'Google Cloud MCP', version: '0.1.0', description: 'Model Context Protocol server for Google Cloud services' }, { capabilities: { prompts: {}, resources: {}, tools: {} } }); // Initialize Google Cloud authentication in non-blocking mode // This allows the server to start even if credentials aren't available yet const lazyAuth = process.env.LAZY_AUTH !== 'false'; // Default to true if not set logger.info(`Initializing Google Cloud authentication in lazy loading mode: ${lazyAuth}`); // If LAZY_AUTH is true (default), we'll defer authentication until it's actually needed // This helps with Smithery which may time out during auth initialization const authPromise = lazyAuth ? Promise.resolve(null) : initGoogleAuth(false).then(auth => { if (auth) { logger.info('Google Cloud authentication initialized successfully'); } else { logger.warn('Google Cloud authentication not available - will attempt lazy loading when needed'); } return auth; }).catch((err) => { logger.warn(`Auth initialization warning: ${err.message}`); return null; }); // Register resources and tools for each Google Cloud service // These operations should not block server startup try { logger.info('Registering Google Cloud Logging services'); registerLoggingResources(server); registerLoggingTools(server); } catch (error) { logger.warn(`Error registering Logging services: ${error instanceof Error ? error.message : String(error)}`); } try { logger.info('Registering Google Cloud Spanner services'); registerSpannerResources(server); registerSpannerTools(server); registerSpannerQueryCountTool(server); } catch (error) { logger.warn(`Error registering Spanner services: ${error instanceof Error ? error.message : String(error)}`); } try { logger.info('Registering Google Cloud Monitoring services'); registerMonitoringResources(server); await registerMonitoringTools(server); } catch (error) { logger.warn(`Error registering Monitoring services: ${error instanceof Error ? error.message : String(error)}`); } try { // Register Google Cloud Trace service logger.info('Registering Google Cloud Trace services'); await registerTraceService(server); } catch (error) { logger.warn(`Error registering Trace services: ${error instanceof Error ? error.message : String(error)}`); } try { // Register additional tools logger.info('Registering additional tools'); registerProjectTools(server); } catch (error) { logger.warn(`Error registering project tools: ${error instanceof Error ? error.message : String(error)}`); } try { // Register prompts logger.info('Registering prompts'); registerPrompts(server); } catch (error) { logger.warn(`Error registering prompts: ${error instanceof Error ? error.message : String(error)}`); } try { // Register resource discovery endpoints logger.info('Registering resource discovery'); await registerResourceDiscovery(server); } catch (error) { logger.warn(`Error registering resource discovery: ${error instanceof Error ? error.message : String(error)}`); } // Setup transport based on configuration logger.info(`Starting ${TRANSPORT_TYPE} transport`); if (TRANSPORT_TYPE === 'http') { // Use HTTP transport const transport = new HttpTransport(HTTP_PORT); // Connect the server to the transport logger.info('Connecting server to HTTP transport'); await server.connect(transport); logger.info(`HTTP transport server started successfully on port ${HTTP_PORT}`); logger.info(`MCP endpoint available at: http://127.0.0.1:${HTTP_PORT}/mcp`); // Keep the process alive and periodically check auth status let heartbeatCount = 0; setInterval(() => { // Heartbeat to keep the process alive heartbeatCount++; if (process.env.DEBUG) { logger.debug(`Server heartbeat #${heartbeatCount}`); } // Check auth status periodically, but not on every heartbeat to reduce load // Only check auth every 5 heartbeats (approximately every 2.5 minutes) if (!authClient && heartbeatCount % 5 === 0) { logger.debug('Attempting delayed authentication check'); initGoogleAuth(false).then(auth => { if (auth && !authClient) { logger.info('Google Cloud authentication initialized successfully (delayed)'); } }).catch((authError) => { // Log but don't crash on auth errors logger.debug(`Delayed auth check failed: ${authError instanceof Error ? authError.message : String(authError)}`); }); } }, 30000); } else { // Use stdio transport (default) const transport = new StdioServerTransport(); // Connect the server to the transport logger.info('Connecting server to stdio transport'); await server.connect(transport); logger.info('Stdio transport server started successfully and ready to handle requests'); // Keep the process alive and periodically check auth status let heartbeatCount = 0; setInterval(() => { // Heartbeat to keep the process alive heartbeatCount++; if (process.env.DEBUG) { logger.debug(`Server heartbeat #${heartbeatCount}`); } // Check auth status periodically, but not on every heartbeat to reduce load // Only check auth every 5 heartbeats (approximately every 2.5 minutes) if (!authClient && heartbeatCount % 5 === 0) { logger.debug('Attempting delayed authentication check'); initGoogleAuth(false).then(auth => { if (auth && !authClient) { logger.info('Google Cloud authentication initialized successfully (delayed)'); } }).catch((authError) => { // Log but don't crash on auth errors logger.debug(`Delayed auth check failed: ${authError instanceof Error ? authError.message : String(authError)}`); }); } }, 30000); } } catch (error) { // Log the error to stderr (won't interfere with stdio protocol) logger.error(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}`); logger.error(error instanceof Error ? error.stack || 'No stack trace available' : 'No stack trace available'); // Don't exit immediately, give time for logs to be seen // But also don't exit at all - let the server continue running even with errors // This is important for Smithery which expects the server to stay alive logger.info('Server continuing to run despite startup errors'); } } // Start the server main(); //# sourceMappingURL=index.js.map

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/andyl25/googlecloud-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server