Skip to content

Supabase MCP Database Workflow

This guide introduces how to use Supabase MCP to build intelligent database management and workflow systems, enabling ByteBuddy to automate database operations.

Overview

Supabase MCP provides deep integration with the Supabase platform, allowing ByteBuddy to:

  • Automate database migrations
  • Optimize queries intelligently
  • Synchronize data in real-time
  • Manage backups and recovery

Configuration

1. Install Supabase MCP

bash
npm install -g supabase-mcp-server

2. Configure ByteBuddy

json
{
  "mcpServers": {
    "supabase": {
      "command": "node",
      "args": ["supabase-mcp-server"],
      "env": {
        "SUPABASE_URL": "https://your-project.supabase.co",
        "SUPABASE_KEY": "your-service-role-key",
        "SUPABASE_PROJECT_ID": "your-project-id"
      }
    }
  }
}

Use Cases

Scenario 1: Automated Database Migration

typescript
// Database migration manager
class DatabaseMigrationManager {
  async runMigrations(migrationConfig: any) {
    // Check database status
    const dbStatus = await this.checkDatabaseStatus();

    if (dbStatus.isLocked) {
      throw new Error(
        "Database is under maintenance, cannot execute migration",
      );
    }

    // Get pending migrations
    const pendingMigrations = await this.getPendingMigrations();

    // Execute migration plan
    const migrationPlan = await this.createMigrationPlan(pendingMigrations);

    // Backup database
    const backup = await this.createBackup("pre-migration");

    try {
      // Execute migrations
      const results = [];
      for (const migration of migrationPlan.migrations) {
        const result = await this.executeMigration(migration);
        results.push(result);
      }

      // Validate migration results
      await this.validateMigration(results);

      return { success: true, migrations: results, backupId: backup.id };
    } catch (error) {
      // Rollback migration
      await this.rollbackMigration(backup.id);
      throw error;
    }
  }

  private async executeMigration(migration: any) {
    const startTime = Date.now();

    try {
      // Execute SQL
      const result = await mcp.call("supabase.executeSql", {
        sql: migration.sql,
        parameters: migration.parameters || [],
      });

      // Record migration history
      await this.recordMigration(migration, result, true);

      return {
        id: migration.id,
        success: true,
        executionTime: Date.now() - startTime,
        affectedRows: result.affectedRows,
        result: result.data,
      };
    } catch (error) {
      // Record failure
      await this.recordMigration(migration, error, false);

      return {
        id: migration.id,
        success: false,
        executionTime: Date.now() - startTime,
        error: error.message,
      };
    }
  }

  private async createMigrationPlan(migrations: any[]) {
    // Analyze migration dependencies
    const dependencyGraph = this.buildDependencyGraph(migrations);

    // Sort migration order
    const sortedMigrations = this.topologicalSort(dependencyGraph);

    // Validate migration safety
    const validatedMigrations = await this.validateMigrations(sortedMigrations);

    return {
      migrations: validatedMigrations,
      estimatedTime: this.estimateExecutionTime(validatedMigrations),
      riskLevel: this.assessRiskLevel(validatedMigrations),
    };
  }
}

Scenario 2: Intelligent Query Optimization

typescript
// Query optimizer
class QueryOptimizer {
  async optimizeSlowQueries(timeRange: string = "24h") {
    // Get slow query logs
    const slowQueries = await mcp.call("supabase.getSlowQueries", {
      timeRange: timeRange,
      threshold: 1000, // 1 second threshold
    });

    const optimizations = [];

    for (const query of slowQueries) {
      const optimization = await this.optimizeQuery(query);
      optimizations.push(optimization);
    }

    // Apply optimization recommendations
    const appliedOptimizations = await this.applyOptimizations(optimizations);

    return {
      originalQueries: slowQueries.length,
      optimizations: optimizations.length,
      applied: appliedOptimizations.length,
      performanceGain: this.calculatePerformanceGain(appliedOptimizations),
    };
  }

  private async optimizeQuery(query: any) {
    // Analyze query execution plan
    const executionPlan = await mcp.call("supabase.explainQuery", {
      sql: query.sql,
      parameters: query.parameters,
    });

    // Identify performance bottlenecks
    const bottlenecks = this.identifyBottlenecks(executionPlan);

    // Generate optimization suggestions
    const suggestions = await this.generateOptimizationSuggestions(
      query,
      bottlenecks,
    );

    // Generate optimized query
    const optimizedQuery = await this.generateOptimizedQuery(
      query,
      suggestions,
    );

    return {
      original: query,
      bottlenecks,
      suggestions,
      optimized: optimizedQuery,
      estimatedImprovement: this.estimateImprovement(query, optimizedQuery),
    };
  }

