MCPg v0.6.12
Added
-
run_analytical_query— long-running reads on an isolated pool.
run_selectis bounded to a short (~30s) timeout on the shared 5-connection
pool so an agent can't pin a connection with a runaway query — which made
genuine analytical queries (large aggregations / joins / window functions)
infeasible, andMCPG_STATEMENT_TIMEOUT_MSdidn't help (it moves Postgres's
statement_timeoutbut not the client-side asyncio cap). The new tool runs a
read-only SELECT through the same allowlist + tenancy/RLS + read-only
transaction, but on a dedicated connection pool isolated from the main
one, with an elevated, bounded timeout — so a slow query can never starve
the fast-path tools. A per-calltimeout_ms(clamped to the max) and optional
work_memare exposed; arun_selecttimeout now points the agent at the
tool. Boot-time knobs:MCPG_ENABLE_ANALYTICAL_QUERIES(default true, gates
the tool — it's a READ tool, so set false to withdraw it from an anonymous
read-only deployment),MCPG_ANALYTICAL_TIMEOUT_MS(default 120000),
MCPG_ANALYTICAL_MAX_TIMEOUT_MS(600000),MCPG_ANALYTICAL_MAX_CONCURRENCY
(2 — the isolated pool size, which is also the concurrency cap). Primary
database only for now. Tool surface 253 → 254. -
Cache-freshness controls for out-of-band schema changes. MCPg's read
cache is invalidated automatically by MCPg's own write/DDL tools, but it
could serve stale introspection/advisor results for up to
MCPG_CACHE_TTL_SECONDS(default 300s) after a schema change made outside
MCPg (a directpsql/migration change, another connection, or a second
process) — e.g. re-running index/constraint validation after altering a
foreign key returned the pre-change answer. Two escape hatches:- a per-call
fresh: bool = Falseargument on the introspection/advisor
reads (describe_table,list_indexes,list_constraints,
list_foreign_keys,get_compact_schema,recommend_indexes,
audit_database) — bypasses the cache read, re-queries live, and refreshes
the entry; - a new
clear_cachetool — a full flush for a known out-of-band change,
gated to write-capable modes (not exposed on the read-only surface).
Tool surface 252 → 253.
docs/user-guide.mdcaching section corrected
(automatic invalidation covers through-MCPg mutations, not arbitrary
out-of-band DDL). - a per-call
Security
- Bumped
mcpSDK ≥ 1.28.1 (from≥ 1.25.0) to clear three advisories in
the resolvedmcp1.27.1: CVE-2026-52870 and CVE-2026-52869 (fixed in
1.27.2) and CVE-2026-59950 (fixed in 1.28.1). No MCPg source change required:
the tenancy per-request role path (tenancy._role_from_request→
mcp.server.lowlevel.server.request_ctx+
ServerMessageMetadata.request_context) is unchanged in 1.28.1, and the full
unit + contract suite (incl.test_tenancy.py,test_http_runtime.py)
passes.pip-audit --stricton the resolved runtime deps is clean.
Changed
-
run_analytical_queryhardening (follow-up to the feature above).
Query timeouts now raise a typedQueryTimeoutError(aQueryError
subclass) covering both the client-side asyncio wall-clock cap and the
server-sidestatement_timeout(SQLSTATE 57014) — therun_select"retry
withrun_analytical_query" hint branches on the type instead of matching
message text, so it fires reliably (and locale-independently) for the
Postgres-side timeout it previously missed. The hint is now shown only when
a runner is actually wired up.AnalyticalRunneris injectable via
create_server(..., analytical_runner=...), andrun_analytical_queryis
registered only when a runner will back it, so an injected-database setup no
longer advertises a non-functional tool.MCPG_ENABLE_ANALYTICAL_QUERIESis
the authoritative off-switch — it wins even over an injected runner. Timeout
detection walks the full__cause__/__context__exception chain (not just
the immediate cause), so extra wrapping layers can't hide a timeout. Also
documents the analytical env vars in the README env-var table, the T3 (DoS)
threat model, and the scaling pool-overhead budget. No tool-surface change
(still 254). -
Bumped
pglast7.15 → 8.2 (the SQL-safety kernel's parser). pglast 8
trackslibpg_query18-latest (PostgreSQL 18 grammar) and now ships type
hints. No behaviour change for MCPg: the full adversarial + fuzz +
differential-parity SQL-kernel suites and the PG-grammar characterisation
tests all pass unchanged. The AST walker's__slots__recursion gets a
targetedtype: ignoresince pglast 8'sNodebase type doesn't declare it
(the concrete node subclasses still carry their fields there at runtime).