Database Plugin
Gives the agent general SQL tools — list connections, list tables, describe a table, run a read-only query, and run a gated write — against one or more configured databases. Each connection picks its own backend: a native Python driver or an external database CLI. Engines: Postgres, MySQL/MariaDB, Oracle, SQL Server, plus SQLite.
Switchable backends
A connection's mode decides how it talks to the database:
mode | Behaviour |
|---|---|
native | Use the engine's Python driver. |
cli | Shell out to the engine's command-line tool. |
auto (default) | Native if the driver is importable, otherwise the CLI. |
| Engine | Native driver | CLI fallback | Default port |
|---|---|---|---|
Postgres (postgres, also postgresql / pg) | pg8000 | psql | 5432 |
MySQL / MariaDB (mysql, also mariadb) | PyMySQL | mysql | 3306 |
Oracle (oracle, also oracledb) | oracledb (thin mode) | — | 1521 |
SQL Server (sqlserver, also mssql) | python-tds | go-sqlcmd | 1433 |
SQLite (sqlite) | stdlib sqlite3 | — | — |
The four native drivers ship with Codumentor (pure Python — no libpq, Oracle Instant Client, or ODBC). They are imported only when a connection uses them, so a missing driver does not prevent the plugin from loading; mode: auto then falls back to the CLI.
Use the CLI path when you prefer not to use the bundled driver, or for cases the pure-Python drivers handle poorly — most notably SQL Server with Azure AD / TDS 8.0, where go-sqlcmd (a single static MIT binary) is the better choice. Oracle thin mode covers Oracle fully, so no Oracle CLI ships.
Native connections support bound params; CLI connections do not (use mode: native for parameterized queries).
Tools
| Tool | Approval | Purpose |
|---|---|---|
sql_list_connections | none | List configured connections (name, engine, host, database, read-only). Touches no database. |
sql_list_tables | none | Explore tables/views. Optional schema and pattern (case-insensitive name substring) filters; see below. |
sql_describe_table | none | Column metadata (name, type, nullability, default). |
sql_query | none | Run a read-only statement; rejects anything that isn't a single SELECT/WITH/EXPLAIN. Row-capped; results are JSON. |
sql_execute | database.execute | Run a write/DDL statement. Permission-gated, and refused on read-only connections (no prompt). |
When exactly one connection is configured, the connection argument is optional everywhere and defaults to it.
Who gets which half
Two gates, not one. target_agents (default all) decides whether the tools are offered at all; write_target_agents (default main) then decides who gets sql_execute.
By default a subagent can explore a schema and run read-only queries but cannot change data or DDL — the approval prompt sql_execute raises is meant for a human, and nobody is reading a subagent's tool calls as they happen. Set write_target_agents: all if subagents should also write. A connection's own read_only is the stricter guard and applies to every agent.
sql_query results come back as a JSON envelope — {"columns": [...], "rows": [...], "row_count": N, "truncated": bool} — rather than an ASCII table. When truncated is true, narrow the query.
Schema exploration scales down, not up
sql_list_tables adapts its output so a huge catalog cannot silently swallow schemas:
- Unfiltered, small catalog (≤ ~200 objects): names, grouped per schema —
{"schemas": {"main": {"tables": [...], "views": [...]}}, "total": N}. - Unfiltered, large catalog: per-schema counts only —
{"schemas": {"dbo": {"tables": 1953, "views": 170}, ...}, "hint": ...}— with a hint to drill in viaschema=and/orpattern=. - Filtered (
schemaand/orpattern): names, capped atmax_rowswith an explicit"truncated": truewhen the filter is still too broad.
The intended flow on big databases is therefore: counts overview → drill into one schema, or search by name fragment (pattern: "kivonat").
Configuration
plugins:
- module: codumentor.plugins.database
class: DatabasePlugin
args:
enabled: true
target_agents: all # main | subagent | all — the whole family
write_target_agents: main # same values, sql_execute only
default_read_only: true # per-connection default
max_rows: 1000 # hard cap for sql_query
statement_timeout: 30 # seconds
connections:
- name: analytics # logical handle the agent uses
engine: postgres # postgres | mysql | oracle | sqlserver | sqlite
mode: auto # native | cli | auto
host: db.example.com
port: 5432
database: analytics
user: reporting
password_secret: pg_analytics_password # per-user secret name
read_only: true
- name: warehouse
engine: sqlserver
mode: cli # go-sqlcmd
host: mssql.example.com
port: 1433
database: warehouse
user: svc_reader
password_secret: mssql_warehouse_password
read_only: true
Load user_secrets alongside this plugin so passwords can resolve per user.
Plugin parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
enabled | bool | true | Master on/off. |
target_agents | string | all | Who gets the tools: main, subagent, all, or a subagent role name. |
write_target_agents | string | main | Same values, applied to sql_execute only. Subagents keep the read half. |
default_read_only | bool | true | Applied to any connection that omits read_only. |
max_rows | int | 1000 | Hard cap for sql_query (and the name cap for filtered sql_list_tables). |
statement_timeout | float | 30 | Per-statement timeout in seconds. |
connections | list | [] | Named connections (see below). Names must be unique. |
Connection parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
name | string | (required) | Handle the agent uses in tool calls. |
engine | string | (required) | postgres, mysql, oracle, sqlserver, or sqlite (aliases above). |
mode | string | auto | native, cli, or auto. |
host | string | localhost | Database host. |
port | int | engine default | Omit to use the engine default. |
database | string | — | Database / service name. For SQLite this is the file path (:memory: if omitted). |
user | string | — | Login user. |
password_secret | string | — | Per-user secret name (preferred). |
password | string | — | Shared credential; may contain ${secret:...}. |
read_only | bool | default_read_only | When true, sql_execute is refused. |
connect_kwargs | map | {} | Extra keyword arguments passed to the native driver (for example an Oracle dsn). |
Credentials
Two models are supported, per connection:
- Per-user secret (preferred) — set
password_secret: <name>. The config stores only the secret name; each user supplies their own password in Settings → Extensions → User Secrets, and tools authenticate as the calling user. The pane shows "DB password (<connection>) — needed by Databases". - Shared service account — set
password: ${secret:<name>}(a shared secret resolved server-side). Useful for a read-only reporting replica with a single account.
If a password_secret is declared but unset, tools return a clear, non-retryable message naming the secret — no connection attempt is made. A connection with neither field resolves to "no password" (works for SQLite, trusted/integrated auth, etc.).
Safety
- Read-only by default, enforced in depth:
sql_queryonly accepts a singleSELECT/WITH/EXPLAIN, plus a session-level read-only setting where the engine supports it (Postgres, MySQL, SQLite). Oracle and SQL Server rely on the statement guard plus the login's privileges, so grant least privilege there. - Writes are gated.
sql_executeasks for approval (database.execute) and is refused outright on a read-only connection. - Result caps.
limitis clamped tomax_rows; an extra sentinel row marks truncation; wide cells are truncated. - Statement timeouts applied natively and as a CLI wall-clock kill.
- Credential hygiene. CLI passwords go through an environment variable (
PGPASSWORD/MYSQL_PWD/SQLCMDPASSWORD), never the command line; SQL is passed on stdin.
Notes & limitations
- CLI
params: not supported — usemode: nativefor bound parameters. - go-sqlcmd: results are coerced to JSON server-side (
FOR JSON PATH) because its CSV output does not quote/escape. - Oracle/SQL Server identifiers in
describe_tablefollow each engine's default case-folding (Oracle uppercases unquoted names).