Declarative, SQL-first schema management: plain SQL in, a reviewable plan out.
Schemorph is a CLI tool and library for managing database schemas as plain SQL files. You write the desired state of your database — tables, views, procedures — as ordinary CREATE statements. Schemorph computes what needs to change, shows you the plan, and applies exactly that plan.
No ORM. No Visual Studio. No proprietary DSL. Just SQL, a single command, and output that holds up equally in a code review and in a pipeline.
# Inspect a live database into SQL files
schemorph inspect --url "Server=...;Database=app" --out ./schema
# Preview what would change (never applies anything) — prints the plan and its fingerprint
schemorph diff --url "..." --schema ./schema
# Apply exactly the reviewed plan, or refuse if anything changed since the diff
schemorph apply --url "..." --schema ./schema --expect-plan <hash>
# Drift, ledger summary, pending migrations
schemorph status --url "..." --schema ./schema --migrations ./migrations
# Run as an MCP server (read-only tools + gated apply)
schemorph mcpStatus: early (0.x), under active development. Released on NuGet and GitHub releases. Two engines ship — SQL Server, and PostgreSQL up to a declared scope (Database Support). Working today against SQL Server: the full loop (inspect / diff / apply / status) with destructive-change gating and semantic exit codes; programmable-object routing via CREATE OR ALTER; checksummed run-once migrations; a history ledger; brownfield adoption (existing databases and SSDT trees are consumed as-is — matching objects reconcile instead of re-applying); a versioned machine-readable plan format with per-change SQL and explanations, an apply gate (--expect-plan) and safety-lint warnings; and an MCP server (schemorph mcp) with schema/plan resources. The documents in docs/ define the project's anchors — see Design Principles for what is fixed and what is open.
On SQL Server, one combination of properties does not exist today as free, offline, login-free tooling: a declarative diff engine that treats procedures and functions as first-class, plus a versioned data-migration ledger, plus a plan you can review and then apply exactly. Every existing tool gives you some of these; none gives you all of them:
| Declarative diff | Procedures / functions as first-class | Versioned data migrations | CLI-first, no login | Reviewable plan contract + gated apply | |
|---|---|---|---|---|---|
| SSDT / SqlPackage | ✅ | ✅ | |||
| EF Migrations | ❌ (code-first) | ❌ | ✅ | migrations script output only |
|
| Flyway / Liquibase | ✅ | ✅ | |||
| Atlas | ✅ | ✅ | ❌ SQL Server driver is paid (Pro) and requires login | ||
| Bytebase | ❌ | ✅ | |||
| sqldef | ✅ | ❌ | ✅ | --dry-run DDL only |
|
| Schemorph | ✅ | ✅ | ✅ | ✅ | ✅ |
Competitive claims as of 2026-07; re-verified periodically as these tools evolve.
- Atlas gates its SQL Server driver behind a paid plan; Bytebase's declarative workflow is PostgreSQL-only; Flyway's schema model requires commercial editions; sqldef cannot manage procedures or functions; SSDT is bound to the Visual Studio project system.
- The last column is a narrow, checkable bar, not a slogan: the plan is a versioned machine contract (plan-format.md) that also renders as a review document, and
applyexecutes only a reviewed plan fingerprint or refuses. Printing a dry-run script is common; binding the apply to the identity of the reviewed plan is rare — Atlas does it (plan files with a from-state check), behind a login and a paid plan, on a SQL Server driver that is itself Pro-gated.
Schemorph is that combination: the declarative diff model (SSDT, Atlas, sqldef) and the versioned migration ledger (Flyway, Liquibase) in one coherent tool, where every change passes through a plan that a person can read and a program can parse — the same plan, not two renderings that can drift apart.
Schemorph treats different kinds of database objects with the strategy that actually fits them, instead of forcing everything through one mechanism:
schema/ ← declarative: desired state, diffed automatically
tables/
views/
procedures/ ← idempotent: CREATE OR ALTER, re-applied on change
functions/
migrations/ ← versioned: ordered, checksummed, run once
V0001__seed_reference_data.sql
V0002__backfill_legacy_codes.sql
- Structural objects (tables, indexes, constraints) are diffed: Schemorph compares your SQL files against the live database and generates the
CREATE/ALTER/DROPplan. - Programmable objects (procedures, functions, views, triggers) are re-applied idempotently via
CREATE OR ALTER. Text-based diffing of procedure bodies is unreliable by nature; re-definition is not. This sidesteps an entire class of bugs that diff-only tools struggle with. - Data changes (seeds, backfills, one-off transformations) are versioned migrations tracked in a checksummed history ledger, Flyway-style. State diffing cannot express these; a ledger can.
See Architecture for the full model.
One change goes through the same four steps whether you type them or a pipeline runs them:
- Edit the
.sqlfiles.git diffalready tells the story. schemorph diff— always a dry run, never touches the database. It prints what would change and the plan's fingerprint (planHash).schemorph diff --format sql— the whole plan as one review document, in execution order, with theplanHashin its header. This is the artifact to read, paste into a pull request, and sign off on.schemorph apply --expect-plan <hash>— executes exactly that plan, or refuses withplan_mismatchif anything drifted since the review.
The DDL a person approves is the DDL that runs, and nothing else — that is the whole point of the fingerprint (human approval gate). Destructive operations (DROP of anything holding data) stay out of plans entirely unless you enable them, and are marked prominently when you do. Every apply is recorded in a history ledger inside the database: what ran, when, its checksum, its outcome.
There is a ready-made GitHub Actions recipe that posts the review document as a comment on every schema pull request.
The same commands speak a stable machine contract, so scripts, CI jobs, and AI coding agents drive the loop above without screen-scraping output meant for a terminal:
- Versioned plan format (docs/plan-format.md) — plans as machine contracts, with
--format jsonon every command; JSON is the default whenever stdout is redirected (pipelines get JSON, terminals get text) - Exit codes distinguish "no changes", "changes pending", and "error", and failures carry a typed
{kind, code, message, hint}envelope — see Errors and exit codes schemorph schemaprints a JSON manifest of the whole CLI surface (verbs, options, exit codes), so a caller discovers it without parsing help text- Credentials come from the
SCHEMORPH_URLenvironment variable (preferred over--url), and password material is redacted from every output channel — safe to pipe into logs and PR comments schemorph mcpruns the same operations as an MCP server over stdio — read-only tools (schemorph_diff,schemorph_inspect,schemorph_status) plusschemorph_applygated behind a required plan fingerprint; the connection string stays in the server's environment, never in the conversation- Schema as context — the MCP server also exposes resources:
schemorph://schema(the live database as desired-state SQL, or one object viaschemorph://schema/{kind}/{name}) andschemorph://plan(the current plan with itsplanHash, ready for the apply gate; setSCHEMORPH_SCHEMA_DIRin the server environment), so hosts attach schema state directly instead of round-tripping tool calls - Agent Skill —
skills/schemorph/SKILL.mdpackages the procedural knowledge (the edit → diff → review → gated apply → converge loop, error-contract branching, migration immutability) in the Agent Skills format; drop it into your agent's skills directory
Nothing here is a separate mode: the JSON, the review document, and the MCP tools are renderings of one plan, so an automated run and a human review never disagree about what will happen.
Schemorph is multi-target by design: one command set, one plan format, one ledger model and one set of safety rules across engines — only the dialect underneath changes (provider boundary). The boundary exists so further engines can follow.
- SQL Server — initial target. The diff engine builds on DacFx (Microsoft's schema comparison framework, the same engine behind SSDT), giving full-fidelity handling of T-SQL objects from day one.
- PostgreSQL — released, up to a declared scope: the table core. Set
SCHEMORPH_PROVIDER=postgresand Schemorph inspects, diffs and applies tables, columns, constraints, indexes and the target schema, with the declarative apply running as one transaction — applied entirely or not at all. Comparison is nativepg_catalogwith shadow normalization (ADR-0007); a database-owner role withCREATE SCHEMAsuffices — no superuser, noCREATEDB, no extensions. Everything outside the declared capability list (views/functions/triggers/procedures, migrations, and concurrent index builds) is refused with an explicit error rather than half-planned (ADR-0003); the scope grows in releasable slices with no committed timeline. If you need the refused parts on PostgreSQL today, Atlas, sqldef, or Flyway will serve you better — that is a more useful answer than a date we cannot keep.
dotnet tool install -g SchemorphStandalone self-contained binaries (win-x64, linux-x64, osx-arm64) are attached to each GitHub release — no .NET runtime required.
- Design Principles — the project's anchors; read this first
- Architecture — the three-strategy model, ledger, provider boundary
- The plan format — the machine-readable plan contract and its versioning
- Errors and exit codes — the typed error envelope callers branch on, and the safety-lint warning band
- When an apply fails — what the database looks like after a partial apply, how to recover, and how to read the ledger
- Limitations — what does not converge or is out of scope, and what to do instead
- Recipes — ready-made integrations: plan-on-PR comment, human approval gate
- Architecture Decision Records — why the foundational choices were made
- Changelog — what changed per release, and what is unreleased
See CONTRIBUTING.md. The project follows ADR-driven governance: significant design decisions are proposed and recorded as ADRs before implementation.
MIT. The core tool is and will remain free and fully functional offline — no accounts, no feature gates, no telemetry required for any local operation.