Skip to main content
Runs a SELECT against a connected PostgreSQL database. Display name “PostgreSQL Query”. Off by default.

Authentication and enablement

Requires a workspace Postgres integration. Host, port, database, user, password, SSL, and optional schema are injected from that integration (x-exclude-from-llm). Enable the tool on the agent and bind the integration.

Inputs

  • query (required): SELECT only. Example: SELECT * FROM users WHERE status = 'active' LIMIT 10.
  • query_params (object, pinned queries only): values for the query’s named placeholders. Supplied by the agent per call — it is not a configurable field and does not appear in the tool’s configuration dialog. It is offered to the agent only when query is pinned to a statement containing placeholders — see below.

Pinned queries with named parameters

Instead of letting the agent write the SQL on every run, set the tool’s Query field in its configuration dialog to a fixed statement that addresses its values by name:
The tool then offers the agent a query_params object whose keys are exactly the placeholders that query declares, and the agent only supplies values:
Values travel to the server as bound parameters, so a value containing a quote or an apostrophe needs no escaping and cannot change the statement. A query is only ever inspected for placeholders on a call that carries query_params, and query_params is offered only when a pinned query declares some — so a query the agent writes itself is passed to the database exactly as written. Prefer this over a model-authored query for any lookup that runs in production: the SQL is reviewed once, at configuration time, instead of being regenerated on every task. Notes:
  • Placeholder names use :name. A ::type cast is a cast, not a placeholder, so :org_id::uuid binds one value and casts it.
  • Repeat a name to reuse the same value: WHERE name = :n OR alias = :n binds once.
  • Declare types in the SQL (:amount::numeric) rather than in the value — values are sent as text and cast server-side.
  • Placeholders substitute values, never table or column names, and cannot supply a list to IN (...). Use = ANY(:ids::uuid[]) for a list — and because every value is scalar text, pass the Postgres array literal as a string ("{id1,id2}"), not a JSON array. A JSON array is rejected.
  • A placeholder cannot appear inside an array subscript. A colon between brackets is read as a slice separator, so arr[lo:hi] stays a slice rather than becoming a parameter named hi.
  • Named and ordinal ($1) placeholders cannot be mixed in one query.
  • Maximum 64 distinct placeholders.

Output

Structured rows plus:
  • query — the SQL as authored, with its :name labels intact. This is what the task feed renders, so a lookup that returned nothing can still be read against the values it was meant to filter on.
  • query_bound — the statement actually sent, with placeholders replaced by $1, $2…
  • query_param_keys — the placeholder names that were bound. The values are never reported: they are customer document data and do not belong in a rendered feed item.
  • param_count, row_count, truncated, columns, rows.
Suitable as content for generate_file.

Limits and side effects

  • Non-SELECT statements are rejected.
  • Read-only by design; use postgres_upsert (separate tool) to write.
  • Returns at most max_rows rows (default and maximum 3000); truncated reports when the limit was hit.

Expected errors

  • Missing/invalid integration credentials.
  • Non-SELECT query.
  • A placeholder in the pinned query with no value in query_params, or a query_params key that is not a placeholder in the query (both name the offending key and list the placeholders the query expects, so the agent can retry).
  • A query_params value that is an object or an array (numbers and booleans are accepted and sent as text).
  • A pinned query mixing :named and $1 placeholders, or declaring more than 64 placeholders. Both are rejected when the tool is saved, and again at run time.
  • A query declaring placeholders on a call that supplied no query_params at all — reported with the placeholder names, rather than reaching the database with :name markers intact.
  • SQL error from the database (surfaced as a tool error, not a panic).