Get the FREE Ultimate OpenClaw Setup Guide →

configuration

npx machina-cli add skill Myst4ke/mcp-to-skills-converter/configuration --openclaw
Files (1)
SKILL.md
10.1 KB

Configuration Management Skill

Overview

This skill provides guidance for implementing safe configuration file management including backup systems, atomic updates, and recovery mechanisms. For US-4 (MCP Uninstaller), this skill covers:

  • Creating timestamped configuration backups
  • Atomic JSON file updates
  • Configuration validation
  • Backup restoration and cleanup

Core Capabilities

  • Atomic File Operations: Write-then-rename pattern for safe file updates
  • Backup Management: Create, restore, and clean up timestamped backups
  • JSON Manipulation: Safe parsing, modification, and validation of JSON config files
  • Error Recovery: Automatic rollback on failures
  • Metadata Tracking: Include metadata in backups for audit trail
  • Cleanup Policies: Implement retention policies (e.g., keep last 10 backups)

Input Requirements

Required:

  • Configuration file path
  • Operation type (backup, update, restore)
  • Backup directory path

Optional:

  • Retention policy (number of backups to keep)
  • Validation schema
  • Metadata to include in backup

Output Format

Returns:

  • Operation result (success/failure)
  • Backup file path (for backup operations)
  • Validation results
  • Error messages if applicable

Dependencies

  • fs-extra: Enhanced file system operations
  • JSON schema validator (optional): For config validation
  • TypeScript 5.x: For type-safe config handling

Workflow

Step 1: Define Configuration Interfaces

Create TypeScript interfaces for config structure:

// src/interfaces/config.interface.ts
export interface ClaudeConfig {
  mcpServers?: Record<string, MCPServerConfig>;
  [key: string]: any;
}

export interface MCPServerConfig {
  command: string;
  args?: string[];
  env?: Record<string, string>;
}

export interface BackupMetadata {
  timestamp: string;
  originalPath: string;
  reason: string;
  mcpName?: string;
}

Step 2: Implement Backup Service

Create backup with metadata:

// src/services/config-backup.ts
import * as fs from 'fs-extra';
import * as path from 'path';

export class ConfigBackup {
  private backupDir: string;
  private maxBackups: number;

  constructor(backupDir: string = '.claude/backups', maxBackups: number = 10) {
    this.backupDir = backupDir;
    this.maxBackups = maxBackups;
  }

  async createBackup(configPath: string, reason: string, mcpName?: string): Promise<string> {
    // Ensure backup directory exists
    await fs.ensureDir(this.backupDir);

    // Generate timestamp
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
    const backupFileName = `config-backup-${timestamp}.json`;
    const backupPath = path.join(this.backupDir, backupFileName);

    // Read original config
    const config = await fs.readJson(configPath);

    // Create backup with metadata
    const backup = {
      metadata: {
        timestamp: new Date().toISOString(),
        originalPath: configPath,
        reason,
        mcpName
      },
      config
    };

    // Write backup atomically
    await this.atomicWrite(backupPath, JSON.stringify(backup, null, 2));

    // Verify backup
    if (!await fs.pathExists(backupPath)) {
      throw new Error('Backup verification failed');
    }

    // Clean up old backups
    await this.cleanupOldBackups();

    return backupPath;
  }

  private async atomicWrite(filePath: string, content: string): Promise<void> {
    const tempPath = `${filePath}.tmp`;
    await fs.writeFile(tempPath, content, 'utf-8');
    await fs.rename(tempPath, filePath);
  }

  private async cleanupOldBackups(): Promise<void> {
    const files = await fs.readdir(this.backupDir);
    const backupFiles = files
      .filter(f => f.startsWith('config-backup-'))
      .sort()
      .reverse();

    // Keep only the most recent maxBackups
    const filesToDelete = backupFiles.slice(this.maxBackups);
    for (const file of filesToDelete) {
      await fs.remove(path.join(this.backupDir, file));
    }
  }

  async restore(backupPath: string, targetPath: string): Promise<void> {
    const backup = await fs.readJson(backupPath);

    if (!backup.config) {
      throw new Error('Invalid backup file: missing config data');
    }

    // Write config atomically
    await this.atomicWrite(targetPath, JSON.stringify(backup.config, null, 2));
  }
}

Step 3: Implement Configuration Update

Update config with atomic operations:

// src/services/config-updater.ts
import * as fs from 'fs-extra';

export class ConfigUpdater {
  async removeMCPEntry(configPath: string, mcpName: string): Promise<void> {
    // Read config
    const config = await fs.readJson(configPath);

    // Remove MCP entry
    if (config.mcpServers && config.mcpServers[mcpName]) {
      delete config.mcpServers[mcpName];
    } else {
      throw new Error(`MCP "${mcpName}" not found in configuration`);
    }

    // Validate resulting config
    this.validateConfig(config);

    // Write atomically
    const tempPath = `${configPath}.tmp`;
    await fs.writeJson(tempPath, config, { spaces: 2 });
    await fs.rename(tempPath, configPath);

    // Verify write
    const written = await fs.readJson(configPath);
    if (JSON.stringify(written) !== JSON.stringify(config)) {
      throw new Error('Configuration verification failed');
    }
  }

  private validateConfig(config: any): void {
    // Basic validation
    if (typeof config !== 'object' || config === null) {
      throw new Error('Invalid config: must be an object');
    }

    // Validate mcpServers if present
    if (config.mcpServers) {
      if (typeof config.mcpServers !== 'object') {
        throw new Error('Invalid config: mcpServers must be an object');
      }

      for (const [name, server] of Object.entries(config.mcpServers)) {
        if (typeof server !== 'object' || server === null) {
          throw new Error(`Invalid config: mcpServers.${name} must be an object`);
        }
      }
    }
  }

