MCP Server

DBLift ships an MCP (Model Context Protocol) server so an AI coding agent — Claude Code, Cursor, GitHub Copilot, Windsurf — can call DBLift's existing checks directly, in-process, instead of shelling out to the CLI or guessing at migration safety. Every tool returns the same JSON as the equivalent --format json command output. There is no LLM in the apply path: tools are deterministic checks, gated by the same licence tiers as the CLI commands they wrap. Destructive or write commands (clean, undo, a real migrate apply, data apply/undo) are never exposed as tools.

DBLift ships an MCP (Model Context Protocol) server so an AI coding agent — Claude Code, Cursor, GitHub Copilot, Windsurf — can call DBLift's existing checks directly, in-process, instead of shelling out to the CLI or guessing at migration safety. Every tool returns the same JSON as the equivalent --format json command output. There is no LLM in the apply path: tools are deterministic checks, gated by the same licence tiers as the CLI commands they wrap. Destructive or write commands (clean, undo, a real migrate apply, data apply/undo) are never exposed as tools.

Built-in tools are read-only. Clients should trust each tool's own read-only hint — add-on packages registered through dblift.mcp_tools may contribute tools that write.

For the day-to-day sequence — setup, reviewing a pending migration, and the Pro and Enterprise 4.5.0 authoring loop — see Use the MCP server.

validate checks the scripts on disk for consistency (duplicate versions, unsupported formats) and, once migrations have been applied, compares them with recorded history — checksums. Pass strict: true to also fail when a previously applied migration is now missing from disk and to require strict version order. The default is false, matching the CLI --strict flag, so a default validate does not report missing files. The missing-file check compares successful applied history rows with files on disk, so it needs a schema-history table that already has applied rows. With no applied rows there is nothing missing to report. validate does not parse or lint SQL. Offline SQL policy checks are the Pro validate_sql tool.

Install

pip install "dblift[mcp]"

The mcp extra installs the MCP SDK. A bare pip install dblift does not. dblift mcp shipped in OSS 4.1.0; this page documents PyPI 4.9.0.

With the default text log format, dblift mcp writes one log file per server session: the first call's file is reused and appended to for later calls in that process. HTML and JSON log formats do not share a file across calls.

Configure your agent

Add DBLift as an MCP server. Most clients read a JSON config with a mcpServers block.

.mcp.json (Claude Code) or the equivalent config file for your client:

{
  "mcpServers": {
    "dblift": {
      "command": "dblift",
      "args": ["--env", "agent", "mcp"]
    }
  }
}

--env goes before mcp and applies to every tool call. No tool argument can change it. Root flags work the same way: "args": ["--config", "config/dblift.yaml", "--env", "agent", "mcp"].

ClientConfig file
Claude Code.mcp.json in the project root, or claude mcp add dblift -- dblift mcp
Cursor.cursor/mcp.json
GitHub Copilot (VS Code).vscode/mcp.json
Windsurf~/.codeium/windsurf/mcp_config.json

Tool argument names must match tools/list. An unknown name is an error before the command runs. Use target_version, not target, with migrate_dry_run. Omitted optional arguments keep their defaults.

Startup line

At start, after the server is built, dblift mcp prints one line on stderr — never on the stdio channel the agent reads — naming the environment it resolved and a masked database target. Passwords, keys, and tokens are not in that line. Check it before letting an agent call anything, especially when you meant to pin a read-only environment.

dblift mcp: environment agent; database postgresql dblift_reader@localhost:5432/app
dblift mcp: environment none; database sqlite /path/to/app.sqlite

