Skip to content

With AI Assistants (MCP) ​

pgembed mcp runs an MCP server over standard input and output. It gives an assistant a real Postgres of any version that it can start, query, explain and inspect, with the same safety rules as the command line.

Set it up ​

npm package

The npm package needs a fix and will be available again on 10 November 2026. Until then, use the dnx setup below (.NET 10).

Claude Code:

bash
claude mcp add pgembed -- npx -y pgembed mcp
snippet source | anchor

With .NET 10, dnx runs it from NuGet instead of npm:

bash
claude mcp add pgembed -- dnx pgembed mcp
snippet source | anchor

Claude Desktop, Cursor and other clients that read an mcpServers file:

json
{
  "mcpServers": {
    "pgembed": {
      "command": "npx",
      "args": ["-y", "pgembed", "mcp"]
    }
  }
}
snippet source | anchor

VS Code (.vscode/mcp.json):

json
{
  "servers": {
    "pgembed": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "pgembed", "mcp"]
    }
  }
}
snippet source | anchor

npx -y runs it without installing; so does "command": "dnx" with "args": ["pgembed", "mcp"] on .NET 10. With pgembed installed globally (npm or .NET tool), use "command": "pgembed" and "args": ["mcp"].

What the assistant can do ​

Tools
list_versions, install_version, list_extensionsVersions, ahead-of-time installs with progress, extensions
start_instance, list_instances, stop_instance, restart_instanceInstances; started ones keep running after the assistant exits
kill_instance, remove_instanceForce-kill, or delete with its data; both ask first
get_connection_string, get_logsFor an instance
run_sqlRows (at most max_rows, default 100, with a paging hint), affected rows, or an error with its position and hint
check_sql, format_sqlOffline: syntax and a summary per statement; formatting
explain_queryThe plan as JSON; with analyze, runs the statement in a transaction that is always rolled back
describe_schema, describe_tableTables; columns, keys, indexes, constraints
doctorWhy Postgres will not start here

Every tool and parameter is in the MCP tools reference. Results come as structured content plus a compact Markdown table; json and jsonb values arrive as JSON, and long values are shortened, so results stay small for the assistant.

Safety ​

  • Changes ask first. Statements that change the schema or existing rows, kill_instance and remove_instance ask before running. Clients that support it (elicitation) ask you directly. Otherwise the tool refuses until the assistant calls again with confirm: true; that second call slows the assistant down but is not a check by you: your client's approval of tool calls is that check.
  • Read-only mode refuses anything that writes, including INSERT and transaction control (so COMMIT; DELETE ... cannot escape it), and runs the SQL in a read-only transaction. It applies per call (read_only: true), to the whole server (--read-only), and always to servers pgembed did not start (connection-string targets), unless you pass --allow-external-writes. Functions that act outside the transaction, such as pg_terminate_backend, are not stopped by it; use a restricted role for that.
  • Least privilege (--least-privilege, PostgreSQL 14 and later): the assistant works on instances as pgembed_assistant, a role that reads, writes and creates tables but is refused files, programs and libraries by the server itself, even through functions pgembed cannot see into. It cannot alter tables the superuser owns, so create the schema yourself (or let the assistant create its own tables).
  • --no-confirm and --allow-external-writes are announced on standard error at startup and in the instructions the assistant receives.
bash
pgembed mcp --read-only                 # refuse anything that writes, on every target
pgembed mcp --no-confirm                # no questions: for sandboxes and throwaway agents
pgembed mcp --allow-external-writes     # allow writes to servers pgembed did not start
snippet source | anchor

A read-only setup:

json
{
  "mcpServers": {
    "pgembed": {
      "command": "pgembed",
      "args": ["mcp", "--read-only"]
    }
  }
}
snippet source | anchor

Other servers, and passwords ​

Anything in a tool call is part of the conversation: the client shows it, logs it, and sends it to the model. So a password must never be in one. pgembed instances need none (they trust local connections). For any other server, give the MCP server a named connection: the assistant uses the name, and the connection string stays on your machine.

json
{
  "mcpServers": {
    "pgembed": {
      "command": "pgembed",
      "args": ["mcp", "--connection", "shop=postgresql://me@db.example.com/shop"],
      "env": { "PGPASSFILE": "/Users/me/.pgpass" }
    }
  }
}
snippet source | anchor
  • list_instances shows connection names, never their connection strings.
  • A named connection is a server pgembed did not start, so it is read-only unless you pass --allow-external-writes.
  • Keep the password out of the connection string too: PGPASSWORD, or a .pgpass file (PGPASSFILE), set for the server process.
  • Errors never repeat a password, even from a connection string pgembed could not read.

Released under the MIT License.