Skip to main content
Glama

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
NameRequiredDescriptionDefault
actionYesThe backup operation to perform
backupPathNoPath for backup storage or backup file to restore/verify
checkIntegrityNoCheck backup integrity during verification (default: false)
compressionNoEnable compression (default: true)
formatNoBackup format (default: tar)
includeUntrackedNoInclude untracked files in backup (default: false)
nameNoBackup name/identifier (for backup operation)
overwriteNoOverwrite existing files during restore (default: false)
projectPathYesPath to the Git repository (required)
sortByNoSort criteria for listing backups (default: date)
targetPathNoTarget path for restoration (for restore operation)

Implementation Reference

  • 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'] } }; } }
  • 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

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/Andre-Buzeli/git-mcp'

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