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:
claude mcp add pgembed -- npx -y pgembed mcpWith .NET 10, dnx runs it from NuGet instead of npm:
claude mcp add pgembed -- dnx pgembed mcpClaude Desktop, Cursor and other clients that read an mcpServers file:
{
"mcpServers": {
"pgembed": {
"command": "npx",
"args": ["-y", "pgembed", "mcp"]
}
}
}VS Code (.vscode/mcp.json):
{
"servers": {
"pgembed": {
"type": "stdio",
"command": "npx",
"args": ["-y", "pgembed", "mcp"]
}
}
}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_extensions | Versions, ahead-of-time installs with progress, extensions |
start_instance, list_instances, stop_instance, restart_instance | Instances; started ones keep running after the assistant exits |
kill_instance, remove_instance | Force-kill, or delete with its data; both ask first |
get_connection_string, get_logs | For an instance |
run_sql | Rows (at most max_rows, default 100, with a paging hint), affected rows, or an error with its position and hint |
check_sql, format_sql | Offline: syntax and a summary per statement; formatting |
explain_query | The plan as JSON; with analyze, runs the statement in a transaction that is always rolled back |
describe_schema, describe_table | Tables; columns, keys, indexes, constraints |
doctor | Why 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_instanceandremove_instanceask before running. Clients that support it (elicitation) ask you directly. Otherwise the tool refuses until the assistant calls again withconfirm: 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
INSERTand transaction control (soCOMMIT; 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 aspg_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 aspgembed_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-confirmand--allow-external-writesare announced on standard error at startup and in the instructions the assistant receives.
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 startA read-only setup:
{
"mcpServers": {
"pgembed": {
"command": "pgembed",
"args": ["mcp", "--read-only"]
}
}
}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.
{
"mcpServers": {
"pgembed": {
"command": "pgembed",
"args": ["mcp", "--connection", "shop=postgresql://me@db.example.com/shop"],
"env": { "PGPASSFILE": "/Users/me/.pgpass" }
}
}
}list_instancesshows 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.pgpassfile (PGPASSFILE), set for the server process. - Errors never repeat a password, even from a connection string pgembed could not read.
