git-backup
Create, restore, list, and verify backups of Git repositories to protect your code and project history from data loss or corruption.
Instructions
Comprehensive backup system for Git repositories. Supports backup creation, restoration, listing, and verification.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | The backup operation to perform | |
| backupPath | No | Path for backup storage or backup file to restore/verify | |
| checkIntegrity | No | Check backup integrity during verification (default: false) | |
| compression | No | Enable compression (default: true) | |
| format | No | Backup format (default: tar) | |
| includeUntracked | No | Include untracked files in backup (default: false) | |
| name | No | Backup name/identifier (for backup operation) | |
| overwrite | No | Overwrite existing files during restore (default: false) | |
| projectPath | Yes | Path to the Git repository (required) | |
| sortBy | No | Sort criteria for listing backups (default: date) | |
| targetPath | No | Target path for restoration (for restore operation) |
Implementation Reference
- src/tools/git-backup.ts:39-556 (handler)GitBackupTool class providing the core handler logic for 'git-backup' tool. Includes execute() method that validates params and routes to specific handlers for backup/restore/list/verify operations using Git commands and file system operations.export class GitBackupTool { private gitExecutor: GitCommandExecutor; private terminal: TerminalController; constructor() { this.gitExecutor = new GitCommandExecutor(); this.terminal = new TerminalController(); } async execute(params: GitBackupParams): Promise<ToolResult> { const startTime = Date.now(); try { // Validate required parameters const validation = ParameterValidator.validateToolParams('git-backup', params); if (!validation.isValid) { return OperationErrorHandler.createToolError( 'VALIDATION_ERROR', `Validation failed: ${validation.errors.join(', ')}`, 'backup', { errors: validation.errors }, validation.suggestions ); } // Validate project path exists if (!existsSync(params.projectPath)) { return OperationErrorHandler.createToolError( 'PROJECT_NOT_FOUND', `Project path does not exist: ${params.projectPath}`, 'backup', { projectPath: params.projectPath }, ['Ensure the project path exists and is accessible'] ); } switch (params.action) { case 'backup': return await this.handleBackup(params, startTime); case 'restore': return await this.handleRestore(params, startTime); case 'list': return await this.handleList(params, startTime); case 'verify': return await this.handleVerify(params, startTime); default: return OperationErrorHandler.createToolError( 'INVALID_ACTION', `Invalid action: ${params.action}`, params.action, {}, ['Use one of: backup, restore, list, verify'] ); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'BACKUP_TOOL_ERROR', `Git backup tool error: ${errorMessage}`, 'backup', { error: errorMessage } ); } } /** * Handle backup creation operation */ private async handleBackup(params: GitBackupParams, startTime: number): Promise<ToolResult> { try { // Check if it's a Git repository const isGitRepo = await this.gitExecutor.isGitRepository(params.projectPath); if (!isGitRepo) { return OperationErrorHandler.createToolError( 'NOT_GIT_REPOSITORY', 'Project is not a Git repository', 'backup', { projectPath: params.projectPath }, ['Initialize Git repository with: git init'] ); } // Generate backup path if not provided if (!params.backupPath) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const repoName = path.basename(params.projectPath); params.backupPath = path.join(params.projectPath, '..', 'backups', `${repoName}-backup-${timestamp}`); } // Ensure backup directory exists const backupDir = path.dirname(params.backupPath); await fs.mkdir(backupDir, { recursive: true }); const format = params.format || 'tar'; const backupName = params.name || `backup-${Date.now()}`; // Create backup using git archive const result = await this.gitExecutor.createBackup(params.projectPath, params.backupPath, format); if (!result.success) { return OperationErrorHandler.handleGitError(result.stderr, 'backup', params.projectPath); } const extension = format === 'zip' ? 'zip' : 'tar.gz'; const finalBackupPath = `${params.backupPath}.${extension}`; // Get backup file stats const stats = await fs.stat(finalBackupPath); // Create backup metadata const metadata = { name: backupName, path: finalBackupPath, format, size: stats.size, created: stats.birthtime.toISOString(), repository: path.basename(params.projectPath), compression: params.compression !== false }; // Save metadata file const metadataPath = `${params.backupPath}.meta.json`; await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2)); return { success: true, data: { message: 'Repository backup created successfully', backup: metadata, output: result.stdout }, metadata: { operation: 'backup', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'BACKUP_CREATE_ERROR', `Failed to create backup: ${errorMessage}`, 'backup', { error: errorMessage, backupPath: params.backupPath } ); } } /** * Handle backup restoration operation */ private async handleRestore(params: GitBackupParams, startTime: number): Promise<ToolResult> { try { if (!params.backupPath) { return OperationErrorHandler.createToolError( 'MISSING_BACKUP_PATH', 'Backup path is required for restore operation', 'restore', {}, ['Provide backupPath parameter with the path to the backup file'] ); } // Check if backup file exists if (!existsSync(params.backupPath)) { return OperationErrorHandler.createToolError( 'BACKUP_NOT_FOUND', `Backup file not found: ${params.backupPath}`, 'restore', { backupPath: params.backupPath }, ['Ensure the backup file exists and path is correct'] ); } const targetPath = params.targetPath || params.projectPath; // Check if target directory exists and handle overwrite if (existsSync(targetPath) && !params.overwrite) { return OperationErrorHandler.createToolError( 'TARGET_EXISTS', `Target directory exists: ${targetPath}`, 'restore', { targetPath }, ['Use overwrite: true to overwrite existing directory', 'Choose a different target path'] ); } // Ensure target directory exists await fs.mkdir(targetPath, { recursive: true }); // Determine backup format from file extension const isZip = params.backupPath.endsWith('.zip'); const format = isZip ? 'zip' : 'tar'; // Extract backup let result: GitCommandResult; if (format === 'zip') { // Use unzip command for zip files result = await this.terminal.executeCommand('unzip', ['-o', params.backupPath, '-d', targetPath]); } else { // Use tar command for tar.gz files result = await this.terminal.executeCommand('tar', ['-xzf', params.backupPath, '-C', targetPath]); } if (!result.success) { return OperationErrorHandler.createToolError( 'RESTORE_ERROR', `Failed to extract backup: ${result.stderr}`, 'restore', { backupPath: params.backupPath, targetPath } ); } // Load backup metadata if available const metadataPath = params.backupPath.replace(/\.(zip|tar\.gz)$/, '.meta.json'); let metadata = null; if (existsSync(metadataPath)) { try { const metadataContent = await fs.readFile(metadataPath, 'utf-8'); metadata = JSON.parse(metadataContent); } catch (error) { // Metadata file exists but couldn't be parsed, continue without it } } return { success: true, data: { message: 'Backup restored successfully', backupPath: params.backupPath, targetPath, format, metadata, output: result.stdout }, metadata: { operation: 'restore', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'RESTORE_ERROR', `Failed to restore backup: ${errorMessage}`, 'restore', { error: errorMessage, backupPath: params.backupPath } ); } } /** * Handle backup listing operation */ private async handleList(params: GitBackupParams, startTime: number): Promise<ToolResult> { try { const backupDir = params.backupPath || path.join(params.projectPath, '..', 'backups'); if (!existsSync(backupDir)) { return { success: true, data: { message: 'No backup directory found', backups: [], backupDir }, metadata: { operation: 'list', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } const files = await fs.readdir(backupDir); const backups = []; for (const file of files) { if (file.endsWith('.tar.gz') || file.endsWith('.zip')) { const filePath = path.join(backupDir, file); const stats = await fs.stat(filePath); // Try to load metadata const metadataPath = filePath.replace(/\.(zip|tar\.gz)$/, '.meta.json'); let metadata = null; if (existsSync(metadataPath)) { try { const metadataContent = await fs.readFile(metadataPath, 'utf-8'); metadata = JSON.parse(metadataContent); } catch (error) { // Continue without metadata if parsing fails } } backups.push({ name: file, path: filePath, size: stats.size, created: stats.birthtime.toISOString(), modified: stats.mtime.toISOString(), format: file.endsWith('.zip') ? 'zip' : 'tar', metadata }); } } // Sort backups const sortBy = params.sortBy || 'date'; backups.sort((a, b) => { switch (sortBy) { case 'name': return a.name.localeCompare(b.name); case 'size': return b.size - a.size; case 'date': default: return new Date(b.created).getTime() - new Date(a.created).getTime(); } }); return { success: true, data: { message: `Found ${backups.length} backup(s)`, backups, backupDir, sortBy }, metadata: { operation: 'list', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'LIST_ERROR', `Failed to list backups: ${errorMessage}`, 'list', { error: errorMessage, backupPath: params.backupPath } ); } } /** * Handle backup verification operation */ private async handleVerify(params: GitBackupParams, startTime: number): Promise<ToolResult> { try { if (!params.backupPath) { return OperationErrorHandler.createToolError( 'MISSING_BACKUP_PATH', 'Backup path is required for verify operation', 'verify', {}, ['Provide backupPath parameter with the path to the backup file'] ); } if (!existsSync(params.backupPath)) { return OperationErrorHandler.createToolError( 'BACKUP_NOT_FOUND', `Backup file not found: ${params.backupPath}`, 'verify', { backupPath: params.backupPath }, ['Ensure the backup file exists and path is correct'] ); } const stats = await fs.stat(params.backupPath); const isZip = params.backupPath.endsWith('.zip'); const format = isZip ? 'zip' : 'tar'; // Basic file verification const verification = { exists: true, readable: true, size: stats.size, format, created: stats.birthtime.toISOString(), modified: stats.mtime.toISOString(), integrity: 'unknown' as 'valid' | 'invalid' | 'unknown' }; // Test archive integrity if requested if (params.checkIntegrity) { let result: GitCommandResult; if (format === 'zip') { // Test zip file integrity result = await this.terminal.executeCommand('unzip', ['-t', params.backupPath]); } else { // Test tar.gz file integrity result = await this.terminal.executeCommand('tar', ['-tzf', params.backupPath]); } verification.integrity = result.success ? 'valid' : 'invalid'; } // Load and verify metadata if available const metadataPath = params.backupPath.replace(/\.(zip|tar\.gz)$/, '.meta.json'); let metadata = null; let metadataValid = false; if (existsSync(metadataPath)) { try { const metadataContent = await fs.readFile(metadataPath, 'utf-8'); metadata = JSON.parse(metadataContent); metadataValid = true; // Verify metadata consistency if (metadata.size !== stats.size) { metadataValid = false; } } catch (error) { metadataValid = false; } } const isValid = verification.integrity !== 'invalid' && (metadata ? metadataValid : true); return { success: true, data: { message: isValid ? 'Backup verification passed' : 'Backup verification failed', valid: isValid, verification, metadata: metadata ? { ...metadata, valid: metadataValid } : null }, metadata: { operation: 'verify', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'VERIFY_ERROR', `Failed to verify backup: ${errorMessage}`, 'verify', { error: errorMessage, backupPath: params.backupPath } ); } } /** * Get tool schema for MCP registration */ static getToolSchema() { return { name: 'git-backup', description: 'Comprehensive backup system for Git repositories. Supports backup creation, restoration, listing, and verification.', inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['backup', 'restore', 'list', 'verify'], description: 'The backup operation to perform' }, projectPath: { type: 'string', description: 'Path to the Git repository (required)' }, backupPath: { type: 'string', description: 'Path for backup storage or backup file to restore/verify' }, name: { type: 'string', description: 'Backup name/identifier (for backup operation)' }, format: { type: 'string', enum: ['tar', 'zip'], description: 'Backup format (default: tar)' }, compression: { type: 'boolean', description: 'Enable compression (default: true)' }, includeUntracked: { type: 'boolean', description: 'Include untracked files in backup (default: false)' }, targetPath: { type: 'string', description: 'Target path for restoration (for restore operation)' }, overwrite: { type: 'boolean', description: 'Overwrite existing files during restore (default: false)' }, sortBy: { type: 'string', enum: ['name', 'date', 'size'], description: 'Sort criteria for listing backups (default: date)' }, checkIntegrity: { type: 'boolean', description: 'Check backup integrity during verification (default: false)' } }, required: ['action', 'projectPath'] } }; } }
- src/tools/git-backup.ts:497-555 (schema)Tool schema definition including name, description, and detailed inputSchema with parameters for action, projectPath, backupPath, format, etc.static getToolSchema() { return { name: 'git-backup', description: 'Comprehensive backup system for Git repositories. Supports backup creation, restoration, listing, and verification.', inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['backup', 'restore', 'list', 'verify'], description: 'The backup operation to perform' }, projectPath: { type: 'string', description: 'Path to the Git repository (required)' }, backupPath: { type: 'string', description: 'Path for backup storage or backup file to restore/verify' }, name: { type: 'string', description: 'Backup name/identifier (for backup operation)' }, format: { type: 'string', enum: ['tar', 'zip'], description: 'Backup format (default: tar)' }, compression: { type: 'boolean', description: 'Enable compression (default: true)' }, includeUntracked: { type: 'boolean', description: 'Include untracked files in backup (default: false)' }, targetPath: { type: 'string', description: 'Target path for restoration (for restore operation)' }, overwrite: { type: 'boolean', description: 'Overwrite existing files during restore (default: false)' }, sortBy: { type: 'string', enum: ['name', 'date', 'size'], description: 'Sort criteria for listing backups (default: date)' }, checkIntegrity: { type: 'boolean', description: 'Check backup integrity during verification (default: false)' } }, required: ['action', 'projectPath'] } }; }
- src/server.ts:494-495 (registration)Registration and dispatch in server executeTool switch statement calling gitBackupTool.execute().case 'git-backup': return await this.gitBackupTool.execute(args);
- src/server.ts:101-101 (registration)Instantiation of GitBackupTool instance in server initialization.this.gitBackupTool = new GitBackupTool();
- src/server.ts:137-137 (registration)Tool schema returned in ListToolsRequestHandler for MCP tool discovery.GitBackupTool.getToolSchema(),
- Parameter validation schema defining supported actions for git-backup.'git-backup': ['backup', 'restore', 'list', 'verify'],
- Remote operations schema marking git-backup as local-only.'git-backup': [], // Backup operations are local