From ae75ec4f350565dcd8c3680df1f32a91ac9b2683 Mon Sep 17 00:00:00 2001 From: Devopam Mittra Date: Wed, 14 Oct 2026 10:08:52 +0530 Subject: [PATCH] chore(release): v0.8.3 Bump to 0.8.3 (init, mcpb bundle, server.json), roll CHANGELOG, add release notes, regenerate the tool index / module map / tools.json, bring the hand-written tool counts current, and put the ALTER SYSTEM write tools under the contract fixtures. Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.md | 15 ++- CLAUDE.md | 4 +- README.md | 2 +- docs/index.md | 1 + docs/plans/bm25-integration.md | 2 +- docs/plans/pg_turboquant-integration.md | 2 +- docs/plans/rag-efficiency-suite.md | 2 +- docs/release-notes-0.8.3.md | 75 +++++++++++++ docs/tools.md | 4 +- docs/tour.md | 4 +- docs/user-guide.md | 8 +- packaging/docker-mcp-registry/tools.json | 67 ++++++++++++ packaging/mcpb/manifest.json | 2 +- packaging/mcpb/pyproject.toml | 4 +- server.json | 4 +- src/mcpg/__init__.py | 2 +- src/mcpg/tools.py | 3 + tests/contract/test_describe_tool.py | 1 + tests/contract/test_tool_annotations.py | 1 + tests/contract/test_tool_output_schemas.py | 1 + tests/contract/test_tool_surface_snapshot.py | 1 + tests/contract/tool_surface.snapshot.json | 104 ++++++++++++++++++- tests/unit/test_about.py | 1 + tests/unit/test_tools.py | 3 +- tools/generate_doc_tables.py | 6 +- 25 files changed, 294 insertions(+), 25 deletions(-) create mode 100644 docs/release-notes-0.8.3.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a4d06a..8cc1157 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,19 @@ adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] +## [0.8.3] - 2026-10-14 + +A feature release by content (new tools and opt-in flags, nothing removed or renamed), shipped as a patch per the maintainer's call: the default tool surface only gains five read-only tools, and every new write or admin tool needs an explicit `MCPG_ALLOW_*` flag. See [release notes](docs/release-notes-0.8.3.md). + ### Added - **`ALTER SYSTEM` GUC tools.** `inspect_guc` (read), plus `alter_system`, `alter_system_reset_all` and `reload_config` behind unrestricted mode + `MCPG_ALLOW_ALTER_SYSTEM`. Runs on an autocommit connection, - requires an explicit `confirm=true` after a structured warning, and blocks connectivity/preload GUCs by - default (`MCPG_ALTER_SYSTEM_ALLOWLIST` / `MCPG_ALTER_SYSTEM_DENYLIST`). See `docs/alter-system.md`. (PR #352) + requires `confirm=true` plus a per-statement `confirmation_token`, and blocks connectivity/preload GUCs and + settings that run OS commands or hold credentials (`archive_command`, `restore_command`, `primary_conninfo`, …) by + default (`MCPG_ALTER_SYSTEM_ALLOWLIST` / `MCPG_ALTER_SYSTEM_DENYLIST`). `RESET ALL` enforces that policy on every + current override, `inspect_guc` redacts secret-bearing values, and the write tools refuse to run under + `MCPG_DEFAULT_ROLE` / `MCPG_ALLOWED_ROLES`. See `docs/alter-system.md`. (PR #352) - **TimescaleDB read-only functions are now allowed in the SQL-safety kernel** (#353): `time_bucket`, `time_bucket_gapfill`, `first`/`last`, `locf`, `interpolate`, `histogram`, the `hypertable_*_size` / `*_compression_stats` / `*_columnstore_stats` family, `show_chunks`, `by_range`/`by_hash`, the uuidv7 helpers and `timescaledb_experimental.show_policies`. Mutating @@ -37,6 +44,9 @@ adheres to [Semantic Versioning](https://semver.org/). ### Changed +- **`pglast` 8.4 → 8.5** (PR #350; `uv.lock` updated). The SQL-safety kernel's adversarial and fuzz suites pass on 8.5. +- **The contract fixtures now enable `MCPG_ALLOW_ALTER_SYSTEM` and `MCPG_ALLOW_TIMESCALEDB_ADMIN`**, so the tool-surface + snapshot (now 281 tools), annotations and return-shape contracts cover the ALTER SYSTEM and TimescaleDB admin tools. - **ORM exporters explain why they reject delimited names.** `generate_prisma_schema` and the Drizzle, SQLAlchemy, sqlc, Diesel, jOOQ, Ent and Ecto exporters still accept only plain `[A-Za-z_][A-Za-z0-9_]*` names (the name becomes an identifier in generated source), but their errors and tool descriptions now @@ -44,6 +54,7 @@ adheres to [Semantic Versioning](https://semver.org/). ### Fixed +- `docs/tools.md` tool-index heading and the hand-written tool counts (README, tour, user guide) were stale; regenerated. - **SQL-safety fuzz harness now actually explores the validator.** `sql_safety_fuzzer` decoded its input with `ConsumeUnicodeNoSurrogates`, which turns ASCII SQL into garbage code points, so `pglast` rejected nearly every input at the first token and coverage never grew. It now decodes bytes directly and ships a diff --git a/CLAUDE.md b/CLAUDE.md index 7a99c4a..e17bea6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ stays true; verify anything here against the code before relying on it. ## What MCPg is A production-grade PostgreSQL MCP server. Single-process, async -(`asyncio`), Python 3.12–3.14. **254 MCP tools** (read-only mode exposes +(`asyncio`), Python 3.12–3.14. **279 MCP tools** (read-only mode exposes a subset). Ships to PyPI (`mcpg`), GHCR, the MCP registry, Smithery, and a read-only HF Spaces demo. @@ -28,7 +28,7 @@ a read-only HF Spaces demo. bundle pins are synced by `packaging/mcpb/sync_version.py`; `tests/unit/test_mcpb_bundle.py` enforces the match. A release bumps exactly one line. -- **Tool surface:** `tests/contract/tool_surface.snapshot.json` (256 +- **Tool surface:** `tests/contract/tool_surface.snapshot.json` (281 tools) is the contract. `about.py`'s `tool_count = len(tool_names)`. Two contract snapshots (surface + return shapes) — regenerate with `MCPG_REGENERATE_TOOL_SNAPSHOT=1` / `MCPG_REGENERATE_TOOL_RETURN_SHAPES=1` diff --git a/README.md b/README.md index 609b4b3..407f118 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ **A production-grade [Model Context Protocol](https://modelcontextprotocol.io) server for PostgreSQL.** It lets AI agents safely inspect, query, operate, and -tune a Postgres database — 254 tools spanning catalog introspection, +tune a Postgres database — 279 tools spanning catalog introspection, query intelligence, natural-language SQL, structural diffs, hybrid search, graph queries, data movement, live ops, and more. diff --git a/docs/index.md b/docs/index.md index 1570a50..9caafe1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -64,6 +64,7 @@ them" — pick the section that matches what you're trying to do. retrospectives. - [**Progress log**](PROGRESS.md) — chronological build log. - Release notes: + [v0.8.3](release-notes-0.8.3.md) · [v0.8.2](release-notes-0.8.2.md) · [v0.8.1](release-notes-0.8.1.md) · [v0.8.0](release-notes-0.8.0.md) · diff --git a/docs/plans/bm25-integration.md b/docs/plans/bm25-integration.md index bc22b79..6449af2 100644 --- a/docs/plans/bm25-integration.md +++ b/docs/plans/bm25-integration.md @@ -6,7 +6,7 @@ > `pg_search_parse_query`, `hybrid_bm25_vector_search`, > `create_pg_search_index`, `reindex_pg_search_index`, plus the > observability/advisor tools — see the -> [tool index](../tools.md#tool-index-254-tools). This doc is kept for +> [tool index](../tools.md#tool-index-281-tools). This doc is kept for > its design rationale and the deferred-alternatives (`pg_textsearch`, > `vchord_bm25`) return conditions; it is no longer a live roadmap. > Current gaps live in [feature-shortlist.md](../feature-shortlist.md). diff --git a/docs/plans/pg_turboquant-integration.md b/docs/plans/pg_turboquant-integration.md index 522da8c..3bf74de 100644 --- a/docs/plans/pg_turboquant-integration.md +++ b/docs/plans/pg_turboquant-integration.md @@ -6,7 +6,7 @@ > (`turboquant_approx_candidates`, `turboquant_rerank_candidates`, > `recommend_turboquant_query_knobs`), `maintain_turboquant_index` > (write), and `create_turboquant_index` / `reindex_turboquant_index` -> (DDL) — see the [tool index](../tools.md#tool-index-254-tools). Kept +> (DDL) — see the [tool index](../tools.md#tool-index-281-tools). Kept > for design rationale; no longer a live roadmap. Current gaps live in > [feature-shortlist.md](../feature-shortlist.md). diff --git a/docs/plans/rag-efficiency-suite.md b/docs/plans/rag-efficiency-suite.md index fd0d02b..bfac32d 100644 --- a/docs/plans/rag-efficiency-suite.md +++ b/docs/plans/rag-efficiency-suite.md @@ -6,7 +6,7 @@ > `analyze_topk_stability`, `analyze_rerank_score_distribution`, > `analyze_rerank_ndcg`, `recommend_rerank_strategy`) plus the telemetry > capture/threshold tools — see the -> [tool index](../tools.md#tool-index-254-tools). Kept for design +> [tool index](../tools.md#tool-index-281-tools). Kept for design > rationale; no longer a live roadmap. Current gaps live in > [feature-shortlist.md](../feature-shortlist.md). diff --git a/docs/release-notes-0.8.3.md b/docs/release-notes-0.8.3.md new file mode 100644 index 0000000..5235d53 --- /dev/null +++ b/docs/release-notes-0.8.3.md @@ -0,0 +1,75 @@ +# MCPg v0.8.3 — release notes + +**Released:** 2026-10-14 +**Tool surface:** **279** tools with every `MCPG_ALLOW_*` gate on (281 with +`MCPG_DYNAMIC_SESSION_INTENT` also enabled) — up from 254 / 256 in 0.8.2. +The default read-only surface grows by five read tools (186 → 191); every +other new tool needs an explicit opt-in flag. +**Tests:** 3105 passed, 3 skipped (`tests/unit` + `tests/contract`, snapshots regenerated and committed); plus 7 TimescaleDB integration tests against a live TimescaleDB 2.30.2; lint, `ruff format`, `mypy src/mcpg`, and `bandit` clean. +**Runtime:** Python 3.12–3.14 (`requires-python >=3.12`; CI/mypy target +3.14) + +A **0.8.2 → 0.8.3** release. It adds two tool families (`ALTER SYSTEM` +configuration tools and a much larger TimescaleDB surface), answers +[#353](https://github.com/devopam/MCPg/issues/353), and bumps `pglast`. No +tool was removed or renamed, and no existing default changes. + +## Added: `ALTER SYSTEM` configuration tools (#352) + +`inspect_guc` (always available, read-only), plus `alter_system`, +`alter_system_reset_all` and `reload_config` behind unrestricted mode and +`MCPG_ALLOW_ALTER_SYSTEM`. They run on an autocommit connection (`ALTER SYSTEM` +cannot run in a transaction) and follow a `dry_run` → `confirm=true` + +`confirmation_token` flow, where the token is derived from the exact +statement. Hardened during review: values are quoted safely regardless of +`standard_conforming_strings`; connectivity, preload and command-running +settings (`archive_command`, `restore_command`, `primary_conninfo`, …) are +denied by default; `RESET ALL` applies the allow/deny lists to every current +override and fails closed; `inspect_guc` redacts secret-bearing values; +reload success reflects what `pg_reload_conf()` returns; the tools refuse to +run when `MCPG_DEFAULT_ROLE` / `MCPG_ALLOWED_ROLES` is set. Reference: +[`alter-system.md`](alter-system.md). + +## Added: TimescaleDB (#353, #354) + +- **SQL-safety allowlist:** the read-only functions from the issue + (`time_bucket`, `time_bucket_gapfill`, `first`/`last`, `locf`, `interpolate`, + `histogram`, the size/stats family, `show_chunks`, `by_range`/`by_hash`, the + uuidv7 helpers, `timescaledb_experimental.show_policies`) now pass the + read-only SQL path. Mutating functions, procedures, the restore helpers and + `get_telemetry_report` stay blocked. +- **21 new tools:** reads (`get_hypertable_stats`, `list_timescale_jobs`, + `list_continuous_aggregates`, `get_timescaledb_telemetry_report`); additive + DDL under `MCPG_ALLOW_DDL` (`add_dimension`, `set_chunk_time_interval`, + `add_continuous_aggregate_policy`, `add_reorder_policy`, four + `remove_*_policy`); and admin tools behind the new + `MCPG_ALLOW_TIMESCALEDB_ADMIN` flag with the same `dry_run` / `confirm` + + `confirmation_token` flow (`drop_chunks`, `compress_chunk`, + `decompress_chunk`, `reorder_chunk`, `merge_chunks`, + `refresh_continuous_aggregate`, `run_job`, `alter_job`, `delete_job`). +- **Verified on TimescaleDB 2.30.2.** `refresh_continuous_aggregate`, + `merge_chunks_concurrently` and `reorder_chunk` cannot run in a transaction + block, so they use an autocommit connection and are refused under tenancy; + `get_telemetry_report()` builds its report locally. A new experimental + `TimescaleDB` CI job runs the integration tests. Reference: + [`timescaledb.md`](timescaledb.md); design and test evidence: + [`plans/timescaledb-tools.md`](plans/timescaledb-tools.md). + +## Changed + +- `pglast` 8.4 → 8.5 (#350). The SQL-safety kernel's adversarial and fuzz + suites pass on 8.5. +- The contract fixtures now enable `MCPG_ALLOW_ALTER_SYSTEM` and + `MCPG_ALLOW_TIMESCALEDB_ADMIN`, so the tool-surface snapshot (281 tools), + annotations and return-shape contracts cover the new write tools. + +## Fixed + +- The `docs/tools.md` tool-index heading and the hand-written tool counts + (README, tour, user guide, project notes) were stale and are regenerated. + +## Upgrading + +Nothing to do: the new write and admin tools stay off unless you set +`MCPG_ALLOW_ALTER_SYSTEM` or `MCPG_ALLOW_TIMESCALEDB_ADMIN` (the latter also +needs unrestricted mode and `MCPG_ALLOW_DDL`). diff --git a/docs/tools.md b/docs/tools.md index 490d8b5..5410485 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -37,7 +37,7 @@ plumbing: | `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`). | -## Tool index (257 tools) +## Tool index (281 tools) Grouped by feature area. The **Gate** column shows the capability each tool needs — plain `read` tools are available in every access @@ -54,7 +54,7 @@ need `unrestricted` **plus** the matching `MCPG_ALLOW_*` opt-in (see | **Visualisation & structural diff** | read | `generate_schema_diagram`, `generate_fk_cascade_graph`, `generate_schema_docs`, `compare_schemas` | | **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` | | **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` | -| **Configuration (GUC)** | read | `inspect_guc` | +| **Configuration (GUC)** | read / **unrestricted** + `MCPG_ALLOW_ALTER_SYSTEM` (writes) | `inspect_guc`, `alter_system`, `alter_system_reset_all`, `reload_config` | | **Search** | read | `fuzzy_search`, `full_text_search`, `vector_search`, `vector_range_search`, `mmr_search`, `hybrid_search`, `geo_search` | | **Test-data factory** | read / **WRITE** (`seed_table_with_sample_data`) | `generate_test_data`, `generate_test_row_for`, `seed_table_with_sample_data` | | **Locks & blocking chains** | read | `list_locks`, `find_blocking_chains`, `walk_blocking_chains`, `read_pg_stat_lock`, `analyze_lock_hotspots` | diff --git a/docs/tour.md b/docs/tour.md index a4abb9b..c4ecc1b 100644 --- a/docs/tour.md +++ b/docs/tour.md @@ -6,13 +6,13 @@ surface, not the full catalogue — the exhaustive per-tool reference is in [`tools.md`](tools.md), and task-oriented recipes live in [`cookbook.md`](cookbook.md). -**256 tools** across 19 capability areas as of trunk (254 without the +**281 tools** across 19 capability areas as of trunk (279 without the opt-in `MCPG_DYNAMIC_SESSION_INTENT` flag). Each line shows the tool name + how its parameters land (required first, common defaults after). Capability gates are noted in section titles where they apply. A few specialist areas (PITR, WarehousePG MPP, Redis FDW, SQL/PGQ property graphs, `pg_prewarm`, `pg_repack`, PG 19 runtime -toggles) aren't walked below — see the [tool index](tools.md#tool-index-256-tools) +toggles) aren't walked below — see the [tool index](tools.md#tool-index-281-tools) for those. --- diff --git a/docs/user-guide.md b/docs/user-guide.md index 7362b66..eeeff58 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -39,7 +39,7 @@ first time, skim it as a reference after. ## What MCPg is MCPg is an MCP server that exposes a PostgreSQL database to an AI -agent through a fixed, audited set of **254 tools** (read-only mode exposes ~186). The agent never +agent through a fixed, audited set of **279 tools** (read-only mode exposes ~191). The agent never gets a raw database connection — it can only call the tools MCPg registers, every call is validated, and every call is logged. MCPg runs as a single async process and ships as both a PyPI package @@ -100,8 +100,8 @@ declares two further tool names, `run_ddl` and `run_write`, that only materialize under `MCPG_ACCESS_MODE=unrestricted` — a separate, pre-existing access-mode gate that `session_intent` filtering doesn't touch; under `unrestricted` with every gate on, `core` resolves to 14 -tools instead of 12. The full catalogue tops out at 254 tools at that -same maximal ceiling — 186 is what a real default deployment sees.) +tools instead of 12. The full catalogue tops out at 279 tools at that +same maximal ceiling — 191 is what a real default deployment sees.) ### Growing the surface at runtime: `MCPG_DYNAMIC_SESSION_INTENT` @@ -140,7 +140,7 @@ export MCPG_DYNAMIC_SESSION_INTENT=true # opt-in: sessions can grow past it header; stdio sessions share one implicit key since stdio is inherently single-session-per-process). - The two meta-tools add to the ceiling: the full maximal surface - (every `MCPG_ALLOW_*` gate on) is 254 tools without this flag, 256 + (every `MCPG_ALLOW_*` gate on) is 279 tools without this flag, 281 once `MCPG_DYNAMIC_SESSION_INTENT=1` is also on. ### Security note: visibility, not authorization diff --git a/packaging/docker-mcp-registry/tools.json b/packaging/docker-mcp-registry/tools.json index cca0256..0d64ab9 100644 --- a/packaging/docker-mcp-registry/tools.json +++ b/packaging/docker-mcp-registry/tools.json @@ -190,6 +190,68 @@ } ] }, + { + "name": "alter_system", + "description": "Persist a PostgreSQL setting with ALTER SYSTEM (writes postgresql.auto.conf). NOT transactional \u2014 cannot ROLLBACK. Structured args only (no free-form SQL): name is the GUC, value is the new setting or null to RESET that name. reload is none|if_needed|always (default if_needed). dry_run=true validates and returns a plan without writing. Without confirm=true the tool returns status=confirmation_required with a warning and a confirmation_token \u2014 show the warning to the user, then call again with confirm=true and that exact confirmation_token (bound to this statement) to apply. Postmaster-context GUCs need a restart; sighup-class GUCs can use pg_reload_conf(). A built-in denylist blocks connectivity/preload foot-guns. Requires MCPG_ACCESS_MODE=unrestricted and MCPG_ALLOW_ALTER_SYSTEM=true. Does not extend run_ddl. The DB connection user must be a superuser (or otherwise allowed to ALTER SYSTEM).", + "arguments": [ + { + "name": "confirm", + "type": "boolean", + "desc": "Confirm" + }, + { + "name": "confirmation_token", + "type": "string", + "desc": "Confirmation Token" + }, + { + "name": "dry_run", + "type": "boolean", + "desc": "Dry Run" + }, + { + "name": "name", + "type": "string", + "desc": "Name" + }, + { + "name": "reload", + "type": "string", + "desc": "Reload" + }, + { + "name": "value", + "type": "string", + "desc": "Value" + } + ] + }, + { + "name": "alter_system_reset_all", + "description": "ALTER SYSTEM RESET ALL \u2014 clears every setting previously written via ALTER SYSTEM (postgresql.auto.conf), not postgresql.conf and not session SET. Instance-wide and NOT transactional. Without confirm=true returns confirmation_required + a warning and a confirmation_token (covers the current override set); call again with confirm=true and that token to apply. The allow/deny policy is enforced against every current override. reload defaults to if_needed. Requires unrestricted mode and MCPG_ALLOW_ALTER_SYSTEM. The DB connection user must be a superuser.", + "arguments": [ + { + "name": "confirm", + "type": "boolean", + "desc": "Confirm" + }, + { + "name": "confirmation_token", + "type": "string", + "desc": "Confirmation Token" + }, + { + "name": "dry_run", + "type": "boolean", + "desc": "Dry Run" + }, + { + "name": "reload", + "type": "string", + "desc": "Reload" + } + ] + }, { "name": "analyze_distance_metric", "description": "Recommend a pgvector distance metric (cosine | l2 | inner_product) from the embedding-magnitude distribution. Samples up to `sample_size` non-NULL rows of schema.table.column, computes each embedding's L2 norm, and applies a small heuristic: pre-normalised (CV < 5% and mean \u2248 1.0) \u2192 inner_product; nearly-constant magnitude but not unit-norm \u2192 cosine (same ranking as L2, safer default); variable magnitude \u2192 cosine (normalises out heterogeneous sources). Returns the metric + a rationale + the underlying distribution stats. Reports available=false if the pgvector extension is not installed.", @@ -5058,6 +5120,11 @@ } ] }, + { + "name": "reload_config", + "description": "Call pg_reload_conf() so sighup-class settings written with ALTER SYSTEM (or edit of config files) take effect without a full restart. Returns whether reload succeeded and any settings still marked pending_restart. Requires unrestricted mode and MCPG_ALLOW_ALTER_SYSTEM.", + "arguments": [] + }, { "name": "remove_compression_policy", "description": "Remove a hypertable's compression policy (no-op if none). Already-compressed chunks stay compressed. Requires unrestricted mode + MCPG_ALLOW_DDL.", diff --git a/packaging/mcpb/manifest.json b/packaging/mcpb/manifest.json index b948268..24296d0 100644 --- a/packaging/mcpb/manifest.json +++ b/packaging/mcpb/manifest.json @@ -2,7 +2,7 @@ "manifest_version": "0.4", "name": "mcpg", "display_name": "MCPg \u2014 PostgreSQL", - "version": "0.8.2", + "version": "0.8.3", "description": "Production-grade PostgreSQL MCP server: 254 tools for querying, tuning, and operating Postgres \u2014 read-only by default.", "long_description": "MCPg lets AI agents safely inspect, query, operate, and tune a PostgreSQL database. 254 tools spanning catalog introspection, query intelligence, natural-language SQL, structural diffs, hybrid search, graph queries, live ops, and more.\n\n**Safe by default**: read-only access mode, AST-validated SQL, and every tool publishes MCP annotations (`readOnlyHint`/`destructiveHint`) so the client knows exactly which calls are safe. Write, DDL, shell, and LISTEN capabilities stay off until explicitly enabled via environment variables.\n\nNo data yet? Run `mcpg --demo` against a scratch database to seed a curated demo dataset and take the tools for a spin \u2014 see the guided tour at https://github.com/devopam/MCPg/blob/main/docs/demo.md.", "author": { diff --git a/packaging/mcpb/pyproject.toml b/packaging/mcpb/pyproject.toml index c8ed0fc..ba9d5e2 100644 --- a/packaging/mcpb/pyproject.toml +++ b/packaging/mcpb/pyproject.toml @@ -5,9 +5,9 @@ # to the release tag by the publish workflow, exactly like server.json. [project] name = "mcpg-desktop-extension" -version = "0.8.2" +version = "0.8.3" description = "MCPB launcher shim for the MCPg PostgreSQL MCP server." requires-python = ">=3.12" dependencies = [ - "mcpg==0.8.2", + "mcpg==0.8.3", ] diff --git a/server.json b/server.json index 848d8c5..75a4aca 100644 --- a/server.json +++ b/server.json @@ -1,7 +1,7 @@ { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.devopam/mcpg", - "version": "0.8.2", + "version": "0.8.3", "description": "Production-grade PostgreSQL MCP server \u2014 254 tools for query, tuning & ops, read-only by default.", "websiteUrl": "https://devopam.github.io/MCPg/", "repository": { @@ -13,7 +13,7 @@ "registryType": "pypi", "registryBaseUrl": "https://pypi.org", "identifier": "mcpg", - "version": "0.8.2", + "version": "0.8.3", "runtimeHint": "uvx", "transport": { "type": "stdio" diff --git a/src/mcpg/__init__.py b/src/mcpg/__init__.py index 8256951..8e134ff 100644 --- a/src/mcpg/__init__.py +++ b/src/mcpg/__init__.py @@ -2,7 +2,7 @@ import warnings -__version__ = "0.8.2" +__version__ = "0.8.3" # ``schema`` is the natural field name for "which Postgres schema" across # ~180 tool-return dataclasses. The ``mcp`` SDK dynamically builds a diff --git a/src/mcpg/tools.py b/src/mcpg/tools.py index a169382..ff3b51e 100644 --- a/src/mcpg/tools.py +++ b/src/mcpg/tools.py @@ -7011,6 +7011,8 @@ async def drop_subscription( "partman_run_maintenance", # applies retention config: may detach/drop old partitions "prune_audit_events", "add_retention_policy", # schedules future data deletion + "alter_system", # persists cluster configuration (postgresql.auto.conf) + "alter_system_reset_all", # clears every ALTER SYSTEM override # TimescaleDB admin / policy removal (docs/plans/timescaledb-tools.md). "drop_chunks", # permanently deletes chunks "delete_job", @@ -7031,6 +7033,7 @@ async def drop_subscription( { # Additive object creation (fails on conflict, destroys nothing). "add_compression_policy", + "reload_config", # pg_reload_conf(): re-reads config files, changes no data # TimescaleDB additive / maintenance tools. "add_dimension", "set_chunk_time_interval", diff --git a/tests/contract/test_describe_tool.py b/tests/contract/test_describe_tool.py index 995e1bd..b3e51fc 100644 --- a/tests/contract/test_describe_tool.py +++ b/tests/contract/test_describe_tool.py @@ -32,6 +32,7 @@ def _build_maximal_server() -> MCPServer: "MCPG_ALLOW_SHELL": "true", "MCPG_ALLOW_LISTEN": "true", "MCPG_ALLOW_TIMESCALEDB_ADMIN": "true", + "MCPG_ALLOW_ALTER_SYSTEM": "true", } ) server: MCPServer = MCPServer("mcpg-describe-tool-fixture") diff --git a/tests/contract/test_tool_annotations.py b/tests/contract/test_tool_annotations.py index fc31bae..5a2d9c0 100644 --- a/tests/contract/test_tool_annotations.py +++ b/tests/contract/test_tool_annotations.py @@ -48,6 +48,7 @@ def _build_server(access_mode: str, *, dynamic_session_intent: bool = False) -> "MCPG_ALLOW_SHELL": "true", "MCPG_ALLOW_LISTEN": "true", "MCPG_ALLOW_TIMESCALEDB_ADMIN": "true", + "MCPG_ALLOW_ALTER_SYSTEM": "true", } if access_mode == "unrestricted" else {} diff --git a/tests/contract/test_tool_output_schemas.py b/tests/contract/test_tool_output_schemas.py index 2362249..a21fb8c 100644 --- a/tests/contract/test_tool_output_schemas.py +++ b/tests/contract/test_tool_output_schemas.py @@ -789,6 +789,7 @@ def _build_maximal_server() -> MCPServer: "MCPG_ALLOW_SHELL": "true", "MCPG_ALLOW_LISTEN": "true", "MCPG_ALLOW_TIMESCALEDB_ADMIN": "true", + "MCPG_ALLOW_ALTER_SYSTEM": "true", } ) server: MCPServer = MCPServer("mcpg-output-schemas-fixture") diff --git a/tests/contract/test_tool_surface_snapshot.py b/tests/contract/test_tool_surface_snapshot.py index e2b5a1f..96947e9 100644 --- a/tests/contract/test_tool_surface_snapshot.py +++ b/tests/contract/test_tool_surface_snapshot.py @@ -75,6 +75,7 @@ def _build_maximal_server() -> MCPServer: "MCPG_ALLOW_SHELL": "true", "MCPG_ALLOW_LISTEN": "true", "MCPG_ALLOW_TIMESCALEDB_ADMIN": "true", + "MCPG_ALLOW_ALTER_SYSTEM": "true", "MCPG_DYNAMIC_SESSION_INTENT": "true", } ) diff --git a/tests/contract/tool_surface.snapshot.json b/tests/contract/tool_surface.snapshot.json index 8c3a227..cf93f5b 100644 --- a/tests/contract/tool_surface.snapshot.json +++ b/tests/contract/tool_surface.snapshot.json @@ -1,7 +1,7 @@ { "_meta": { "schema_version": 1, - "tool_count": 278 + "tool_count": 281 }, "tools": [ { @@ -301,6 +301,99 @@ }, "name": "alter_job" }, + { + "description": "Persist a PostgreSQL setting with ALTER SYSTEM (writes postgresql.auto.conf). NOT transactional — cannot ROLLBACK. Structured args only (no free-form SQL): name is the GUC, value is the new setting or null to RESET that name. reload is none|if_needed|always (default if_needed). dry_run=true validates and returns a plan without writing. Without confirm=true the tool returns status=confirmation_required with a warning and a confirmation_token — show the warning to the user, then call again with confirm=true and that exact confirmation_token (bound to this statement) to apply. Postmaster-context GUCs need a restart; sighup-class GUCs can use pg_reload_conf(). A built-in denylist blocks connectivity/preload foot-guns. Requires MCPG_ACCESS_MODE=unrestricted and MCPG_ALLOW_ALTER_SYSTEM=true. Does not extend run_ddl. The DB connection user must be a superuser (or otherwise allowed to ALTER SYSTEM).", + "inputSchema": { + "properties": { + "confirm": { + "default": false, + "title": "Confirm", + "type": "boolean" + }, + "confirmation_token": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Confirmation Token" + }, + "dry_run": { + "default": false, + "title": "Dry Run", + "type": "boolean" + }, + "name": { + "title": "Name", + "type": "string" + }, + "reload": { + "default": "if_needed", + "title": "Reload", + "type": "string" + }, + "value": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Value" + } + }, + "required": [ + "name" + ], + "title": "alter_systemArguments", + "type": "object" + }, + "name": "alter_system" + }, + { + "description": "ALTER SYSTEM RESET ALL — clears every setting previously written via ALTER SYSTEM (postgresql.auto.conf), not postgresql.conf and not session SET. Instance-wide and NOT transactional. Without confirm=true returns confirmation_required + a warning and a confirmation_token (covers the current override set); call again with confirm=true and that token to apply. The allow/deny policy is enforced against every current override. reload defaults to if_needed. Requires unrestricted mode and MCPG_ALLOW_ALTER_SYSTEM. The DB connection user must be a superuser.", + "inputSchema": { + "properties": { + "confirm": { + "default": false, + "title": "Confirm", + "type": "boolean" + }, + "confirmation_token": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Confirmation Token" + }, + "dry_run": { + "default": false, + "title": "Dry Run", + "type": "boolean" + }, + "reload": { + "default": "if_needed", + "title": "Reload", + "type": "string" + } + }, + "title": "alter_system_reset_allArguments", + "type": "object" + }, + "name": "alter_system_reset_all" + }, { "description": "Recommend a pgvector distance metric (cosine | l2 | inner_product) from the embedding-magnitude distribution. Samples up to `sample_size` non-NULL rows of schema.table.column, computes each embedding's L2 norm, and applies a small heuristic: pre-normalised (CV < 5% and mean ≈ 1.0) → inner_product; nearly-constant magnitude but not unit-norm → cosine (same ranking as L2, safer default); variable magnitude → cosine (normalises out heterogeneous sources). Returns the metric + a rationale + the underlying distribution stats. Reports available=false if the pgvector extension is not installed.", "inputSchema": { @@ -8319,6 +8412,15 @@ }, "name": "reindex_turboquant_index" }, + { + "description": "Call pg_reload_conf() so sighup-class settings written with ALTER SYSTEM (or edit of config files) take effect without a full restart. Returns whether reload succeeded and any settings still marked pending_restart. Requires unrestricted mode and MCPG_ALLOW_ALTER_SYSTEM.", + "inputSchema": { + "properties": {}, + "title": "reload_configArguments", + "type": "object" + }, + "name": "reload_config" + }, { "description": "Remove a hypertable's compression policy (no-op if none). Already-compressed chunks stay compressed. Requires unrestricted mode + MCPG_ALLOW_DDL.", "inputSchema": { diff --git a/tests/unit/test_about.py b/tests/unit/test_about.py index bf0210d..823fc74 100644 --- a/tests/unit/test_about.py +++ b/tests/unit/test_about.py @@ -44,6 +44,7 @@ async def _registered_tool_names() -> list[str]: "MCPG_ALLOW_SHELL": "true", "MCPG_ALLOW_LISTEN": "true", "MCPG_ALLOW_TIMESCALEDB_ADMIN": "true", + "MCPG_ALLOW_ALTER_SYSTEM": "true", "MCPG_DYNAMIC_SESSION_INTENT": "true", } ) diff --git a/tests/unit/test_tools.py b/tests/unit/test_tools.py index 8ea7a90..5012305 100644 --- a/tests/unit/test_tools.py +++ b/tests/unit/test_tools.py @@ -575,6 +575,7 @@ async def test_maximal_settings_tool_surface_is_unchanged_with_dynamic_flag_off( "MCPG_ALLOW_SHELL": "true", "MCPG_ALLOW_LISTEN": "true", "MCPG_ALLOW_TIMESCALEDB_ADMIN": "true", + "MCPG_ALLOW_ALTER_SYSTEM": "true", } ) server: MCPServer = MCPServer("mcpg-maximal-parity-fixture") @@ -582,7 +583,7 @@ async def test_maximal_settings_tool_surface_is_unchanged_with_dynamic_flag_off( names = {t.name for t in await server.list_tools()} assert "list_session_intents" not in names assert "enable_session_intent" not in names - assert len(names) == 276 + assert len(names) == 279 async def test_static_session_intent_only_is_unchanged() -> None: diff --git a/tools/generate_doc_tables.py b/tools/generate_doc_tables.py index 9d0cfef..3e230f2 100755 --- a/tools/generate_doc_tables.py +++ b/tools/generate_doc_tables.py @@ -56,7 +56,11 @@ ["@health_rest", "@advisors_rest", "_register_composite"], "read", ), - ("Configuration (GUC)", ["inspect_guc"], "read"), + ( + "Configuration (GUC)", + ["inspect_guc", "alter_system", "alter_system_reset_all", "reload_config"], + "read / **unrestricted** + `MCPG_ALLOW_ALTER_SYSTEM` (writes)", + ), ("Search", ["@search"], "read"), ( "Test-data factory",