Set up the Postgres Toolbox MCP server
Postgres Toolbox is a hosted MintMCP connector, built on Google's MCP Toolbox, that exposes SQL and database-introspection tools against a single PostgreSQL database. You decide exactly which tools the connector exposes — ready-made tools like listing tables or inspecting query plans, plus your own parameterized SQL — by declaring them in a configuration value.
This guide covers creating a least-privilege database role, allowing MintMCP to reach your database, defining your tools, and connecting the connector.
Prerequisites
- A MintMCP admin account
- A PostgreSQL database reachable from MintMCP's runtime — a managed cloud Postgres (Amazon RDS, Cloud SQL, Neon, Supabase, etc.) or any host MintMCP can reach over the network.
- Credentials for a database role
Create a connector role
All users of this connector share a single database connection using the role you configure, so create a dedicated, least-privilege role rather than reusing an admin account.
- Connect to your PostgreSQL server as a superuser or a role with sufficient privileges, and create the role. Roles are server-wide, so this step works from any database. Replace
mydbwith your database name and choose a strong password:
CREATE ROLE mintmcp_ro LOGIN PASSWORD 'choose-a-strong-password';
GRANT CONNECT ON DATABASE mydb TO mintmcp_ro;
- Connect to the target database (
mydb) — not the defaultpostgresdatabase — then grant schema and table access. TheUSAGE,SELECT, andALTER DEFAULT PRIVILEGESstatements apply to the database you are currently connected to, so running them against the wrong database silently grants nothing:
GRANT USAGE ON SCHEMA public TO mintmcp_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mintmcp_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO mintmcp_ro;
How you switch databases depends on your client: in psql, run \c mydb first (it is a psql-only command); in a GUI client such as DBeaver or pgAdmin, open a connection to mydb and run the grants there.
A few things to keep in mind:
ALTER DEFAULT PRIVILEGESonly covers tables created by the role that runs it. If your tables are created by a different owner, addFOR ROLE <owner>— for example,ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA public GRANT SELECT ON TABLES TO mintmcp_ro;.- Only the
publicschema is covered above. If your data lives in other schemas, repeat theUSAGEandSELECTgrants for each one. - To let the monitoring tools (active queries, replication stats, long-running transactions) return complete data, also grant the built-in
pg_monitorrole:GRANT pg_monitor TO mintmcp_ro;. Without it, a plain role sees only partial output (for example, other users' query text is hidden inpg_stat_activity). - The role is the real boundary on what the connector can do. If you expose write or arbitrary-SQL tools (see below), they can only run what this role is granted — so keep it read-only unless write access is explicitly intended.
Allow MintMCP to reach the database
Postgres Toolbox uses a dedicated egress IP shown on its MintMCP connector settings page.
- In MintMCP, go to MCP store > Manage store and open the Postgres Toolbox connector page.
- Note the Egress IP shown there.
- If your database or its firewall / security group restricts inbound traffic, add that IP to the allowlist and confirm the port is open (default:
5432). For managed providers like Amazon RDS or Cloud SQL, add it to the instance's authorized networks.
Define the tools
The connector exposes only the tools you declare in the TOOLBOX_TOOLS environment variable. You define tools, not data sources — every tool runs against the database you configure in the next section, and the connector wires that connection in for you, so you never set a source. Each tool has a kind and an optional description; group tools into named toolsets.
There are two categories of tools:
- Ready-made tools — built-in introspection and monitoring tools. Declare the
kindand adescription; the tool's query is built in, and it exposes its own input parameters automatically (for example,postgres-list-tablesaccepts an optional list of table names to filter by). These never take astatement. - Custom SQL tools —
postgres-sqlruns a fixed, parameterizedstatementyou write;postgres-execute-sqlruns an arbitrary statement supplied by the caller.
{
"tools": {
"list_tables": { "kind": "postgres-list-tables", "description": "List tables in the database." },
"active_sessions": { "kind": "postgres-list-active-queries", "description": "Show currently running queries." },
"get_customer": {
"kind": "postgres-sql",
"description": "Look up a customer by id.",
"statement": "SELECT id, name, created_at FROM customers WHERE id = $1",
"parameters": [
{ "name": "id", "type": "integer", "description": "Customer id." }
]
},
"execute_sql": { "kind": "postgres-execute-sql", "description": "Execute an arbitrary SQL statement." }
},
"toolsets": {
"read": ["list_tables", "active_sessions", "get_customer"],
"write": ["execute_sql"]
}
}
Available tools
Pick a category to see what each tool returns, then expand Copy as JSON to grab a ready-to-paste config for that category. Ready-made tools need only a kind and a description; postgres-sql and postgres-execute-sql are the custom SQL tools.
- Custom SQL
- Schema & objects
- Activity & performance
- Storage & health
- Configuration & extensions
- Replication & access
| Kind | Required fields | Purpose |
|---|---|---|
postgres-sql | statement, plus parameters (bound $1, $2, …) and/or templateParameters ({{.name}}) | A fixed query you define and expose as a single tool |
postgres-execute-sql | none | Run an arbitrary SQL statement passed by the caller (read or write) |
Copy as JSON
{
"tools": {
"top_customers": {
"kind": "postgres-sql",
"description": "Highest-spending customers.",
"statement": "SELECT name, total FROM customers ORDER BY total DESC LIMIT $1",
"parameters": [
{ "name": "limit", "type": "integer", "description": "Number of rows to return.", "default": 10 }
]
},
"execute_sql": { "kind": "postgres-execute-sql", "description": "Execute an arbitrary SQL statement." }
},
"toolsets": { "sql": ["top_customers", "execute_sql"] }
}
| Kind | Returns |
|---|---|
postgres-list-tables | Table schema — columns, constraints, indexes, triggers (optional name filter) |
postgres-list-views | Views |
postgres-list-schemas | Schemas |
postgres-list-indexes | Indexes |
postgres-list-sequences | Sequences |
postgres-list-triggers | Triggers |
postgres-list-stored-procedure | Stored procedures and functions |
postgres-get-column-cardinality | Distinct-value counts per column |
Copy as JSON
{
"tools": {
"list_tables": { "kind": "postgres-list-tables", "description": "List table schema." },
"list_views": { "kind": "postgres-list-views", "description": "List views." },
"list_schemas": { "kind": "postgres-list-schemas", "description": "List schemas." },
"list_indexes": { "kind": "postgres-list-indexes", "description": "List indexes." },
"list_sequences": { "kind": "postgres-list-sequences", "description": "List sequences." },
"list_triggers": { "kind": "postgres-list-triggers", "description": "List triggers." },
"list_stored_procedures": { "kind": "postgres-list-stored-procedure", "description": "List stored procedures and functions." },
"column_cardinality": { "kind": "postgres-get-column-cardinality", "description": "Distinct-value counts per column." }
},
"toolsets": { "schema": ["list_tables", "list_views", "list_schemas", "list_indexes", "list_sequences", "list_triggers", "list_stored_procedures", "column_cardinality"] }
}
| Kind | Returns |
|---|---|
postgres-list-active-queries | Currently running queries (pg_stat_activity) |
postgres-long-running-transactions | Transactions exceeding a time limit |
postgres-list-locks | Locks held by active processes |
postgres-list-query-stats | Query execution statistics (pg_stat_statements) |
Copy as JSON
{
"tools": {
"active_queries": { "kind": "postgres-list-active-queries", "description": "Show currently running queries." },
"long_running_transactions": { "kind": "postgres-long-running-transactions", "description": "List transactions exceeding a time limit." },
"locks": { "kind": "postgres-list-locks", "description": "List locks held by active processes." },
"query_stats": { "kind": "postgres-list-query-stats", "description": "Query execution statistics." }
},
"toolsets": { "activity": ["active_queries", "long_running_transactions", "locks", "query_stats"] }
}
| Kind | Returns |
|---|---|
postgres-list-table-stats | Per-table stats — live/dead tuples, vacuum and analyze times |
postgres-list-tablespaces | Tablespaces |
postgres-list-database-stats | Database-level statistics |
postgres-database-overview | High-level overview of the database |
Copy as JSON
{
"tools": {
"table_stats": { "kind": "postgres-list-table-stats", "description": "Per-table statistics." },
"tablespaces": { "kind": "postgres-list-tablespaces", "description": "List tablespaces." },
"database_stats": { "kind": "postgres-list-database-stats", "description": "Database-level statistics." },
"database_overview": { "kind": "postgres-database-overview", "description": "High-level database overview." }
},
"toolsets": { "storage": ["table_stats", "tablespaces", "database_stats", "database_overview"] }
}
| Kind | Returns |
|---|---|
postgres-list-pg-settings | Server settings (pg_settings) |
postgres-list-available-extensions | Extensions available to install |
postgres-list-installed-extensions | Installed extensions |
Copy as JSON
{
"tools": {
"pg_settings": { "kind": "postgres-list-pg-settings", "description": "List server settings." },
"available_extensions": { "kind": "postgres-list-available-extensions", "description": "Extensions available to install." },
"installed_extensions": { "kind": "postgres-list-installed-extensions", "description": "Installed extensions." }
},
"toolsets": { "config": ["pg_settings", "available_extensions", "installed_extensions"] }
}
| Kind | Returns |
|---|---|
postgres-replication-stats | Per-replica lag and connection state |
postgres-list-publication-tables | Tables included in publications |
postgres-list-roles | Database roles |
Copy as JSON
{
"tools": {
"replication_stats": { "kind": "postgres-replication-stats", "description": "Per-replica lag and connection state." },
"publication_tables": { "kind": "postgres-list-publication-tables", "description": "Tables included in publications." },
"roles": { "kind": "postgres-list-roles", "description": "List database roles." }
},
"toolsets": { "replication": ["replication_stats", "publication_tables", "roles"] }
}
Postgres-only lock
This connector is Postgres-only by design. You cannot declare your own sources or use a non-postgres-* tool kind — the connector rejects them and refuses to start, with the reason in its logs, rather than silently connecting somewhere unexpected. The database your tools run against is always the one you configure below.
Add Postgres Toolbox to MintMCP
- Go to app.mintmcp.com/vmcps?tab=manage-store.
- Find Postgres Toolbox and click to install it.
- Set the following environment variables:
| Variable | Description | Required |
|---|---|---|
POSTGRES_HOST | Database hostname or IP | Yes |
POSTGRES_PORT | Port (default: 5432) | No |
POSTGRES_DATABASE | Database name | Yes |
POSTGRES_USER | Database role (e.g., mintmcp_ro) | Yes |
POSTGRES_PASSWORD | Role password — stored as a secret | Yes |
POSTGRES_SSLMODE | TLS mode: disable, prefer (default), require, verify-ca, or verify-full | No |
TOOLBOX_TOOLS | The tool definitions (see Define the tools) | Yes |
For managed databases that require TLS — which most do — set POSTGRES_SSLMODE to require (or verify-full). The default, prefer, attempts TLS and falls back to an unencrypted connection if the server does not offer it.
- Click Save. The connector connects to your database and loads your tools on startup, and becomes available once it can reach the database. If the database is unreachable or
TOOLBOX_TOOLSis invalid, the connector fails to start — check its logs for the reason.
Security considerations
- Every user queries your database as the single configured role — use a read-only, least-privilege role unless write access is explicitly intended.
- The Postgres-only lock prevents the connector from reaching other databases or services, but it does not restrict SQL operations. The
postgres-execute-sqltool and any writepostgres-sqltools can run whatever the role is granted, so the database role is the real guardrail. Omit those tools, or keep the role read-only, to prevent writes. - Connection details are stored as MintMCP global environment variables; the password is held as a secret.
- Prefer
POSTGRES_SSLMODE=require(orverify-full) for managed or remote databases. - Combine the dedicated egress IP with a database firewall allowlist so only MintMCP can reach the database.
Next steps
- Tool customization — Control which tools are exposed to users
- MCP gateway administration — Manage access and permissions