Skip to main content
Glama
git-config.ts18.2 kB
/** * Git Configuration Tool * * Git configuration management tool providing comprehensive Git config operations. * Supports get, set, unset, list, edit, and show operations for Git configuration. * * Operations: get, set, unset, list, edit, show */ import { GitCommandExecutor, GitCommandResult } from '../utils/git-command-executor.js'; import { ParameterValidator, ToolParams } from '../utils/parameter-validator.js'; import { OperationErrorHandler, ToolResult } from '../utils/operation-error-handler.js'; import { configManager } from '../config.js'; export interface GitConfigParams extends ToolParams { action: 'get' | 'set' | 'unset' | 'list' | 'edit' | 'show'; // Configuration parameters key?: string; // Configuration key (required for get, set, unset, show) value?: string; // Configuration value (required for set) // Scope parameters global?: boolean; // Use global configuration local?: boolean; // Use local repository configuration system?: boolean; // Use system configuration // Display options showOrigin?: boolean; // Show configuration file origin showScope?: boolean; // Show configuration scope } export class GitConfigTool { private gitExecutor: GitCommandExecutor; constructor() { this.gitExecutor = new GitCommandExecutor(); } /** * Execute git-config operation */ async execute(params: GitConfigParams): Promise<ToolResult> { const startTime = Date.now(); try { // Validate basic parameters const validation = ParameterValidator.validateToolParams('git-config', params); if (!validation.isValid) { return OperationErrorHandler.createToolError( 'VALIDATION_ERROR', `Parameter validation failed: ${validation.errors.join(', ')}`, params.action, { validationErrors: validation.errors }, validation.suggestions ); } // Validate operation-specific parameters const operationValidation = this.validateOperationParams(params); if (!operationValidation.isValid) { return OperationErrorHandler.createToolError( 'VALIDATION_ERROR', `Operation validation failed: ${operationValidation.errors.join(', ')}`, params.action, { validationErrors: operationValidation.errors }, operationValidation.suggestions ); } // Route to appropriate handler switch (params.action) { case 'get': return await this.handleGet(params, startTime); case 'set': return await this.handleSet(params, startTime); case 'unset': return await this.handleUnset(params, startTime); case 'list': return await this.handleList(params, startTime); case 'edit': return await this.handleEdit(params, startTime); case 'show': return await this.handleShow(params, startTime); default: return OperationErrorHandler.createToolError( 'UNSUPPORTED_OPERATION', `Operation '${params.action}' is not supported`, params.action, {}, ['Use one of: get, set, unset, list, edit, show'] ); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'EXECUTION_ERROR', `Failed to execute ${params.action}: ${errorMessage}`, params.action, { error: errorMessage }, ['Check the error details and try again'] ); } } /** * Validate operation-specific parameters */ private validateOperationParams(params: GitConfigParams): { isValid: boolean; errors: string[]; suggestions: string[] } { const errors: string[] = []; const suggestions: string[] = []; // Validate key requirement for specific operations if (['get', 'set', 'unset'].includes(params.action) && !params.key) { errors.push(`Key parameter is required for ${params.action} operation`); suggestions.push('Provide a configuration key (e.g., user.name, user.email)'); } // Validate value requirement for set operation if (params.action === 'set' && !params.value) { errors.push('Value parameter is required for set operation'); suggestions.push('Provide a configuration value to set'); } // Validate scope conflicts const scopeCount = [params.global, params.local, params.system].filter(Boolean).length; if (scopeCount > 1) { errors.push('Only one scope can be specified (global, local, or system)'); suggestions.push('Choose either --global, --local, or --system, not multiple'); } // Validate key format if (params.key && !this.isValidConfigKey(params.key)) { errors.push('Invalid configuration key format'); suggestions.push('Use format like: user.name, core.editor, remote.origin.url'); } return { isValid: errors.length === 0, errors, suggestions }; } /** * Validate configuration key format */ private isValidConfigKey(key: string): boolean { // Git config keys should contain at least one dot and valid characters return /^[a-zA-Z][a-zA-Z0-9-]*(\.[a-zA-Z][a-zA-Z0-9-]*)+$/.test(key); } /** * Handle get configuration operation */ private async handleGet(params: GitConfigParams, startTime: number): Promise<ToolResult> { try { const result = await this.gitExecutor.getConfig( params.projectPath, params.key!, { global: params.global, local: params.local, system: params.system } ); if (!result.success) { // Check if it's a "not found" error if (result.stderr.includes('not found') || result.exitCode === 1) { return { success: true, data: { key: params.key, value: null, found: false, message: `Configuration key '${params.key}' not found`, scope: this.getScopeString(params) }, metadata: { operation: 'get', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } return OperationErrorHandler.handleGitError(result.stderr, 'get config', params.projectPath); } return { success: true, data: { key: params.key, value: result.value, found: true, scope: this.getScopeString(params), raw: result.stdout }, metadata: { operation: 'get', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'GET_CONFIG_ERROR', `Failed to get configuration: ${errorMessage}`, 'get', { error: errorMessage, key: params.key } ); } } /** * Handle set configuration operation */ private async handleSet(params: GitConfigParams, startTime: number): Promise<ToolResult> { try { const result = await this.gitExecutor.setConfig( params.projectPath, params.key!, params.value!, { global: params.global, local: params.local, system: params.system } ); if (!result.success) { return OperationErrorHandler.handleGitError(result.stderr, 'set config', params.projectPath); } return { success: true, data: { message: 'Configuration set successfully', key: params.key, value: params.value, scope: this.getScopeString(params), output: result.stdout }, metadata: { operation: 'set', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'SET_CONFIG_ERROR', `Failed to set configuration: ${errorMessage}`, 'set', { error: errorMessage, key: params.key, value: params.value } ); } } /** * Handle unset configuration operation */ private async handleUnset(params: GitConfigParams, startTime: number): Promise<ToolResult> { try { const result = await this.gitExecutor.unsetConfig( params.projectPath, params.key!, { global: params.global, local: params.local, system: params.system } ); if (!result.success) { // Check if it's a "not found" error if (result.stderr.includes('not found') || result.exitCode === 5) { return { success: true, data: { key: params.key, found: false, message: `Configuration key '${params.key}' was not set`, scope: this.getScopeString(params) }, metadata: { operation: 'unset', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } return OperationErrorHandler.handleGitError(result.stderr, 'unset config', params.projectPath); } return { success: true, data: { message: 'Configuration unset successfully', key: params.key, scope: this.getScopeString(params), output: result.stdout }, metadata: { operation: 'unset', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'UNSET_CONFIG_ERROR', `Failed to unset configuration: ${errorMessage}`, 'unset', { error: errorMessage, key: params.key } ); } } /** * Handle list configuration operation */ private async handleList(params: GitConfigParams, startTime: number): Promise<ToolResult> { try { const result = await this.gitExecutor.listConfig( params.projectPath, { global: params.global, local: params.local, system: params.system, showOrigin: params.showOrigin } ); if (!result.success) { return OperationErrorHandler.handleGitError(result.stderr, 'list config', params.projectPath); } const configs = result.configs || []; const groupedConfigs = this.groupConfigsBySection(configs); return { success: true, data: { message: `Found ${configs.length} configuration entries`, scope: this.getScopeString(params), totalCount: configs.length, configs: configs, groupedConfigs: groupedConfigs, showOrigin: params.showOrigin, raw: result.stdout }, 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_CONFIG_ERROR', `Failed to list configuration: ${errorMessage}`, 'list', { error: errorMessage } ); } } /** * Handle edit configuration operation */ private async handleEdit(params: GitConfigParams, startTime: number): Promise<ToolResult> { try { const result = await this.gitExecutor.editConfig( params.projectPath, { global: params.global, local: params.local, system: params.system } ); if (!result.success) { return OperationErrorHandler.handleGitError(result.stderr, 'edit config', params.projectPath); } return { success: true, data: { message: 'Configuration editor opened successfully', scope: this.getScopeString(params), note: 'Configuration file was opened in the default editor', output: result.stdout }, metadata: { operation: 'edit', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'EDIT_CONFIG_ERROR', `Failed to edit configuration: ${errorMessage}`, 'edit', { error: errorMessage } ); } } /** * Handle show configuration operation */ private async handleShow(params: GitConfigParams, startTime: number): Promise<ToolResult> { try { const result = await this.gitExecutor.showConfig( params.projectPath, params.key, { global: params.global, local: params.local, system: params.system, showOrigin: params.showOrigin, showScope: params.showScope } ); if (!result.success) { // Check if it's a "not found" error for specific key if (params.key && (result.stderr.includes('not found') || result.exitCode === 1)) { return { success: true, data: { key: params.key, found: false, message: `Configuration key '${params.key}' not found`, scope: this.getScopeString(params) }, metadata: { operation: 'show', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } return OperationErrorHandler.handleGitError(result.stderr, 'show config', params.projectPath); } const configs = result.configs || []; const groupedConfigs = params.key ? undefined : this.groupConfigsBySection(configs); return { success: true, data: { message: params.key ? `Configuration for '${params.key}'` : `Configuration details (${configs.length} entries)`, key: params.key, scope: this.getScopeString(params), totalCount: configs.length, configs: configs, groupedConfigs: groupedConfigs, showOrigin: params.showOrigin, showScope: params.showScope, raw: result.stdout }, metadata: { operation: 'show', timestamp: new Date().toISOString(), executionTime: Date.now() - startTime } }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return OperationErrorHandler.createToolError( 'SHOW_CONFIG_ERROR', `Failed to show configuration: ${errorMessage}`, 'show', { error: errorMessage, key: params.key } ); } } /** * Get scope string for display */ private getScopeString(params: GitConfigParams): string { if (params.global) return 'global'; if (params.local) return 'local'; if (params.system) return 'system'; return 'default'; } /** * Group configurations by section */ private groupConfigsBySection(configs: Array<{ key: string; value: string; origin?: string; scope?: string }>): Record<string, Array<{ key: string; value: string; origin?: string; scope?: string }>> { const grouped: Record<string, Array<{ key: string; value: string; origin?: string; scope?: string }>> = {}; for (const config of configs) { const dotIndex = config.key.indexOf('.'); const section = dotIndex > 0 ? config.key.substring(0, dotIndex) : 'other'; if (!grouped[section]) { grouped[section] = []; } grouped[section].push(config); } return grouped; } /** * Get tool schema for MCP registration */ static getToolSchema() { return { name: 'git-config', description: 'Git configuration management tool for get, set, unset, list, edit, and show operations. Supports global, local, and system configuration scopes.', inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['get', 'set', 'unset', 'list', 'edit', 'show'], description: 'The Git configuration operation to perform' }, projectPath: { type: 'string', description: 'Absolute path to the project directory' }, key: { type: 'string', description: 'Configuration key (required for get, set, unset, show operations). Examples: user.name, user.email, core.editor' }, value: { type: 'string', description: 'Configuration value (required for set operation)' }, global: { type: 'boolean', description: 'Use global configuration (~/.gitconfig)' }, local: { type: 'boolean', description: 'Use local repository configuration (.git/config)' }, system: { type: 'boolean', description: 'Use system configuration (/etc/gitconfig)' }, showOrigin: { type: 'boolean', description: 'Show configuration file origin (for list and show operations)' }, showScope: { type: 'boolean', description: 'Show configuration scope (for show operation)' } }, required: ['action', 'projectPath'] } }; } }

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