A credentialed URL is masked (postgresql://u:***@h/db). A missing or unreadable configuration file, an unknown environment, an invalid database field, or a secret that cannot be resolved does not stop the start: the line says configuration not loaded at start and the server still starts. Every tool call loads the configuration itself and reports its own error.

No connection is opened at start. The configuration file is read once, including under --offline, and secrets it references are resolved once at that moment, the same way the first tool call would resolve them.

Tools by edition

Tool availability follows the same licence tiers as the CLI — see Editions and Tiers. Pro and Enterprise tools are contributed by the commercial build through dblift.mcp_tools registrars.

EditionToolsResources
OSS Coreinfo · validate · migrate_dry_run (forces --dry-run)dblift://history · dblift://pending
Prodiff · validate_sql · export_schema · data_plan · data_status · diff_to_sql—
Enterpriseplan · preflight · snapshot · diff_impact · edit_model · validate_modeldblift://model-schema · dblift://policy

dblift://pending lists migrations not yet applied, as a JSON array — the same rows migrate_dry_run returns.

diff_to_sql — writes the SQL that reconciles live schema drift to a path you name (and its undo script next to it unless no_undo); overwrites those files; does not apply anything.

  • OSS observes — history, validate, dry-run preview.
  • Pro derives from the live database — diff, export, data corrections, SQL generation (diff_to_sql).
  • Enterprise authors from a model and validates it — offline plan, plan-grade preflight (--skip-replay), snapshot, impact, edit_model, and validate_model. The sequence is on Use the MCP server.

No tier writes the database through MCP; apply / undo / clean / data apply / data undo stay CLI-only. The first info or validate call creates the schema-history table when the role is allowed to. migrate_dry_run never creates it.

A tool from a tier your licence doesn't cover behaves like the equivalent CLI command on the open-source build: it reports what it does and exits without running, rather than failing silently.

validate argument strict

ArgumentDefaultWhat it does
strictfalseAppends the CLI --strict flag. A previously applied migration that is now missing from disk fails validation, and version order must be strict.

Checksum drift and duplicate versions are reported without strict. Missing files are not. The missing-file check needs a history table with applied rows. If the role cannot create the table — a reader on an empty schema — validate is an MCP error (ConnectionError), not a verdict that lists missing files.

migrate_dry_run argument show_sql

ArgumentDefaultWhat it does
show_sqlfalseAppends --show-sql. The tool still forces --dry-run and applies nothing.

When show_sql is true, the result adds show_sql: true and a sql array, one entry per pending script:

{
  "show_sql": true,
  "sql": [
    {
      "script": "V1_0_1__add_note.sql",
      "version": "1.0.1",
      "description": "add note",
      "statements": ["ALTER TABLE users ADD note TEXT"]
    }
  ]
}

Without show_sql, the result has no sql key. The same sql key is what dblift migrate --show-sql --format json adds, dry run or not.

Placeholders in that SQL are resolved, so a placeholder value that is a secret appears in the output. A placeholder that has no value stays visible as ${VAR}. Read the statements before proposing a change.

show_sql needs a live connection. dblift mcp --offline refuses migrate_dry_run (and the other built-in tools) when the tool is called. It does not return SQL from the files alone.

A dry run skips creating the schema-history table. On a database that has no history table yet, and that the role can still connect to, migrate_dry_run reports every script as pending and creates nothing. On engines where a schema is a whole database — MySQL and MariaDB — a dry run against a database that does not exist yet fails at connection time, before that skip.

Errors and verdicts

A command that fails before producing a result — a refused connection, a history table that could not be created, an exception inside the command — is an MCP error result (isError: true) carrying the CLI message, for example ConnectionError: Could not create the schema-history table: .... Reading dblift://history or dblift://pending fails the same way. They do not return [].

A command that ran to a verdict is a normal result, even when the verdict failed. Checksum issues from validate stay isError: false with success: false and the issues list. An agent that only checks isError will treat that as success. Check success.

validate --format json sets error to null on success, the same contract as info and migrate. It is not "".

A role that can connect but lacks a privilege is reported with the database's own message (permission denied for schema … on PostgreSQL, for example), not as Connection failed: invalid credentials.

Tool errors never stop the server.

What protects the database

None of the built-in tools can apply, undo, or clean a migration. That is not what protects a database from an agent. The agent has a shell next to this server, and both run with the same dblift.yaml, environment variables, and secrets. What protects the database is the role the server connects with and the environment it is pointed at.

The flags in the next section only shape what an agent sees and asks this server for. --read-only and --mode review trust each tool's own read_only declaration. --offline trusts its connects declaration. --tools and --resources are exact-name allowlists. All of them live in a file the agent can edit.

Give the agent a role that can only read. Once the schema-history table exists, info, validate, and migrate_dry_run need USAGE on the schema and SELECT on its tables, nothing else. On a database that has no history table yet, the reader has no CREATE, so info and validate fail instead of creating it, and migrate_dry_run neither fails nor creates it. Create the table with the role that applies migrations first (dblift migrate or dblift baseline from the CLI), then hand the reader to the agent.

Grant that role only the one schema the server is pointed at. The root --db-schema flag can retarget any schema the role can read, and no restriction flag fences it. The role is the boundary.

On PostgreSQL:

CREATE ROLE dblift_reader LOGIN PASSWORD '...';
GRANT USAGE ON SCHEMA public TO dblift_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO dblift_reader;

Point the server at its own environment and pin it. Declare the reader's credentials as an environment of their own and name it in .mcp.json, so the agent never runs under the block your deploy pipeline uses. An environment is deep-merged over the root sections (see Environments), so only the credential differs:

database:
  type: postgresql
  host: localhost
  port: 5432
  database: app
  username: dblift_app
  password: "${DBLIFT_APP_PASSWORD}"

environments:
  agent:
    database:
      username: dblift_reader
      password: "${DBLIFT_READER_PASSWORD}"

Keep the migrator credential out of the agent's process. The credential that can CREATE, DROP, or TRUNCATE belongs to the pipeline that runs dblift migrate, not to a shell with a coding agent in it.

Restricting a session

These flags narrow what an agent can ask this server for. They are not what protects the database. The section above is.

dblift mcp --read-only skips every tool whose registrar declared read_only=False. It trusts declarations: it catches an honest add-on's writing tool, not a dishonest one. Built-in tools are all read-only and are unaffected.

dblift mcp --tools NAME[,NAME...] is the allowlist: only the named tools are served, built-in or add-on, and everything else is skipped. An unknown name makes the server refuse to start, so a typo cannot silently shrink the tool list, and so does a list with no usable name in it (--tools "") rather than serving everything.

{
  "mcpServers": {
    "dblift": {
      "command": "dblift",
      "args": ["--env", "agent", "mcp", "--tools", "info,validate,migrate_dry_run"]
    }
  }
}

dblift mcp --mode review serves a review session. It withholds the same tools --read-only does — every tool whose registrar declared read_only=False — and additionally sets server instructions that the session is for reading and reporting, not for producing files. Use --read-only when you are fencing a job's capabilities and --mode review when you are telling an agent what it is there for. The default, --mode author, restricts nothing.

dblift mcp --resources NAME[,NAME...] is the allowlist for resources, the way --tools is the one for tools. --resources history serves dblift://history and withholds dblift://pending; either spelling works (history or dblift://history), and an unknown name — or an empty list — makes the server refuse to start, as with --tools. --tools fences tools only; it does not withhold resources. An allowlist written before this flag existed still serves both built-in resources.

dblift mcp --offline refuses every tool and resource that would open a database connection. The refusal happens when the tool is called, not when the server starts: the tool stays listed and the call returns an error naming --offline. The server can start with no database configured — the configuration is read at start only to print the resolved target; no connection is opened until a tool is called. Start-up prints, on stderr, which registrations will refuse.

All three built-in tools and both built-in resources read the schema-history table, so on a bare OSS install with no add-on packages an offline server refuses everything. --offline does not make info, validate, or migrate_dry_run work without a database, and it does not make show_sql return SQL. The flag is for installs whose add-on tools run from the project's files.

The flags compose: --read-only --tools export_schema admits the name and still skips the tool if it declares read_only=False; --offline --tools info serves info and refuses every call to it; --tools and --resources fence their own lists side by side, and a name unknown to either refuses the start. Skipped tools and resources are listed on stderr when the server starts; the server still serves what is left.

Installed add-on packages can contribute further tools through dblift.mcp_tools, and may also contribute resources. --resources and --offline fence add-on resources the same way they fence the built-in ones. A writing tool registers with read_only=False; --read-only skips it. Paid writers skipped by --read-only include export_schema, diff_to_sql, data_plan, snapshot, and edit_model — the same class as other file-writers. A tool that overwrites a caller-named path also passes destructive=True, which sets destructive_hint. A client that auto-approves non-destructive tools should prompt for it. Each tool declares its own read-only hint; clients should trust that per-tool hint.

What an agent can ask

With the server configured, an agent can, for example:

  • Check migration status and pending migrations (info)
  • Read pending migrations (dblift://pending) — the same rows migrate_dry_run returns; this does not apply them
  • Check history and checksums (validate). Pass strict: true for missing applied files and strict version order — this is not SQL lint, and it is not an apply
  • Preview pending SQL without applying it (migrate_dry_run with show_sql: true)
  • Diff live schema against migrations, or lint a SQL script against your rule profile with validate_sql (Pro)
  • Write sync SQL (and undo) to a path you name with diff_to_sql (Pro) — overwrites those files; does not apply anything
  • Run a full preflight check or generate an evidence snapshot before a release (Enterprise)

None of this replaces review — it gives the agent the same ground truth a human reviewer would use, instead of it inferring migration safety from reading SQL files. Applying, undoing, and cleaning stay on the CLI.

See Use the MCP server for the review and authoring sequences, Commands for the server flags, and dblift validate for what the underlying command checks.

DBLift is information technology / developer tools software. Contact: contact@dblift.com.