  private identifyBottlenecks(executionPlan: any) {
    const bottlenecks = [];

    // Check for full table scans
    if (executionPlan.scanType === "Seq Scan") {
      bottlenecks.push({
        type: "full_table_scan",
        table: executionPlan.tableName,
        suggestion: "Add appropriate indexes",
      });
    }

    // Check for missing indexes
    if (
      executionPlan.missingIndexes &&
      executionPlan.missingIndexes.length > 0
    ) {
      bottlenecks.push({
        type: "missing_index",
        columns: executionPlan.missingIndexes,
        suggestion: "Create composite indexes",
      });
    }

    // Check for sorting operations
    if (executionPlan.sortMethod === "External Sort") {
      bottlenecks.push({
        type: "external_sort",
        suggestion: "Optimize sorting fields or increase memory",
      });
    }

    return bottlenecks;
  }

  private async generateOptimizedQuery(query: any, suggestions: any[]) {
    let optimizedSql = query.sql;
    const parameters = [...(query.parameters || [])];

    // Apply optimization suggestions
    for (const suggestion of suggestions) {
      switch (suggestion.type) {
        case "add_index":
          // Generate index creation statement
          await this.createIndex(suggestion);
          break;

        case "rewrite_join":
          optimizedSql = this.rewriteJoin(optimizedSql, suggestion);
          break;

        case "add_limit":
          optimizedSql = this.addLimit(optimizedSql, suggestion);
          break;
      }
    }

    return {
      sql: optimizedSql,
      parameters,
      optimizations: suggestions.length,
    };
  }
}

Scenario 3: Data Synchronization and Backup

typescript
// Data synchronization manager
class DataSyncManager {
  async setupDataSync(syncConfig: any) {
    // Create sync task
    const syncTask = await this.createSyncTask(syncConfig);

    // Set up triggers
    await this.setupTriggers(syncConfig);

    // Configure real-time sync
    if (syncConfig.realtime) {
      await this.setupRealtimeSync(syncConfig);
    }

    return syncTask;
  }

  private async setupRealtimeSync(config: any) {
    // Subscribe to data changes
    const subscription = await mcp.call("supabase.subscribe", {
      table: config.sourceTable,
      event: "*",
    });

    // Handle change events
    subscription.on("change", async (payload) => {
      await this.handleChange(payload, config);
    });

    return subscription;
  }

  private async handleChange(payload: any, config: any) {
    const { eventType, record, oldRecord } = payload;

    switch (eventType) {
      case "INSERT":
        await this.syncInsert(record, config);
        break;
      case "UPDATE":
        await this.syncUpdate(record, oldRecord, config);
        break;
      case "DELETE":
        await this.syncDelete(oldRecord, config);
        break;
    }
  }

  private async createBackup(backupType: string) {
    const backup = await mcp.call("supabase.createBackup", {
      type: backupType,
      compression: "gzip",
      encryption: true,
    });

    // Verify backup integrity
    const integrityCheck = await this.verifyBackupIntegrity(backup.id);

    if (!integrityCheck.valid) {
      throw new Error(`Backup verification failed: ${integrityCheck.error}`);
    }

    return backup;
  }
}

Monitoring and Alerts

1: Performance Monitoring

typescript
// Performance monitoring system
class PerformanceMonitor {
  async monitorDatabaseHealth() {
    const metrics = {
      connections: await this.getConnectionMetrics(),
      queries: await this.getQueryMetrics(),
      storage: await this.getStorageMetrics(),
      performance: await this.getPerformanceMetrics(),
    };

    // Calculate health score
    const healthScore = this.calculateHealthScore(metrics);

    // Generate alerts
    const alerts = await this.generateAlerts(metrics);

    // Create monitoring report
    const report = await this.createMonitoringReport(metrics, healthScore);

    return { metrics, healthScore, alerts, report };
  }

  private async getConnectionMetrics() {
    return await mcp.call("supabase.getConnectionMetrics", {
      timeRange: "1h",
    });
  }

  private async generateAlerts(metrics: any) {
    const alerts = [];

    // Connection alert
    if (metrics.connections.active > metrics.connections.max * 0.8) {
      alerts.push({
        type: "connection_limit",
        severity: "warning",
        message: `Connection usage reached ${((metrics.connections.active / metrics.connections.max) * 100).toFixed(1)}%`,
        recommendation:
          "Consider increasing connection pool size or optimizing queries",
      });
    }

    // Query performance alert
    if (metrics.performance.avgQueryTime > 1000) {
      alerts.push({
        type: "slow_queries",
        severity: "warning",
        message: `Average query time: ${metrics.performance.avgQueryTime}ms`,
        recommendation: "Check slow query logs and optimize queries",
      });
    }

    // Storage space alert
    if (metrics.storage.usage > metrics.storage.limit * 0.9) {
      alerts.push({
        type: "storage_full",
        severity: "critical",
        message: `Storage usage ${((metrics.storage.usage / metrics.storage.limit) * 100).toFixed(1)}%`,
        recommendation: "Clean up unnecessary data or upgrade storage plan",
      });
    }

    return alerts;
  }
}