  async updateWithRetry(configPath: string, mcpName: string, maxAttempts: number = 3): Promise<void> {
    let lastError: Error | null = null;

    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      try {
        await this.removeMCPEntry(configPath, mcpName);
        return; // Success
      } catch (error) {
        lastError = error instanceof Error ? error : new Error('Unknown error');

        if (error instanceof Error && error.message.includes('EBUSY')) {
          // File is locked, wait and retry
          await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
        } else {
          // Non-retryable error
          throw error;
        }
      }
    }

    throw new Error(`Config update failed after ${maxAttempts} attempts: ${lastError?.message}`);
  }
}

Step 4: Integrate with Rollback

Support transaction-like operations:

// src/services/config-transaction.ts
export class ConfigTransaction {
  private backup: ConfigBackup;
  private updater: ConfigUpdater;
  private backupPath?: string;

  constructor() {
    this.backup = new ConfigBackup();
    this.updater = new ConfigUpdater();
  }

  async execute(configPath: string, mcpName: string): Promise<void> {
    try {
      // Step 1: Create backup
      this.backupPath = await this.backup.createBackup(
        configPath,
        `Removing MCP: ${mcpName}`,
        mcpName
      );

      // Step 2: Update config
      await this.updater.removeMCPEntry(configPath, mcpName);

    } catch (error) {
      // Rollback on error
      if (this.backupPath) {
        await this.backup.restore(this.backupPath, configPath);
      }
      throw error;
    }
  }

  async rollback(configPath: string): Promise<void> {
    if (!this.backupPath) {
      throw new Error('No backup available for rollback');
    }

    await this.backup.restore(this.backupPath, configPath);
  }
}

Example Usage

Example 1: Create Backup (TASK-4.2)

import { ConfigBackup } from './services/config-backup';

const backup = new ConfigBackup('.claude/backups', 10);

// Create backup before modifications
const backupPath = await backup.createBackup(
  '~/.claude/claude_desktop_config.json',
  'Pre-uninstall backup',
  'server-filesystem'
);

console.log(`Backup created: ${backupPath}`);

Example 2: Update Configuration (TASK-4.5)

import { ConfigUpdater } from './services/config-updater';

const updater = new ConfigUpdater();

// Remove MCP entry with retry logic
await updater.updateWithRetry(
  '~/.claude/claude_desktop_config.json',
  'server-filesystem',
  3 // max retries
);

console.log('MCP entry removed successfully');

Example 3: Transactional Update

import { ConfigTransaction } from './services/config-transaction';

const transaction = new ConfigTransaction();

try {
  // Execute with automatic backup
  await transaction.execute(
    '~/.claude/claude_desktop_config.json',
    'server-filesystem'
  );

  console.log('Configuration updated successfully');
} catch (error) {
  console.error('Update failed, rollback performed:', error);
}

Error Handling

File Locked Error

if (error.code === 'EBUSY' || error.code === 'EPERM') {
  throw new Error('Configuration file is locked. Close Claude Code and try again.');
}

Malformed JSON Error

try {
  const config = await fs.readJson(configPath);
} catch (error) {
  throw new Error(`Invalid JSON in config file: ${error.message}. Please fix manually or restore from backup.`);
}

Backup Restore Error

if (!await fs.pathExists(backupPath)) {
  throw new Error(`Backup file not found: ${backupPath}. Cannot perform rollback.`);
}

Integration Points

With API Services

  • Backup service is used by configuration updater
  • Both used by rollback mechanism

With Rollback Mechanism

  • Provides backup/restore capabilities
  • Supports transaction-like operations

With CLI Interface

  • CLI orchestrates backup and update operations
  • Displays backup paths and success messages

With Testing

  • Mock file system for unit tests
  • Test atomic operations and rollback

Source

git clone https://github.com/Myst4ke/mcp-to-skills-converter/blob/main/.claude/spec-implementer-generated/skills/configuration/SKILL.mdView on GitHub

Overview

This skill guides safe configuration management in TypeScript, covering timestamped backups, atomic JSON updates, validation, and recovery. It emphasizes backup restoration, metadata tracking, and cleanup policies to minimize risk during config changes.

How This Skill Works

Define TypeScript interfaces for the config, implement a backup service to create metadata-rich backups, and perform write-then-rename atomic updates. The system validates JSON when updating, automatically rolls back on failure, and enforces a retention policy to keep only the desired number of backups.

When to Use It

  • Implementing configuration management for a TypeScript application
  • Performing atomic updates to JSON configuration files
  • Backing up and restoring configurations during MCP uninstall or upgrades
  • Enforcing backup retention policies and cleanup
  • Validating config against a schema before loading or applying changes

Quick Start

  1. Step 1: Define TypeScript interfaces for your config structure (e.g., ClaudeConfig, MCPServerConfig, BackupMetadata)
  2. Step 2: Implement a backup service that creates a metadata-rich backup file and stores it in a backups directory
  3. Step 3: Perform an update using an atomic write (temp file + rename), followed by retention cleanup and optional validation

Best Practices

  • Always back up before applying changes to a config file
  • Use the write-then-rename atomic update pattern
  • Include high-value metadata in each backup for audit trails
  • Set a retention policy (e.g., keep last 10 backups) and clean up older files
  • Optionally run JSON schema validation before applying changes and rollback on failure

Example Use Cases

  • Create a timestamped backup of a JSON config before updating a server setting
  • Apply atomic updates to a config file to prevent partial writes during deployment
  • Restore the latest backup if a new config causes validation errors or corruption
  • Enforce a retention policy to keep only the last 10 backups and delete older files
  • Validate the config against a schema prior to loading and applying it

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers