Skip to content

Commit 66546cf

Browse files
authored
feat: cache-freshness controls — fresh arg + clear_cache tool (#282)
Fixes a footgun surfaced by a user report: re-running index/constraint validation after an out-of-band foreign-key change returned the pre-change answer, served stale from the TTL read cache. The cache is invalidated by MCPg's own write/DDL tools but is blind to changes made outside MCPg until the TTL (default 300s) expires. Proportionate fix (no pg_catalog polling / event triggers): - `_cached_call` gains `fresh: bool = False`, threaded onto the introspection/advisor reads used in change->verify loops (describe_table, list_indexes, list_constraints, list_foreign_keys, get_compact_schema, recommend_indexes, audit_database). fresh=true bypasses the read, re-queries live, and refreshes the entry; it is not part of the cache key. - new `clear_cache` tool (full flush), gated to the WRITE surface so the read-only demo can't spam-flush; non-destructive; operations_and_health. Tool surface 252 -> 253; both contract snapshots + doc tables regenerated; current-state counts and anchors updated; user-guide caching section corrected and fresh/clear_cache/MCPG_CACHE_ENABLED documented. New test_cache_freshness.py. Full unit suite (2805) + contract + about green. N/A — reactive correctness fix, not a planned roadmap item.
1 parent fc48f4d commit 66546cf

18 files changed

Lines changed: 257 additions & 44 deletions

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@ adheres to [Semantic Versioning](https://semver.org/).
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- **Cache-freshness controls for out-of-band schema changes.** MCPg's read
12+
cache is invalidated automatically by MCPg's own write/DDL tools, but it
13+
could serve stale introspection/advisor results for up to
14+
`MCPG_CACHE_TTL_SECONDS` (default 300s) after a schema change made *outside*
15+
MCPg (a direct `psql`/migration change, another connection, or a second
16+
process) — e.g. re-running index/constraint validation after altering a
17+
foreign key returned the pre-change answer. Two escape hatches:
18+
- a per-call **`fresh: bool = False`** argument on the introspection/advisor
19+
reads (`describe_table`, `list_indexes`, `list_constraints`,
20+
`list_foreign_keys`, `get_compact_schema`, `recommend_indexes`,
21+
`audit_database`) — bypasses the cache read, re-queries live, and refreshes
22+
the entry;
23+
- a new **`clear_cache`** tool — a full flush for a known out-of-band change,
24+
gated to write-capable modes (not exposed on the read-only surface).
25+
26+
Tool surface **252 → 253**. `docs/user-guide.md` caching section corrected
27+
(automatic invalidation covers through-MCPg mutations, not arbitrary
28+
out-of-band DDL).
29+
930
### Security
1031

1132
- **Bumped `mcp` SDK ≥ 1.28.1** (from `≥ 1.25.0`) to clear three advisories in

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ stays true; verify anything here against the code before relying on it.
66
## What MCPg is
77

88
A production-grade PostgreSQL MCP server. Single-process, async
9-
(`asyncio`), Python 3.12–3.14. **252 MCP tools** (read-only mode exposes
9+
(`asyncio`), Python 3.12–3.14. **253 MCP tools** (read-only mode exposes
1010
a subset). Ships to PyPI (`mcpg`), GHCR, the MCP registry, Smithery, and
1111
a read-only HF Spaces demo.
1212

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
**A production-grade [Model Context Protocol](https://modelcontextprotocol.io)
66
server for PostgreSQL.** Lets AI agents safely inspect, query, operate, and
7-
tune a Postgres database — 252 tools spanning catalog introspection,
7+
tune a Postgres database — 253 tools spanning catalog introspection,
88
query intelligence, natural-language SQL, structural diffs, hybrid search,
99
graph queries, data movement, live ops, and more.
1010

docs/architecture.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ this table is the exhaustive index. Regenerate with
8383
| `mcpg.autovacuum` | Autovacuum priority advisor — `read_autovacuum_priority`. |
8484
| `mcpg.cache` | Thread-safe, async-safe caching manager for PostgreSQL introspections and summaries. |
8585
| `mcpg.composite` | Composite tools — agent UX wins built on top of existing primitives. |
86-
| `mcpg.config` | Env-driven, validated `Settings` (frozen dataclass); redacts secrets in `__repr__`. |
86+
| `mcpg.config` | Env-driven, typed configuration for the MCPg server. |
8787
| `mcpg.config_advisor` | Configuration & sizing advisors — pghero / pgtune coverage. |
8888
| `mcpg.context` | `AppContext` — per-server state (settings, DB, cursor/listen managers) shared with tool wrappers. |
8989
| `mcpg.cron` | pg_cron job-scheduling wrappers. |
@@ -147,7 +147,7 @@ this table is the exhaustive index. Regenerate with
147147
| `mcpg.replicas` | Read-replica routing — distribute read-only queries across replicas. |
148148
| `mcpg.resources` | MCP resources — preload-on-connect surface (`mcpg://…`). |
149149
| `mcpg.rls` | Row-Level Security tester — see what an RLS-bound role would read. |
150-
| `mcpg.schema_diff` | Structural schema diff powering `compare_schemas`. |
150+
| `mcpg.schema_diff` | Structural diff between two PostgreSQL schemas. |
151151
| `mcpg.schema_docs` | Schema documentation generator. |
152152
| `mcpg.secrets` | Pluggable secrets backend. |
153153
| `mcpg.server` | MCP server bootstrap for MCPg. |

docs/plans/bm25-integration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
> `pg_search_parse_query`, `hybrid_bm25_vector_search`,
77
> `create_pg_search_index`, `reindex_pg_search_index`, plus the
88
> observability/advisor tools — see the
9-
> [tool index](../tools.md#tool-index-252-tools). This doc is kept for
9+
> [tool index](../tools.md#tool-index-253-tools). This doc is kept for
1010
> its design rationale and the deferred-alternatives (`pg_textsearch`,
1111
> `vchord_bm25`) return conditions; it is no longer a live roadmap.
1212
> Current gaps live in [feature-shortlist.md](../feature-shortlist.md).

docs/plans/pg19-readiness.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ that accidentally deletes a tool fails CI.
4444
|---|---|---|
4545
| 1. CI matrix + compatibility surface |**Shipped** | PG 19 beta runs as an experimental (`continue-on-error`) matrix entry; pgvector built from source via `.github/ci-postgres-pg19.Dockerfile`. A WarehousePG (MPP) characterisation lane landed alongside. PostGIS still deferred until an apt package is published. |
4646
| 2. Feature audit |**Done** | The Beta 1 sweep below is complete — every domain triaged through the product-owner lens. |
47-
| 3. Incremental landing |**Largely shipped** | Many tool families have landed: SQL/PGQ property graphs (`run_pgq`, `create_property_graph`, …), in-server `REPACK` (`repack_table`), skip-scan advisor (`recommend_skip_scan_indexes`), `WAIT FOR LSN` read-your-writes (`wait_for_lsn`), online data-checksum + on-demand logical-replication toggles, DDL introspection (`get_role_ddl` / `get_database_ddl` / `get_tablespace_ddl` / `validate_check_constraint`), partition `MERGE` / `SPLIT`, lock + recovery stats, and async-I/O coverage. See the [tool index](../tools.md#tool-index-252-tools) for the shipped surface and [feature-shortlist.md](../feature-shortlist.md) for the remaining items (tracked to GA). |
47+
| 3. Incremental landing |**Largely shipped** | Many tool families have landed: SQL/PGQ property graphs (`run_pgq`, `create_property_graph`, …), in-server `REPACK` (`repack_table`), skip-scan advisor (`recommend_skip_scan_indexes`), `WAIT FOR LSN` read-your-writes (`wait_for_lsn`), online data-checksum + on-demand logical-replication toggles, DDL introspection (`get_role_ddl` / `get_database_ddl` / `get_tablespace_ddl` / `validate_check_constraint`), partition `MERGE` / `SPLIT`, lock + recovery stats, and async-I/O coverage. See the [tool index](../tools.md#tool-index-253-tools) for the shipped surface and [feature-shortlist.md](../feature-shortlist.md) for the remaining items (tracked to GA). |
4848

4949
## Phase 1 — what landed
5050

docs/plans/pg_turboquant-integration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
> (`turboquant_approx_candidates`, `turboquant_rerank_candidates`,
77
> `recommend_turboquant_query_knobs`), `maintain_turboquant_index`
88
> (write), and `create_turboquant_index` / `reindex_turboquant_index`
9-
> (DDL) — see the [tool index](../tools.md#tool-index-252-tools). Kept
9+
> (DDL) — see the [tool index](../tools.md#tool-index-253-tools). Kept
1010
> for design rationale; no longer a live roadmap. Current gaps live in
1111
> [feature-shortlist.md](../feature-shortlist.md).
1212

docs/plans/rag-efficiency-suite.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
> `analyze_topk_stability`, `analyze_rerank_score_distribution`,
77
> `analyze_rerank_ndcg`, `recommend_rerank_strategy`) plus the telemetry
88
> capture/threshold tools — see the
9-
> [tool index](../tools.md#tool-index-252-tools). Kept for design
9+
> [tool index](../tools.md#tool-index-253-tools). Kept for design
1010
> rationale; no longer a live roadmap. Current gaps live in
1111
> [feature-shortlist.md](../feature-shortlist.md).
1212

docs/token-efficiency.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@ raw `EXPLAIN` JSON, which is mostly nested bookkeeping an agent must wade throug
5757

5858
## The break-even — the honest centerpiece
5959

60-
MCPg's full surface is **252 tools**, and every tool definition costs context
61-
tokens on every turn. A "tokens saved" number that ignores that is dishonest,
60+
MCPg's full surface was **252 tools** at the time of this SF1 run (the committed
61+
JSON is that snapshot; the surface grows as tools are added), and every tool
62+
definition costs context tokens on every turn. A "tokens saved" number that
63+
ignores that is dishonest,
6264
and a reviewer would rightly pounce. So we measure it head-on — a bare
6365
`run_select` tool is **193** tokens, and MCPg's surface is far larger. With a
6466
mean per-call saving of ~2,750 tokens per database task, the surface is repaid

docs/tools.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ plumbing:
3737
| `MCPG_NL2SQL_PROVIDER` / `MCPG_NL2SQL_API_KEY` / `MCPG_NL2SQL_MODEL` / `MCPG_NL2SQL_BASE_URL` / `MCPG_NL2SQL_MAX_TOKENS` | `translate_nl_to_sql` provider config — 22 built-in providers + custom (`MCPG_NL2SQL_CUSTOM_PROVIDERS`). |
3838

3939
<!-- BEGIN generated: tool-index (python tools/generate_doc_tables.py --tools) -->
40-
## Tool index (252 tools)
40+
## Tool index (253 tools)
4141

4242
Grouped by feature area. The **Gate** column shows the capability
4343
each tool needs — plain `read` tools are available in every access
@@ -85,7 +85,7 @@ need `unrestricted` **plus** the matching `MCPG_ALLOW_*` opt-in (see
8585
| **PG 19 — stats status** | read | `get_pg19_stats_status` |
8686
| **WarehousePG (MPP)** | read | `get_warehousepg_status`, `list_distribution_policies`, `check_segment_health`, `describe_ao_table`, `list_resource_groups`, `analyze_mpp_query_plan`, `recommend_redistribute` |
8787
| **Logical replication pub/sub** | **DDL** | `create_publication`, `drop_publication`, `create_subscription`, `drop_subscription` |
88-
| **Write & DDL core** | **WRITE** / **DDL** | `run_write`, `run_maintenance`, `cancel_query`, `terminate_backend`, `run_ddl`, `enable_extension`, `dry_run_ddl` |
88+
| **Write & DDL core** | **WRITE** / **DDL** | `run_write`, `run_maintenance`, `clear_cache`, `cancel_query`, `terminate_backend`, `run_ddl`, `enable_extension`, `dry_run_ddl` |
8989
<!-- END generated: tool-index -->
9090

9191
> The reference sections below describe the v0.4.0-era tools in

0 commit comments

Comments
 (0)