Engines
You write every template the same way, with
:name placeholders; the engine rewrites them per driver. Whatever the engine, the template is validated before it runs: one SELECT (or WITH ... SELECT), no comments, no second statement, no data-modifying keyword anywhere.
The connection reference
connection_ref names where the connection string is, in one of three forms:
A value in that field (anything with a scheme, a host or a password) is refused at registration, by the dashboard and by the agent. What the variable holds, per engine:
A read-only role
The validator refuses writes, and Postgres, Redshift, MySQL and ClickHouse refuse them a second time on the connection. For the others the role is the second layer, so create one that can only read:roles/bigquery.dataViewer on the dataset and roles/bigquery.jobUser on the project.
Drivers in the agent image
The published image ships no database driver. Build your own fromdataplane-agent/ with the engines you need, comma-separated:
install_drivers.sh installs the pinned lines from requirements-drivers.txt for those engines and nothing else; an engine that is not built in fails a query with driver_missing. postgres and redshift share one driver.
Connect
From Dashboard > Connect a system, choose Database, the organization, the engine and the reference. Preview runs discovery through the agent and lists the tables and columns it found; Connect creates the service. Over the API:local_only and gets three method: SQL tools, none with a template of its own:
Discovery reads every non-system schema (or the one in
database.schema; on BigQuery a dataset is required) through the agent and stores the tables and columns on the service as metadata.discovery.tables, capped at 500 tables and 200 columns per table. With no agent online the service is still created and discovered.error is no_dataplane_agent; start the agent, then Discover again on the service page (POST /v1/organizations/{org_id}/connect/{service_id}/discover).
Allowlist and masking
The policy starts with no table allowed: a Query on any table answerspermission_denied until you list some. On the service page, pick tables from what discovery found, or over the API:
allowed_tables and denied_tables take schema.table or table, case-insensitive; a deny wins over an allow, and List tables shows only what is allowed. masked_columns (table.column or column) come back as [REDACTED:<column>] in every row and are marked in Describe table. max_rows caps every call at 1,000 or less. A statement that names a table outside the allowlist, or anything but a single SELECT, is refused with error_type: permission_denied, fault: caller, and the message names the table; the agent skill tells agents to read that as policy, not as an outage.
Promote a statement
Once an agent has run a statement worth keeping, turn it into a named tool from the Audit Log (Promote to tool on thetool.execute row) or over the API, with the execution id or the statement itself:
method: SQL row at /queries/<slug> carrying the engine, the reference and the template, exactly like a template written by hand, and it is not subject to the allowlist: it does what its template says.
Templates (advanced)
A connector that declares its own query templates is still registered throughPOST /v1/organizations/{org_id}/services with source: database, or from the Connector Builder. Each template becomes one tool and no generic tools are created:
method: SQL tool at /queries/<slug> with the engine, the reference and the template in its metadata. A parameter used in the template and not declared is reported as a warning: an agent will not know to send it, and a call without it fails with bad_request: missing parameter.
Limits
- One read statement per template. Comments,
;, dollar quotes and#are refused. - At most 1,000 rows per call (
max_rowson the connector lowers it);truncated: truein the result says more matched. - A 30 second statement timeout by default (
timeout_seconds, up to 55 inside the 60 second dispatch budget) and a 10 second connect timeout. - Cells are returned JSON-safe: dates as ISO 8601, decimals as numbers, bytes as base64. Credential-shaped values in rows are masked like any other tool result.
POST /v1/connectors/database/test,/introspect,/inferand/dry-runconnect from the control plane and are for self-hosted deployments; for alocal_onlyconnector they answer400and the test is a tool run after registration.
