Skip to main content
Glama

Google Drive MCP Server

by ducla5
server-manager.tsโ€ข9.31 kB
/** * Server Manager * Manages server startup, shutdown, and testing */ import chalk from 'chalk'; import ora from 'ora'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { ConfigManager } from '../config/config-manager.js'; import { AuthService } from '../auth/auth-service.js'; import { GoogleDriveMCPServer } from '../mcp/index.js'; import { ServerConfig } from '../types/config.js'; import { serverConfigToMCPConfig } from '../config/config-adapter.js'; import { registerCleanupHandler, triggerShutdown } from '../utils/process-cleanup.js'; export interface StartOptions { port?: number | undefined; logLevel?: string | undefined; useStdio?: boolean; } export interface TestOptions { authOnly?: boolean; driveOnly?: boolean; } export class ServerManager { private configManager: ConfigManager; private server: GoogleDriveMCPServer | null = null; private isShuttingDown = false; constructor(configPath?: string) { this.configManager = new ConfigManager(configPath); this.setupSignalHandlers(); } /** * Start the MCP server */ async start(options: StartOptions = {}): Promise<void> { console.log(chalk.blue.bold('๐Ÿš€ Starting Google Drive MCP Server...\n')); // Load and validate configuration const spinner = ora('Loading configuration...').start(); let config: ServerConfig; try { config = await this.configManager.loadConfig(); // Apply command line overrides if (options.port) { config.server.port = options.port; } if (options.logLevel) { config.server.logLevel = options.logLevel as any; } spinner.succeed('Configuration loaded'); } catch (error) { spinner.fail('Failed to load configuration'); throw error; } // Initialize authentication const authSpinner = ora('Initializing authentication...').start(); try { const authService = new AuthService(); await authService.initialize(); const status = await authService.validateAuthentication(); if (!status.isValid) { authSpinner.fail('Authentication failed'); console.log(chalk.red(`Error: ${status.error}`)); if (status.needsSetup) { console.log(chalk.yellow('Run "google-drive-mcp auth setup" to configure authentication')); } else if (status.needsReauth) { console.log(chalk.yellow('Run "google-drive-mcp auth refresh" to refresh tokens')); } throw new Error('Authentication not configured'); } authSpinner.succeed('Authentication validated'); } catch (error) { authSpinner.fail('Authentication initialization failed'); throw error; } // Create and start server const serverSpinner = ora('Starting MCP server...').start(); try { const mcpConfig = serverConfigToMCPConfig(config); this.server = new GoogleDriveMCPServer(mcpConfig); await this.server.start(); // Register cleanup handler registerCleanupHandler('mcp-server', async () => { if (this.server) { await this.server.stop(); this.server = null; } }, 10); // High priority serverSpinner.succeed('MCP server started'); } catch (error) { serverSpinner.fail('Failed to start MCP server'); throw error; } // Connect transport const transportSpinner = ora('Connecting transport...').start(); try { if (options.useStdio !== false) { // Use stdio transport (default for MCP) const transport = new StdioServerTransport(); await this.server.getServer().connect(transport); transportSpinner.succeed('Connected to stdio transport'); console.log(chalk.green.bold('\nโœ… Google Drive MCP Server is running!')); console.log(chalk.blue('Server is ready to accept MCP requests via stdio')); console.log(chalk.gray('Press Ctrl+C to stop the server\n')); // Keep the process alive await this.waitForShutdown(); } else { // HTTP transport mode (for testing/debugging) transportSpinner.succeed('Server ready for HTTP connections'); console.log(chalk.green.bold('\nโœ… Google Drive MCP Server is running!')); console.log(chalk.blue(`Server listening on port ${config.server.port}`)); console.log(chalk.gray('Press Ctrl+C to stop the server\n')); // Keep the process alive await this.waitForShutdown(); } } catch (error) { transportSpinner.fail('Failed to connect transport'); throw error; } } /** * Test server functionality */ async test(options: TestOptions = {}): Promise<void> { console.log(chalk.blue.bold('๐Ÿงช Testing Google Drive MCP Server...\n')); // Load configuration const configSpinner = ora('Loading configuration...').start(); let config: ServerConfig; try { config = await this.configManager.loadConfig(); configSpinner.succeed('Configuration loaded'); } catch (error) { configSpinner.fail('Failed to load configuration'); throw error; } // Test authentication if (!options.driveOnly) { const authSpinner = ora('Testing authentication...').start(); try { const authService = new AuthService(); await authService.initialize(); const status = await authService.validateAuthentication(); if (status.isValid) { authSpinner.succeed('Authentication test passed'); } else { authSpinner.fail('Authentication test failed'); console.log(chalk.red(`Error: ${status.error}`)); if (options.authOnly) { return; } } } catch (error) { authSpinner.fail('Authentication test failed'); console.log(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`)); if (options.authOnly) { return; } } } // Test Google Drive API if (!options.authOnly) { const driveSpinner = ora('Testing Google Drive API...').start(); try { // Create a temporary server instance for testing const mcpConfig = serverConfigToMCPConfig(config); const testServer = new GoogleDriveMCPServer(mcpConfig); await testServer.start(); // Test basic Drive API functionality // This would typically involve making a simple API call driveSpinner.succeed('Google Drive API test passed'); // Clean up test server await testServer.stop(); } catch (error) { driveSpinner.fail('Google Drive API test failed'); console.log(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`)); } } // Test MCP protocol if (!options.authOnly && !options.driveOnly) { const mcpSpinner = ora('Testing MCP protocol...').start(); try { const mcpConfig = serverConfigToMCPConfig(config); const testServer = new GoogleDriveMCPServer(mcpConfig); await testServer.start(); // Test MCP server initialization const server = testServer.getServer(); if (server) { mcpSpinner.succeed('MCP protocol test passed'); } else { mcpSpinner.fail('MCP protocol test failed'); } // Clean up test server await testServer.stop(); } catch (error) { mcpSpinner.fail('MCP protocol test failed'); console.log(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`)); } } console.log(chalk.green.bold('\nโœ… Testing completed!')); } /** * Graceful shutdown */ async shutdown(): Promise<void> { if (this.isShuttingDown) { return; } this.isShuttingDown = true; console.log(chalk.yellow('\n๐Ÿ›‘ Shutting down server...')); // Use the process cleanup system for consistent shutdown await triggerShutdown('manual'); } /** * Setup signal handlers for graceful shutdown */ private setupSignalHandlers(): void { // The ProcessCleanup utility will handle all signal registration // We just need to register our cleanup handler registerCleanupHandler('server-manager', async () => { if (this.server) { const spinner = ora('Cleaning up server resources...').start(); try { await this.server.stop(); this.server = null; spinner.succeed('Server resources cleaned up'); console.log(chalk.green('โœ… Goodbye!')); } catch (error) { spinner.fail('Error during cleanup'); console.error(chalk.red('Cleanup error:'), error instanceof Error ? error.message : error); } } }, 20); // Lower priority than the main server cleanup } /** * Wait for shutdown signal */ private async waitForShutdown(): Promise<void> { return new Promise((resolve) => { const checkShutdown = () => { if (this.isShuttingDown) { resolve(); } else { setTimeout(checkShutdown, 100); } }; checkShutdown(); }); } }

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/ducla5/gdriver-mcp'

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