safe-migrations-mcp
Supports inspecting Drizzle schema files and proposing edits with diff and risk analysis.
Allows parsing, inspecting, and editing .env configuration files with diff preview and risk flagging.
Provides tools for inspecting MySQL databases and proposing migrations (DML only; DDL apply is intentionally blocked).
Supports inspecting Prisma schema files and proposing edits with diff and risk analysis.
Provides tools for inspecting and migrating SQLite databases, including schema inspection, migration proposal, simulation, and application with rollback.
Allows parsing, inspecting, and editing TOML configuration files with diff preview and risk flagging.
Allows parsing, inspecting, and editing YAML configuration files with diff preview and risk flagging.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@safe-migrations-mcp Add a last_login timestamp column to users in demo.db"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Safe Migrations MCP
Your AI coding agent can silently destroy your production database.
A misplaced DROP COLUMN, a missing WHERE, a one-character .env typo — Claude Code, Cursor, OpenClaw, and every other coding agent will cheerfully execute the change and report success while your data quietly vanishes.
This was born from watching an OpenClaw agent break its own config file trying to make a "small" change. The fix turned out to be universal: any agent that can edit anything should have to slow down, show its work, and ask first.
Safe Migrations MCP is the gate they have to pass through first. Every proposed schema change or config edit is diffed, risk-flagged, snapshotted, and requires a one-time confirmation token from a fresh simulate_impact call before a single byte is written.
Works with any MCP-capable agent. Local-first. Zero cloud dependency.
How it works
The mandatory checkpoint between the agent and your disk:
Propose — agent sends an intent (natural language, raw SQL, or a new config file); server returns a
proposal_idplus a redacted preview and SHA-256 hash of the SQL/edit and its rollback. Full payload is stored server-side, never echoed back.Simulate — dry-run inside a rolled-back transaction; count affected rows; surface every DROP, TRUNCATE, NOT-NULL-without-default, secret-key removal, etc. On success, returns a one-time
confirmation_tokenbound to the proposal's fingerprint.Apply — only runs with that fresh
confirmation_token. Snapshots the file or DB first. Logs everything to an append-only audit trail.
~2k LOC of Python, hardened against the usual footguns (symlink writes, silent SQLite creation, MySQL DDL auto-commit, token replay, secret leakage in diffs).
Related MCP server: impact-preview
Install & run
pip install safe-migrations-mcp # or: pipx install safe-migrations-mcp
safe-migrations-mcp # speaks MCP over stdioOr from a local clone:
git clone https://github.com/possibly6/safe-migrations-mcp
cd safe-migrations-mcp
pip install -e '.[all]'
safe-migrations-mcpOptional extras (Postgres / MySQL drivers):
pip install 'safe-migrations-mcp[postgres]'— addspsycopgpip install 'safe-migrations-mcp[mysql]'— addsPyMySQLpip install 'safe-migrations-mcp[all]'— both
SQLite, YAML, JSON, .env, and Prisma/Drizzle schema-file parsing work with
zero extra deps.
Wire it into your agent
Claude Code / Claude Desktop
~/.claude/claude_desktop_config.json (or ~/.config/Claude/claude_desktop_config.json on Linux):
{
"mcpServers": {
"safe-migrations": {
"command": "safe-migrations-mcp"
}
}
}Cursor
~/.cursor/mcp.json:
{
"mcpServers": {
"safe-migrations": { "command": "safe-migrations-mcp" }
}
}OpenClaw / any MCP-capable agent
Point the agent at the safe-migrations-mcp binary on stdio. The tool names
match the list below.
The 8 tools
Tool | Purpose |
| Current DB/ORM schema. Postgres, MySQL, SQLite, plus |
| Parse YAML / JSON / |
| Create a proposal from natural language, raw SQL, or a new config file. Returns |
| Dry-run the proposal. DB: rolled-back transaction + affected-row count + SQL/rollback previews. Config: unified diff + key delta. Always returns risk flags. |
| Exact undo SQL (or the config-snapshot restore path). |
| Requires the fresh |
| Append-only audit log of every applied change. |
| Prune old proposal metadata, secret payloads, and snapshots from local state. |
Example agent conversation (the confirmation flow)
You: "Add a
last_logintimestamp column tousersin./demo.db."
Agent (silently):
propose_migration_or_edit(kind="db", connection="sqlite:///./demo.db", request="add column last_login of type TEXT to users")→
proposal_id = p_9a1b2c3d4e,sql_preview = ALTER TABLE "users" ADD COLUMN "last_login" TEXT;,rollback_preview = ALTER TABLE "users" DROP COLUMN "last_login";
Agent (silently):
simulate_impact("p_9a1b2c3d4e")→
risk_level = "low",dry_run.ok = true,affected_rows = 0
Agent (to you):
I'd like to run:
ALTER TABLE "users" ADD COLUMN "last_login" TEXT;Risk: low. Rollback available. Confirm?
You: "yes, confirm"
Agent (silently): copies the
confirmation_tokenfromsimulate_impact(...)and callsapply_change("p_9a1b2c3d4e", "<that token>")→ done, audit logged.
And the dangerous case:
You: "drop the orders table."
Agent (silently):
propose_migration_or_edit(kind="db", ..., sql="DROP TABLE orders;")→simulate_impact(...)returnsrisk_level: "critical", flag: "Drops an entire table (all data lost)".
Agent (to you):
🛑 This is a critical destructive change on 3 rows. Rollback can recreate the table but row data cannot be auto-restored without a backup. Are you sure? Reply with the exact
confirmation_tokenfrom the simulation step to proceed.
The agent physically cannot execute until that token is passed through — the server enforces it, not the model.
Example prompts you can paste
Claude / Cursor / OpenClaw:
Use the safe-migrations MCP. Inspect sqlite:///./examples/demo.db.
Then propose adding a `phone` TEXT column to users (nullable).
Show me the proposal + simulated risk. Wait for my approval before applying with the confirmation token from simulate_impact.Use safe-migrations to edit ./examples/config.yaml: set app.log_level to "debug".
Show the diff and risk first. Only apply after I confirm.Use safe-migrations to audit recent changes — call get_change_history and
summarize the last 10 entries.Try the demo locally
git clone https://github.com/possibly6/safe-migrations-mcp
cd safe-migrations-mcp
pip install -e .
python examples/seed_demo.py # creates examples/demo.db
safe-migrations-mcp # start the serverThen, from your MCP-connected agent:
inspect_schema("sqlite:///./examples/demo.db")inspect_config("./examples/config.yaml")propose_migration_or_edit(kind="db", connection="sqlite:///./examples/demo.db", request="create index on orders(status)")simulate_impact(...)→apply_change(..., "<confirmation_token>")
What gets flagged
SQL risk rules (severity):
DROP TABLE/DROP DATABASE/DROP SCHEMA— criticalDROP COLUMN,TRUNCATE,DELETE/UPDATEwithoutWHERE— highALTER COLUMN … TYPE,RENAME TO,ADD COLUMN NOT NULLwithoutDEFAULT,GRANT/REVOKE— medium
Config risk rules:
Removal of keys matching
database|db|auth|secret|token|production|prod|migration— highAdding or changing values on keys matching
password|secret|api_key|token|private_key|database_url|dsn|credentials— mediumAny removed key — at least medium
Anything ≥ medium sets confirmation_required: true.
High-risk config changes are blocked from direct apply so the agent has to reduce scope or hand the edit back for manual review.
State & audit
All local, all visible:
~/.safe-migrations-mcp/
├── proposals/ # redacted proposal metadata as JSON
├── secrets/ # private proposal payloads (0600), pruned with cleanup_state
├── snapshots/ # pre-change backups
└── audit.jsonl # append-only log of applied changes (redacted)Override with SAFE_MIGRATIONS_HOME=/path.
When to use this
✅ Use Safe Migrations MCP when:
You let an AI agent touch your database or config files
You want every schema change diffed and confirmed before it runs
You want an audit trail of every change an agent has ever made
You want rollback SQL generated automatically
You're tired of agents silently dropping columns or rewriting
.envfiles
❌ Use a real migration framework (Alembic / Prisma Migrate / Flyway) when:
You need versioned, repeatable migrations checked into source control
You're running zero-downtime migrations on a production Postgres
You need MySQL DDL apply support (this server intentionally blocks it — see FAQ)
You want online schema changes / backfills / dual-writes
This is a gatekeeper, not a migration framework. They're complementary — author your migrations with Alembic, let agents propose runtime tweaks through this.
In scope: SQLite, Postgres, MySQL inspection / DML; YAML, JSON, .env, Prisma, Drizzle (best-effort). Natural-language intents for common DDL (add/drop/rename column, create index, drop table). Raw SQL pass-through with automatic best-effort rollback.
Out of scope (on purpose): MySQL DDL apply, full SQL dialect coverage, online schema migrations, arbitrary DSL translation. Safety and clarity beat breadth here — if a proposal is outside what we can analyze, we say so instead of guessing.
FAQ
Q: How is this different from Alembic, Prisma Migrate, or Flyway? A: Those are migration frameworks — they track, version, and apply schema changes you author by hand. This is a gatekeeper — it sits between an AI agent and your DB/configs and forces every change through diff → simulate → confirm → apply. Use both.
Q: Can the agent bypass the confirmation token?
A: Not from inside this server. The token is generated server-side, bound to a SHA-256 fingerprint of the proposal, and expires in 15 minutes. Editing the proposal invalidates the token. The agent must call simulate_impact to get a fresh one — and the client UI (Claude Code's prompt, Cursor's confirm dialog) is what makes a human actually read the proposal before that token gets passed back.
Q: Does this work in CI? A: It's the wrong tool for CI. The whole point is a human checkpoint. In CI, use your normal migration framework with version-controlled SQL. Reach for this server when an agent (Claude Code, Cursor, OpenClaw) is the one proposing the edit.
Q: What happens if the apply fails halfway through?
A: SQLite changes run inside a transaction and roll back on error. Postgres applies via psycopg with explicit commit/rollback. Failed applies are marked apply_failed, not applied, and audited as a separate event so you can find them later.
Q: Why is MySQL DDL apply intentionally blocked? A: MySQL auto-commits DDL — there's no real "dry-run inside a transaction" or rollback. This server refuses to pretend it has a safety net it doesn't have. MySQL DML (INSERT/UPDATE/DELETE) and inspection still work fine.
Q: Does it know about my Postgres triggers, views, or functions? A: Not in v0.1 — inspection covers tables and columns. Triggers/views/functions are out of scope for now.
Q: My agent has a raw write_file tool too. Can't it just bypass the MCP?
A: Yes — the safety is opt-in at the agent's tool level. If you wire safe-migrations in and leave a raw filesystem write tool enabled for the same paths, the agent can route around the gate. The intended pattern is: route DB and config edits through this server, and keep raw filesystem writes scoped to other paths.
Development
pip install -e '.[all]'
pip install pytest
pytest -qLicense
MIT.
Available Tools
8 toolsapply_changeA
Apply a proposal. Requires the one-time confirmation token from simulate_impact.
Before writing, snapshots the affected file (configs) or the SQLite file, then executes inside a transaction where possible. Logs an audit entry.
Inputs: proposal_id (required), confirmation (required). Returns: object
cleanup_stateB
Prune old proposal metadata, secret payloads, and snapshots from local state.
Inputs: max_age_days. Returns: object
generate_rollbackA
Produce exact undo SQL (for DB proposals) or a restore plan (for config).
Either pass an existing proposal_id, or pass raw sql (+ optional connection
for better schema-aware rollbacks).
Inputs: proposal_id, sql, connection. Returns: object
get_change_historyC
Return the audit log of applied changes (most recent last).
Inputs: limit. Returns: object
inspect_configC
Parse and summarize a config file (YAML/JSON/.env/Prisma/TOML/text).
Inputs: path (required). Returns: object
inspect_schemaA
Inspect a database or ORM schema file.
connection examples:
sqlite:///path/to/app.db OR ./app.db
postgresql://user:pw@host/db
mysql://user:pw@host/db
./prisma/schema.prisma
./drizzle/schema.ts
Results are cached locally for 60s unless refresh=True.
Inputs: connection (required), refresh. Returns: object
propose_migration_or_editA
Propose a DB migration OR a config edit. Returns a proposal_id you pass to apply_change.
Args:
kind: 'db' or 'config'
connection: (db) connection string or schema-file path
request: (db) natural-language intent, e.g. "add column email of type TEXT not null default '' to users"
sql: (db) raw SQL — takes priority over request
path: (config) path to the file being edited
new_content: (config) full new file contents
Nothing is written. Call simulate_impact(proposal_id) next, then apply_change with the one-time confirmation_token returned by the simulation step.
Inputs: kind (required), connection, request, sql, +2 more. Returns: object
simulate_impactA
Dry-run a proposal. DB proposals execute inside a rolled-back transaction to surface errors and count affected rows. Config proposals show full diff + key delta. All results include risk flags and whether explicit confirmation is required.
Inputs: proposal_id (required). Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
Each tool has a clearly distinct purpose: proposing, simulating, applying, rolling back, inspecting config/schema, getting history, and cleanup. No overlap in functionality.
All tools follow a consistent verb_noun naming pattern (e.g., apply_change, inspect_schema). Verbs are descriptive and standardized.
8 tools is well-scoped for a migration server. It covers the core workflow without being excessive or insufficient.
The tool surface covers the full migration lifecycle: propose, simulate, apply, rollback, history, inspection, and cleanup. No obvious gaps for its intended domain.
Maintenance
Related MCP Connectors
The MCP server that vets MCP servers: identity, risk grade and per-tool risk before you install.
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
Independent trust scores, tool surfaces and change history for MCP servers.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA governed MCP server that enforces a trust layer between AI agents and databases, requiring sign-off on joins and metrics and producing auditable receipts for every query.-
- AlicenseNot gradedqualityDmaintenanceMCP server that provides impact preview and approval workflow for AI agent actions, allowing users to see diffs and risk assessments before any changes are executed.1MIT
- FlicenseNot gradedqualityCmaintenanceThis MCP server provides secure access to databases for AI agents, enforcing authentication, authorization, human approval, logging, and notifications to prevent dangerous actions.-
- AlicenseNot gradedqualityBmaintenanceAn MCP server exposing scoped, read-only enterprise operations tools with fail-closed credential handling. It returns opaque approval IDs for mutations and requires a separate operator approval command to release one-time capabilities.MIT