Skip to main content
Glama
README.md
# migratoor

[![npm version](https://img.shields.io/npm/v/@mrzadexinho/migratoor.svg)](https://www.npmjs.com/package/@mrzadexinho/migratoor)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

Database migration safety checker MCP server. Catch dangerous migrations before they hit production.

## Problem

Developers write migrations, deploy, and accidentally lock production tables for minutes. Or drop a column that running code still references. Or create an index that blocks all writes. These cause real outages.

migratoor checks your SQL migrations against 14 proven safety rules from [Squawk](https://squawkhq.com) and [strong_migrations](https://github.com/ankane/strong_migrations), explains why each pattern is dangerous, and suggests the safe alternative — all as MCP tools in your AI editor.

## Quick Start

### As MCP Server

Works with any MCP-compatible client — **Claude Code**, Claude Desktop, Cursor, Windsurf, VS Code (Copilot), Continue.dev, Zed, Cline, and more.

```json
{
  "mcpServers": {
    "migratoor": {
      "command": "npx",
      "args": ["-y", "@mrzadexinho/migratoor"]
    }
  }
}
```

### As Library

```typescript
import { analyzeMigration, formatReport } from '@mrzadexinho/migratoor';

const result = analyzeMigration(`
  ALTER TABLE users ADD COLUMN email TEXT NOT NULL;
  CREATE INDEX idx_email ON users (email);
`);

console.log(formatReport(result));
// [CRITICAL] Adding NOT NULL column without default blocks reads/writes
// [CRITICAL] Creating index without CONCURRENTLY blocks all writes
// Safe alternatives provided for each finding
```

## MCP Tools

| Tool | Description |
|------|-------------|
| `check_migration` | Analyze SQL (file path or inline) for safety issues with risk levels and safe alternatives |
| `list_rules` | List all 14 migration safety rules with severity and description |
| `explain_rule` | Deep dive on a specific rule — why it's dangerous, what lock it takes, safe pattern |

## Rules

### Critical (blocks reads/writes, causes outages)

| Rule | What it catches |
|------|----------------|
| `add-column-not-null-without-default` | Adding NOT NULL column without default rewrites entire table under ACCESS EXCLUSIVE lock |
| `create-index-not-concurrently` | CREATE INDEX blocks all writes during index build |
| `change-column-type` | Changing column type rewrites entire table under ACCESS EXCLUSIVE lock |
| `add-foreign-key-without-validation` | Adding FK validates all rows, blocking writes |
| `set-not-null-on-existing-column` | SET NOT NULL scans all rows under exclusive lock |

### Warning (breaks running code, data risk)

| Rule | What it catches |
|------|----------------|
| `drop-column` | Dropping column breaks cached ORM attributes and running code |
| `drop-table` | Data loss risk, breaks dependent views/functions |
| `rename-column` | Running code referencing old name fails immediately |
| `rename-table` | Same as rename column with wider blast radius |
| `add-unique-constraint` | ACCESS EXCLUSIVE lock during index build |

### Info (best practices)

| Rule | What it catches |
|------|----------------|
| `prefer-text-over-varchar` | VARCHAR(N) has no performance benefit over TEXT in PostgreSQL |
| `prefer-jsonb-over-json` | JSON lacks equality operators and indexing, use JSONB |
| `multiple-ddl-statements` | Multiple DDL in one migration makes rollback harder |
| `data-in-ddl-migration` | Mixing DML with DDL risks inconsistent state on failure |

Every critical and warning finding includes the **safe alternative SQL** — not just "this is wrong" but "here's exactly how to fix it."

## Example Output

```
# Migration Safety Check

**3 statement(s) analyzed, 2 issue(s) found:**
- 2 critical

**This migration is NOT safe to run in production.**

## Critical

[CRITICAL] **Adding NOT NULL column without default blocks reads/writes**
Rule: `add-column-not-null-without-default` | Line: 1
```sql
ALTER TABLE users ADD COLUMN email TEXT NOT NULL
```
**Safe alternative:** Add column as nullable, backfill, then add NOT NULL constraint
```sql
ALTER TABLE users ADD COLUMN email TEXT;
ALTER TABLE users ALTER COLUMN email SET DEFAULT <value>;
UPDATE users SET email = <value> WHERE email IS NULL;
ALTER TABLE users ALTER COLUMN email SET NOT NULL;
```
```

## Architecture

```
migratoor/
  src/
    sql/            # SQL parsing layer
      types         # Statement, ParsedMigration
      parser        # Split SQL, classify DDL operations
    rules/          # Safety rules
      types         # Rule, Finding, Severity
      postgres      # 14 PostgreSQL safety rules
      registry      # Rule lookup, list, filter
    engine/         # Analysis engine
      analyzer      # Apply rules, collect findings, summarize
      reporter      # Format results (structured + markdown)
    mcp/            # MCP server layer
      tools/        # 3 MCP tools
  tests/            # 93 tests mirroring src/ structure
```

## Prerequisites

- **Node.js** >= 20.0.0
- No other dependencies required — migratoor analyzes SQL statically

## Development

```bash
git clone https://github.com/mrzadexinho/migratoor.git
cd migratoor
npm install
npm run build
npm test
```

## License

MIT

Maintenance

ActivityInactive
ResponsivenessNo issues