A connection that failed before PostgreSQL answered was reported as "connection failed (Error)", which
said nothing: a timeout from a firewall dropping the port looked like any other fault. It now says
which it was - the host does not resolve, the connection was refused, timed out, was closed before an
answer, or the TLS handshake failed - in words that point at where to look, still without the
address or the credentials. The internal mode's own connection, as the catalog's user, says the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The internal mode already took its whole connection from headers the catalog entry pins; the external one
let the model choose the endpoint, bucket, host and database in its open_session call. Now the catalog
entry sends them from the node's own configuration - x-minio-endpoint and x-minio-bucket,
x-postgres-host, x-postgres-port and x-postgres-database - pinned for the session like the internal
ones, and the model gives only the credentials. An external session still never creates a bucket or a
database: those must exist.
Without MINIO_MCP_ALLOWED_ENDPOINTS or POSTGRES_MCP_ALLOWED_HOSTS an external session reaches public
addresses only: loopback, private, link-local and metadata addresses are refused. For MinIO the check is
in the S3 client's own DNS lookup, at every connection it makes; for PostgreSQL the name is resolved and
checked, and the connection made to the address that was checked, the certificate still verified against
the name. With a list, exactly the listed destinations, private ones included. Before, an empty list let
MinIO reach anything and kept PostgreSQL's external mode off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four tools - postgres_query (read-only, bounded in rows and bytes), postgres_execute, postgres_list_tables
and postgres_describe_table - in two modes chosen by the catalog entry, as for minio-mcp.
Internal: the session arrives with x-postgres-scope set to the execution's id, and the server makes a
database and a user <prefix><execution id> if they are not there, closes the database to everyone else
and connects as that user, so the model can do anything inside it and nothing outside. Checked on a real
PostgreSQL, as a catalog user who is not a superuser: it cannot switch role, make users or databases, read
the server's files or reach another execution's database. The password is derived from the name and a
secret shared with the workflow manager, whose storage nodes use the same convention; both test suites
assert the same known name and password.
External: the model opens a session with a host and credentials it is given. It is off until
POSTGRES_MCP_ALLOWED_HOSTS names the hosts it may use.
The client's query_timeout is not used: with a cursor it arms a timer that is never cleared and that,
when it fires, throws outside any handler and takes the server down.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>