2: Automated Maintenance

typescript
// Automated maintenance system
class AutoMaintenance {
  async scheduleMaintenance() {
    const maintenanceTasks = [
      this.optimizeTables(),
      this.updateStatistics(),
      this.cleanupOldData(),
      this.rebuildIndexes(),
    ];

    const results = await Promise.allSettled(maintenanceTasks);

    const report = {
      completed: results.filter((r) => r.status === "fulfilled").length,
      failed: results.filter((r) => r.status === "rejected").length,
      details: results.map((r, i) => ({
        task: [
          "table_optimization",
          "statistics_update",
          "data_cleanup",
          "index_rebuild",
        ][i],
        status: r.status,
        result: r.status === "fulfilled" ? r.value : r.reason,
      })),
    };

    // Send maintenance report
    await this.sendMaintenanceReport(report);

    return report;
  }

  private async optimizeTables() {
    const tables = await mcp.call("supabase.getTables");

    const optimizationResults = [];

    for (const table of tables) {
      const beforeMetrics = await this.getTableMetrics(table.name);

      // Execute optimization
      await mcp.call("supabase.optimizeTable", {
        tableName: table.name,
      });

      const afterMetrics = await this.getTableMetrics(table.name);

      optimizationResults.push({
        table: table.name,
        sizeReduction: beforeMetrics.size - afterMetrics.size,
        performanceGain: afterMetrics.scanTime - beforeMetrics.scanTime,
      });
    }

    return optimizationResults;
  }

  private async cleanupOldData() {
    // Clean up expired logs
    const deletedLogs = await mcp.call("supabase.deleteOldLogs", {
      olderThan: "90d",
    });

    // Clean up temporary data
    const deletedTempData = await mcp.call("supabase.deleteTempData", {
      olderThan: "7d",
    });

    return {
      deletedLogs,
      deletedTempData,
      totalFreedSpace: deletedLogs.size + deletedTempData.size,
    };
  }
}

CI/CD Integration

1: Database Version Control

typescript
// Database version control
class DatabaseVersionControl {
  async syncWithGit(repoPath: string, targetBranch: string = "main") {
    // Get current database schema
    const currentSchema = await this.getCurrentSchema();

    // Get schema definition from Git
    const gitSchema = await this.getGitSchema(repoPath, targetBranch);

    // Compare differences
    const differences = await this.compareSchemas(currentSchema, gitSchema);

    if (differences.length > 0) {
      // Generate migration script
      const migrationScript = await this.generateMigrationScript(differences);

      // Create PR or apply directly
      if (differences.some((d) => d.breaking)) {
        await this.createSchemaPR(differences, migrationScript);
      } else {
        await this.applySchemaChanges(migrationScript);
      }
    }

    return differences;
  }

  private async getCurrentSchema() {
    return await mcp.call("supabase.getSchema", {
      include: ["tables", "views", "functions", "indexes", "constraints"],
    });
  }

  private async generateMigrationScript(differences: any[]) {
    let script = "-- Auto-generated migration script\n";
    script += "-- Generated at: " + new Date().toISOString() + "\n\n";

    for (const diff of differences) {
      switch (diff.type) {
        case "table_added":
          script += this.generateCreateTableSQL(diff.table);
          break;
        case "column_added":
          script += this.generateAddColumnSQL(diff.column);
          break;
        case "index_added":
          script += this.generateCreateIndexSQL(diff.index);
          break;
        // ... other types
      }
    }

    return script;
  }
}

Best Practices

1: Security Configuration

typescript
// Security configuration management
class SecurityConfigurator {
  async setupSecurity(config: any) {
    // Configure row-level security
    await this.setupRowLevelSecurity(config.tables);

    // Configure user permissions
    await this.setupUserPermissions(config.users);

    // Configure API rate limiting
    await this.setupRateLimiting(config.rateLimit);

    // Configure audit logging
    await this.setupAuditLogging(config.audit);
  }

  private async setupRowLevelSecurity(tables: any[]) {
    for (const table of tables) {
      await mcp.call("supabase.enableRLS", {
        tableName: table.name,
      });

      for (const policy of table.policies) {
        await mcp.call("supabase.createPolicy", {
          tableName: table.name,
          policy: policy,
        });
      }
    }
  }
}

Through Supabase MCP, ByteBuddy can provide powerful database management capabilities, making data workflows more intelligent and automated.