Skip to content

Commit 1ab0771

Browse files
authored
feat: run_analytical_query — long-running reads on an isolated pool (#283)
Adds run_analytical_query — a read-only SELECT for genuine analytical work (large aggregations / joins / window functions) that runs on a DEDICATED connection pool isolated from the main 5-slot pool, so a long query can't starve the fast-path tools. Reuses the same pglast allowlist + TenantSqlDriver (SET LOCAL ROLE / RLS) + read-only transaction as run_select, with an elevated, bounded timeout: the isolated pool size is the concurrency cap, a per-call timeout_ms (clamped to the max) is the client budget, and the pool's statement_timeout is the PG ceiling. A run_select timeout points the agent at the tool. Boot-time knobs: MCPG_ENABLE_ANALYTICAL_QUERIES (default true; gates the READ tool), MCPG_ANALYTICAL_TIMEOUT_MS (120000), MCPG_ANALYTICAL_MAX_TIMEOUT_MS (600000), MCPG_ANALYTICAL_MAX_CONCURRENCY (2). Primary DB only for now; the runner is built only in production (mock-DB tests don't spin a real pool). Surface 253 -> 254; snapshots + doc tables regenerated; test_analytical.py added. Full unit+contract suite (2859) + mypy/ruff clean. N/A — reactive capability from the statement-timeout-ceiling analysis.
1 parent 31c7396 commit 1ab0771

22 files changed

Lines changed: 370 additions & 19 deletions

CHANGELOG.md

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

99
### Added
1010

11+
- **`run_analytical_query` — long-running reads on an isolated pool.**
12+
`run_select` is bounded to a short (~30s) timeout on the shared 5-connection
13+
pool so an agent can't pin a connection with a runaway query — which made
14+
genuine analytical queries (large aggregations / joins / window functions)
15+
infeasible, and `MCPG_STATEMENT_TIMEOUT_MS` didn't help (it moves Postgres's
16+
`statement_timeout` but not the client-side asyncio cap). The new tool runs a
17+
read-only SELECT through the **same** allowlist + tenancy/RLS + read-only
18+
transaction, but on a **dedicated connection pool** isolated from the main
19+
one, with an **elevated, bounded** timeout — so a slow query can never starve
20+
the fast-path tools. A per-call `timeout_ms` (clamped to the max) and optional
21+
`work_mem` are exposed; a `run_select` timeout now points the agent at the
22+
tool. Boot-time knobs: `MCPG_ENABLE_ANALYTICAL_QUERIES` (default true, gates
23+
the tool — it's a READ tool, so set false to withdraw it from an anonymous
24+
read-only deployment), `MCPG_ANALYTICAL_TIMEOUT_MS` (default 120000),
25+
`MCPG_ANALYTICAL_MAX_TIMEOUT_MS` (600000), `MCPG_ANALYTICAL_MAX_CONCURRENCY`
26+
(2 — the isolated pool size, which is also the concurrency cap). Primary
27+
database only for now. Tool surface **253 → 254**.
28+
1129
- **Cache-freshness controls for out-of-band schema changes.** MCPg's read
1230
cache is invalidated automatically by MCPg's own write/DDL tools, but it
1331
could serve stale introspection/advisor results for up to

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. **253 MCP tools** (read-only mode exposes
9+
(`asyncio`), Python 3.12–3.14. **254 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 — 253 tools spanning catalog introspection,
7+
tune a Postgres database — 254 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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ flowchart TD
6464
---
6565

6666
<!-- BEGIN generated: module-map (python tools/generate_doc_tables.py --modules) -->
67-
## Module map (103 modules)
67+
## Module map (104 modules)
6868

6969
Every `mcpg.*` module and what it owns, alphabetical. The layered
7070
request path through these lives in the [Overview](#overview) diagram;
@@ -76,6 +76,7 @@ this table is the exhaustive index. Regenerate with
7676
| `mcpg.about` | MCPg self-description. |
7777
| `mcpg.advisors` | Schema advisors — codified lint rules over the PG catalog. |
7878
| `mcpg.aio` | `AIO` — PG 19 asynchronous-I/O subsystem coverage. |
79+
| `mcpg.analytical` | Long-running analytical read path — an isolated pool, capped concurrency. |
7980
| `mcpg.audit` | Audit logging of tool invocations and DBA database performance checks. |
8081
| `mcpg.audit_integrity` | Audit trail verification utility. |
8182
| `mcpg.audit_nl2sql` | NL→SQL audit table — partitioned, compressed, RLS-gated. |

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-253-tools). This doc is kept for
9+
> [tool index](../tools.md#tool-index-254-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-253-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-254-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-253-tools). Kept
9+
> (DDL) — see the [tool index](../tools.md#tool-index-254-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-253-tools). Kept for design
9+
> [tool index](../tools.md#tool-index-254-tools). Kept for design
1010
> rationale; no longer a live roadmap. Current gaps live in
1111
> [feature-shortlist.md](../feature-shortlist.md).
1212

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 (253 tools)
40+
## Tool index (254 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
@@ -51,7 +51,7 @@ need `unrestricted` **plus** the matching `MCPG_ALLOW_*` opt-in (see
5151
| **Catalog — schemas / tables / columns** | read | `list_schemas`, `list_tables`, `describe_table`, `list_indexes`, `list_constraints`, `list_foreign_keys`, `list_views`, `list_functions`, `list_triggers`, `list_partitions`, `list_roles`, `list_grants`, `list_policies`, `list_sequences`, `list_enums`, `list_domains`, `list_composite_types`, `list_foreign_data_wrappers`, `list_foreign_servers`, `list_foreign_tables`, `list_user_mappings`, `list_publications`, `list_subscriptions`, `list_extensions`, `list_available_extensions`, `list_generated_columns` |
5252
| **Catalog — compact** | read | `get_compact_schema` |
5353
| **Visualisation & structural diff** | read | `generate_schema_diagram`, `generate_fk_cascade_graph`, `generate_schema_docs`, `compare_schemas` |
54-
| **Query & cursors** | read | `run_select`, `run_select_tuned`, `run_select_parallel`, `open_cursor`, `fetch_cursor`, `close_cursor`, `list_cursors`, `explain_query`, `analyze_query_plan`, `translate_nl_to_sql` |
54+
| **Query & cursors** | read | `run_select`, `run_select_tuned`, `run_select_parallel`, `open_cursor`, `fetch_cursor`, `close_cursor`, `list_cursors`, `explain_query`, `analyze_query_plan`, `translate_nl_to_sql`, `run_analytical_query` |
5555
| **Health, tuning & advisors** | read | `check_database_health`, `analyze_table_bloat`, `list_databases`, `audit_database`, `analyze_workload`, `detect_n_plus_one`, `read_autovacuum_priority`, `recommend_indexes`, `recommend_index_drops`, `run_advisors`, `find_unused_objects`, `find_sensitive_columns`, `lint_naming_conventions`, `test_rls_for_role`, `analyze_session_cost`, `recommend_headline_tools`, `audit_sequences`, `audit_settings`, `recommend_postgres_conf`, `optimize_query`, `summarize_table`, `why_is_this_slow` |
5656
| **Search** | read | `fuzzy_search`, `full_text_search`, `vector_search`, `vector_range_search`, `mmr_search`, `hybrid_search`, `geo_search` |
5757
| **Test-data factory** | read / **WRITE** (`seed_table_with_sample_data`) | `generate_test_data`, `generate_test_row_for`, `seed_table_with_sample_data` |

docs/tour.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ surface, not the full catalogue — the exhaustive per-tool reference is
66
in [`tools.md`](tools.md), and task-oriented recipes live in
77
[`cookbook.md`](cookbook.md).
88

9-
**253 tools** across 19 capability areas as of trunk. Each line shows
9+
**254 tools** across 19 capability areas as of trunk. Each line shows
1010
the tool name + how its parameters land (required first, common
1111
defaults after). Capability gates are noted in section titles where
1212
they apply. A few specialist areas (PITR, WarehousePG MPP, Redis FDW,
1313
SQL/PGQ property graphs, `pg_prewarm`, `pg_repack`, PG 19 runtime
14-
toggles) aren't walked below — see the [tool index](tools.md#tool-index-253-tools)
14+
toggles) aren't walked below — see the [tool index](tools.md#tool-index-254-tools)
1515
for those.
1616

1717
---

0 commit comments

Comments
 (0)