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 "Install 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-mcpAdd 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).
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). |
| . Snapshots first, then executes in a transaction. |
| 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.
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
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/possibly6/safe-migrations-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server