Skip to content

Releases: ginkida/dbecho

v0.5.0

11 Jun 08:45

Choose a tag to compare

What's new in 0.5.0

Features

  • trend JSON output — the trend tool gains a format="table"|"json" parameter, mirroring query.
  • dbecho --version — print the installed version.
  • dbecho --check — validate the config and ping every configured database, printing an [OK]/[FAIL] <name> line per database and exiting non-zero if any is unreachable. Drops straight into a CI or healthcheck script.
  • find tool (first published release) — locate tables and columns by name substring across all configured databases at once.

Hardening

  • publish.yml now fails the publish if the release tag and pyproject version disagree (PyPI versions are immutable, so a mismatch would burn the number permanently).

Full test suite: 224 tests, all passing. No Postgres required (mocked connections).

v0.3.0 — Per-database schema support

05 Jun 12:43

Choose a tag to compare

Apps that keep tables outside public (a dedicated analytics schema, multi-tenant layouts, …) are now first-class citizens.

✨ New: per-database schema config option

[databases.events]
url = "${EVENTS_DATABASE_URL}"
schema = "analytics"
  • All metadata tools target the configured schemaschema, describe, analyze, anomalies, trend, sample, erd, indexes, and foreign keys now parametrize the schema instead of hardcoding public (default remains public; existing configs are unaffected).
  • Raw queries work unqualified — for non-public schemas the connection sets search_path=<schema>,public, so SELECT count(*) FROM events just works; public stays second so extensions (pgvector operators etc.) keep resolving.
  • Clearer errors — "table not found" and empty-schema messages now name the schema they looked in.

🔒 Safety

  • The schema name is validated at config load as a plain lowercase identifier (^[a-z_][a-z0-9_]*\Z) — it is the one identifier-shaped value embedded outside psycopg.sql.Identifier() (in the libpq search_path option), so it is held to a stricter shape than table/column identifiers. Lowercase-only because unquoted search_path case-folds: an uppercase schema would silently diverge from the case-preserving parametrized metadata queries. \Z anchor keeps a trailing newline out of the options string.
  • All schema-scoped catalog queries use %s parameters — never string interpolation.

✅ Tests

  • Test suite expanded 179 → 199: config validation (uppercase/trailing-newline/non-string rejection), per-tool schema scoping, anti-mutant "no hardcoded public" asserts, search_path option assembly.

Backward-compatible: without schema in the config nothing changes.

Install: pip install dbecho

v0.2.0 — Security hardening + new tools

04 Jun 14:19

Choose a tag to compare

A security-hardening and capability release. No live exploit existed in 0.1.x, but the SQL whitelist relied entirely on the read-only connection; this release makes the validator independently sufficient, closes information-leak channels, fixes two correctness crashers, and adds two new tools.

🔒 Security (defense-in-depth)

  • Validator no longer depends on the read-only GUC alone — rejects data-modifying CTEs (WITH x AS (DELETE … RETURNING *) …), SELECT … INTO, and EXPLAIN ANALYZE over write statements.
  • Blocked dangerous function callspg_read_file, lo_*, dblink, set_config (exfiltration/escape vectors even under read-only).
  • Sensitive-column redaction (default on) — values of password/token/api_key/secret-like columns are replaced with <redacted> in query/sample/analyze. Metadata columns (token_count, password_changed_at, otp_enabled) are not affected. Disable with redact_sensitive = false.
  • Sanitized connection errors — host/user/dbname no longer leak to the agent; full detail goes to the server log only.
  • Resource boundsmax_profile_rows gate for analyze/anomalies, schema/FK/index caps, compare dedup + cap (with a visible note), config upper bounds, session statement_timeout + idle_in_transaction backstops.
  • Identifier regex uses \Z (trailing-newline safe); $1 is treated as a positional parameter, not a dollar-quote tag.

🐛 Correctness

  • Fixed numeric(20,2) overflow on large SUM/AVG in analyze/trend.
  • Per-column probes run inside savepoints — a failed probe is skipped (reported via skipped_columns) instead of aborting the whole profile; timeouts still propagate.
  • trend validates column types up front (clear errors instead of opaque failures).
  • Row counts are labeled as planner estimates (~N rows est.).

✨ New capabilities

  • describe — single-table schema + indexes (much cheaper than schema on large databases).
  • explain — query plan cost/row estimates without executing the query.
  • query gains offset paging and format="json".
  • Cells render NULL/bytes/JSON safely; display truncation is marked with an ellipsis.

🧰 Robustness / ops

  • main() validates config eagerly at startup and forces logging to stderr (stdout is the MCP channel).
  • ${ VAR } placeholders tolerate inner whitespace.

✅ Tests / CI

  • Test suite expanded 89 → 179 (read-only option, redaction, savepoint isolation, timeout budget, identifier-injection boundaries, new tools, full server tool layer).
  • CI pins ruff via dev extras and adds a build + wheel-import job.

Tools: 11 → 13 (added describe, explain). Backward-compatible; the one behavior change is redaction defaulting on.

Install: `pip install dbecho`

v0.1.1 — bug fixes

17 Apr 07:13

Choose a tag to compare

Fixed

  • SQL validation: ; inside string literals (e.g. SELECT 'a;b' FROM t) and leading comments (-- foo\nSELECT 1, /* */ SELECT 1) no longer trigger false rejections. A new scanner strips strings (single/double/dollar-quoted) and comments (line + nested block) before the structural check.
  • EXPLAIN parser: the parenthesized form EXPLAIN (ANALYZE) DELETE … is now correctly blocked. Previously it slipped past the inner whitelist (read-only transaction still caught it, but the tool-level error message now arrives earlier with a clear reason).
  • Anomaly detection: anomalies no longer false-flags columns like paid, void, candidate, guide, solid as possible duplicates. The old check used "id" in col_name.lower() which matched those as substrings. Now uses token-level matching: exact id, or email/uuid as a whole underscore-separated token. *_id FK columns are intentionally excluded (their duplicates are legitimate).
  • Schema cache: get_schema(use_cache=True) returns a fresh copy and stores a copy internally — caller mutation no longer corrupts the cache.
  • Error handling: schema and erd tools now catch ValueError/TimeoutError/Exception and return friendly error strings, matching the other tools. TimeoutError is handled specifically across every tool — user sees "query timeout exceeded" instead of "unexpected failure".
  • Config validation: unknown keys in [databases.<name>] (e.g. typo descreption = …) and non-string description values are now rejected at load time.

Internal

  • +17 tests (72 → 89)
  • No public API changes; drop-in upgrade from 0.1.0

v0.1.0

11 Mar 12:58

Choose a tag to compare

Initial release of dbecho — MCP server for multi-database PostgreSQL analytics.

Features

  • 11 tools: schema, query, analyze, compare, trend, anomalies, sample, erd, health, summary, list_databases
  • 3 MCP resources and 3 MCP prompts
  • Read-only connections with SQL injection prevention
  • Query timeouts and row limits
  • Multi-database support with TOML config
  • Environment variable expansion (${VAR} syntax)

Install

pip install dbecho

Quick start

See README for setup instructions.