postgres-aiops 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. postgres_aiops-0.1.0/.github/workflows/publish.yml +26 -0
  2. postgres_aiops-0.1.0/.gitignore +7 -0
  3. postgres_aiops-0.1.0/CHANGELOG.md +62 -0
  4. postgres_aiops-0.1.0/LICENSE +21 -0
  5. postgres_aiops-0.1.0/PKG-INFO +119 -0
  6. postgres_aiops-0.1.0/README.md +103 -0
  7. postgres_aiops-0.1.0/RELEASE_NOTES.md +52 -0
  8. postgres_aiops-0.1.0/SECURITY.md +89 -0
  9. postgres_aiops-0.1.0/mcp_server/__init__.py +1 -0
  10. postgres_aiops-0.1.0/mcp_server/_shared.py +101 -0
  11. postgres_aiops-0.1.0/mcp_server/server.py +37 -0
  12. postgres_aiops-0.1.0/mcp_server/tools/__init__.py +1 -0
  13. postgres_aiops-0.1.0/mcp_server/tools/activity.py +52 -0
  14. postgres_aiops-0.1.0/mcp_server/tools/analysis.py +89 -0
  15. postgres_aiops-0.1.0/mcp_server/tools/indexes.py +65 -0
  16. postgres_aiops-0.1.0/mcp_server/tools/queries.py +60 -0
  17. postgres_aiops-0.1.0/mcp_server/tools/remediation.py +281 -0
  18. postgres_aiops-0.1.0/mcp_server/tools/replication.py +43 -0
  19. postgres_aiops-0.1.0/mcp_server/tools/server.py +86 -0
  20. postgres_aiops-0.1.0/mcp_server/tools/tables.py +46 -0
  21. postgres_aiops-0.1.0/postgres_aiops/__init__.py +9 -0
  22. postgres_aiops-0.1.0/postgres_aiops/cli/__init__.py +9 -0
  23. postgres_aiops-0.1.0/postgres_aiops/cli/_common.py +78 -0
  24. postgres_aiops-0.1.0/postgres_aiops/cli/_root.py +68 -0
  25. postgres_aiops-0.1.0/postgres_aiops/cli/activity.py +52 -0
  26. postgres_aiops-0.1.0/postgres_aiops/cli/analyze.py +53 -0
  27. postgres_aiops-0.1.0/postgres_aiops/cli/doctor.py +21 -0
  28. postgres_aiops-0.1.0/postgres_aiops/cli/index.py +55 -0
  29. postgres_aiops-0.1.0/postgres_aiops/cli/init.py +112 -0
  30. postgres_aiops-0.1.0/postgres_aiops/cli/overview.py +16 -0
  31. postgres_aiops-0.1.0/postgres_aiops/cli/query.py +70 -0
  32. postgres_aiops-0.1.0/postgres_aiops/cli/remediate.py +186 -0
  33. postgres_aiops-0.1.0/postgres_aiops/cli/replication.py +45 -0
  34. postgres_aiops-0.1.0/postgres_aiops/cli/secret.py +103 -0
  35. postgres_aiops-0.1.0/postgres_aiops/cli/server.py +69 -0
  36. postgres_aiops-0.1.0/postgres_aiops/cli/table.py +45 -0
  37. postgres_aiops-0.1.0/postgres_aiops/config.py +154 -0
  38. postgres_aiops-0.1.0/postgres_aiops/connection.py +178 -0
  39. postgres_aiops-0.1.0/postgres_aiops/doctor.py +84 -0
  40. postgres_aiops-0.1.0/postgres_aiops/governance/__init__.py +40 -0
  41. postgres_aiops-0.1.0/postgres_aiops/governance/audit.py +377 -0
  42. postgres_aiops-0.1.0/postgres_aiops/governance/budget.py +225 -0
  43. postgres_aiops-0.1.0/postgres_aiops/governance/decorators.py +474 -0
  44. postgres_aiops-0.1.0/postgres_aiops/governance/paths.py +23 -0
  45. postgres_aiops-0.1.0/postgres_aiops/governance/patterns.py +378 -0
  46. postgres_aiops-0.1.0/postgres_aiops/governance/policy.py +411 -0
  47. postgres_aiops-0.1.0/postgres_aiops/governance/sanitize.py +39 -0
  48. postgres_aiops-0.1.0/postgres_aiops/governance/undo.py +218 -0
  49. postgres_aiops-0.1.0/postgres_aiops/ops/__init__.py +1 -0
  50. postgres_aiops-0.1.0/postgres_aiops/ops/_util.py +102 -0
  51. postgres_aiops-0.1.0/postgres_aiops/ops/activity.py +193 -0
  52. postgres_aiops-0.1.0/postgres_aiops/ops/analysis.py +263 -0
  53. postgres_aiops-0.1.0/postgres_aiops/ops/indexes.py +211 -0
  54. postgres_aiops-0.1.0/postgres_aiops/ops/overview.py +51 -0
  55. postgres_aiops-0.1.0/postgres_aiops/ops/queries.py +123 -0
  56. postgres_aiops-0.1.0/postgres_aiops/ops/remediation.py +237 -0
  57. postgres_aiops-0.1.0/postgres_aiops/ops/replication.py +144 -0
  58. postgres_aiops-0.1.0/postgres_aiops/ops/server.py +151 -0
  59. postgres_aiops-0.1.0/postgres_aiops/ops/tables.py +146 -0
  60. postgres_aiops-0.1.0/postgres_aiops/secretstore.py +302 -0
  61. postgres_aiops-0.1.0/pyproject.toml +59 -0
  62. postgres_aiops-0.1.0/server.json +21 -0
  63. postgres_aiops-0.1.0/skills/postgres-aiops/SKILL.md +116 -0
  64. postgres_aiops-0.1.0/skills/postgres-aiops/references/capabilities.md +66 -0
  65. postgres_aiops-0.1.0/skills/postgres-aiops/references/cli-reference.md +76 -0
  66. postgres_aiops-0.1.0/skills/postgres-aiops/references/setup-guide.md +97 -0
  67. postgres_aiops-0.1.0/smithery.yaml +9 -0
  68. postgres_aiops-0.1.0/tests/conftest.py +54 -0
  69. postgres_aiops-0.1.0/tests/test_analysis.py +84 -0
  70. postgres_aiops-0.1.0/tests/test_connection.py +105 -0
  71. postgres_aiops-0.1.0/tests/test_reads.py +179 -0
  72. postgres_aiops-0.1.0/tests/test_secretstore.py +99 -0
  73. postgres_aiops-0.1.0/tests/test_smoke.py +216 -0
  74. postgres_aiops-0.1.0/tests/test_writes.py +159 -0
  75. postgres_aiops-0.1.0/uv.lock +1040 -0
@@ -0,0 +1,26 @@
1
+ name: Publish to PyPI
2
+
3
+ # Trusted Publishing (OIDC) — publishes from GitHub's runners with no API token,
4
+ # sidestepping the local-IP / account new-project rate limit. Configure a matching
5
+ # "trusted publisher" for this package on PyPI (see the repo release notes).
6
+ on:
7
+ release:
8
+ types: [published]
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ publish:
16
+ runs-on: ubuntu-latest
17
+ permissions:
18
+ id-token: write # required for PyPI Trusted Publishing (OIDC)
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - name: Set up uv
22
+ uses: astral-sh/setup-uv@v5
23
+ - name: Build sdist + wheel
24
+ run: uv build
25
+ - name: Publish to PyPI (Trusted Publishing)
26
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ dist/
3
+ __pycache__/
4
+ *.pyc
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ *.egg-info/
@@ -0,0 +1,62 @@
1
+ # Changelog
2
+
3
+ All notable changes to postgres-aiops are documented here. Format loosely follows
4
+ [Keep a Changelog](https://keepachangelog.com/); this project uses semantic
5
+ versioning (currently 0.x preview — the API may change).
6
+
7
+ ## [0.1.0] — 2026-07-13
8
+
9
+ Initial preview release: governed AI-ops for **PostgreSQL DBA operations** —
10
+ connecting via **psycopg 3** and reading the system catalogs and `pg_stat_*`
11
+ views — with a bundled governance harness. **Mock-validated only — not run
12
+ against a live cluster.** Community-maintained; not affiliated with the
13
+ PostgreSQL project.
14
+
15
+ ### Added
16
+
17
+ - **psycopg 3 connection layer** (`postgres_aiops.connection`) — parameterised
18
+ reads with a `dict_row` factory, autocommit for maintenance statements
19
+ (VACUUM / CONCURRENTLY / REINDEX), an injectable connection for tests, and
20
+ teaching error translation (`PgError`, with connect/permission/missing-view
21
+ hints).
22
+ - **33 governed MCP tools**, every one wrapped with `@governed_tool`:
23
+ - **Overview** — `overview` (one-shot cluster health snapshot).
24
+ - **Server** — `server_version`, `show_settings`, `list_extensions`,
25
+ `list_databases`, `list_roles`.
26
+ - **Activity** — `list_activity`, `long_running_queries`, `list_locks`.
27
+ - **Queries** — `top_queries` (pg_stat_statements), `explain_query`.
28
+ - **Indexes** — `unused_indexes`, `missing_index_hints`, `index_bloat`,
29
+ `invalid_indexes`.
30
+ - **Tables** — `table_sizes`, `table_bloat`, `autovacuum_status`.
31
+ - **Replication** — `replication_status`, `replication_slots`, `wal_status`.
32
+ - **Analysis (flagship)** — `slow_query_rca`, `bloat_and_vacuum_analysis`,
33
+ `blocking_lock_chain_rca`.
34
+ - **Writes** — `terminate_backend` (high), `cancel_query` (high),
35
+ `drop_index` (high), `run_vacuum` (medium), `run_analyze` (medium),
36
+ `create_index` (medium), `reindex` (medium), `update_setting` (medium),
37
+ `reset_query_stats` (medium).
38
+ - **Guarded writes** — every write supports a `dry_run` preview and (at the CLI)
39
+ double confirmation. Reversible writes fetch the **real before-state** and
40
+ record a faithful inverse: `create_index`↔`drop_index` (drop captures
41
+ `pg_get_indexdef` so undo recreates it exactly); `update_setting` captures the
42
+ prior value. Irreversible ops record prior stats for audit but no undo.
43
+ - **SQL-injection defenses** — all values are bound query parameters; the few
44
+ identifiers that cannot be parameterised (table/index/GUC names, ORDER BY
45
+ columns, index methods) are validated against strict allow-lists and quoted
46
+ before interpolation.
47
+ - **Bundled governance harness** (`postgres_aiops.governance`) — audit log, policy
48
+ engine, token/runaway budget guard, undo-token recording, graduated risk tiers,
49
+ prompt-injection `sanitize`. State under `~/.postgres-aiops/` (relocatable via
50
+ `POSTGRES_AIOPS_HOME`).
51
+ - **Encrypted secret store** — role passwords in `~/.postgres-aiops/secrets.enc`
52
+ (Fernet + scrypt); legacy `PG_<TARGET>_PASSWORD` env fallback + `secret migrate`.
53
+ - **CLI** — `init` wizard, `overview`, `server`, `activity`, `query`, `index`,
54
+ `table`, `repl`, `analyze`, `remediate`, `secret`, `doctor`, `mcp`.
55
+
56
+ ### Known limitations
57
+
58
+ - Preview / mock-only: catalog and `pg_stat_*` queries need live verification.
59
+ - `top_queries` / `slow_query_rca` require the `pg_stat_statements` extension.
60
+ - Coverage is a curated subset of PostgreSQL's surface; open an issue/PR for gaps.
61
+
62
+ [0.1.0]: https://github.com/AIops-tools/Postgres-AIops/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wei <zhouwei008@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.4
2
+ Name: postgres-aiops
3
+ Version: 0.1.0
4
+ Summary: Governed AI-ops for PostgreSQL DBA operations: slow-query RCA, bloat/vacuum & blocking-lock analysis with a built-in governance harness (audit, budget, undo, risk tiers)
5
+ Author-email: wei <zhouwei008@gmail.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.11
9
+ Requires-Dist: cryptography>=42.0
10
+ Requires-Dist: mcp[cli]<2.0,>=1.10
11
+ Requires-Dist: psycopg[binary]<4.0,>=3.1
12
+ Requires-Dist: pyyaml<7.0,>=6.0
13
+ Requires-Dist: rich<16.0,>=13.0
14
+ Requires-Dist: typer<1.0,>=0.12
15
+ Description-Content-Type: text/markdown
16
+
17
+ <!-- mcp-name: io.github.AIops-tools/postgres-aiops -->
18
+
19
+ # Postgres AIops (preview)
20
+
21
+ > **Disclaimer**: Community-maintained open-source project. **Not affiliated with, endorsed by, or sponsored by the PostgreSQL Global Development Group or any vendor.** "PostgreSQL" and the elephant logo are trademarks of the PostgreSQL Community Association; all product/trademark names belong to their respective owners. MIT licensed.
22
+
23
+ Governed AI-ops for **PostgreSQL DBA operations** — connecting to a server with
24
+ **psycopg 3** and reading the system catalogs and `pg_stat_*` views — with a
25
+ **built-in governance harness**: unified audit log, policy engine, token/runaway
26
+ budget guard, undo-token recording, and graduated-autonomy risk tiers.
27
+ **Preview — mock-validated only, not run against a live cluster.**
28
+
29
+ ## What it does
30
+
31
+ Three flagship signature analyses, plus the guarded reads and writes around them:
32
+
33
+ - **Slow-query RCA** — take the worst `pg_stat_statements` entry (plus an optional
34
+ `EXPLAIN` plan) and map its numbers — mean time, cache-hit ratio, temp spill,
35
+ call count, plan node types — to a cited cause and a concrete action. Every
36
+ finding carries its measured number, not a black-box verdict.
37
+ - **Bloat & vacuum analysis** — combine per-table dead-tuple ratio and autovacuum
38
+ recency into a ranked, cited recommendation (VACUUM / tune autovacuum).
39
+ - **Blocking lock-chain RCA** — build the wait-for tree from `pg_blocking_pids`,
40
+ name the **root blocker** (blocks others, waits on none), and give the action;
41
+ a cycle is flagged as a likely deadlock.
42
+
43
+ ## What works
44
+
45
+ - **CLI** (`postgres-aiops ...`): `init`, `overview`, `server`, `activity`, `query`, `index`, `table`, `repl`, `analyze`, `remediate`, `secret`, `doctor`, `mcp`.
46
+ - **MCP server** (`postgres-aiops mcp` or `postgres-aiops-mcp`): **33 tools** (24 read, 9 write), every one wrapped with the bundled `@governed_tool` harness.
47
+ - **Encrypted credentials**: the role password lives in an encrypted store `~/.postgres-aiops/secrets.enc` (Fernet + scrypt) — **never plaintext on disk**. Unlock with a master password from `POSTGRES_AIOPS_MASTER_PASSWORD` (MCP/CI) or an interactive prompt (CLI).
48
+ - **Reversibility**: mutating writes fetch the **real before-state first** and record a faithful inverse — `create_index`↔`drop_index`; `drop_index` captures `pg_get_indexdef` so undo recreates it exactly; `update_setting` captures the prior value so undo sets it back. Irreversible ops (`terminate_backend`, `cancel_query`, `run_vacuum`, `run_analyze`, `reindex`, `reset_query_stats`) record prior stats for audit but declare no undo.
49
+ - **Safety**: every state-changing CLI op supports `--dry-run` and requires double confirmation; every write MCP tool takes a `dry_run` preview. All identifiers that cannot be parameterised (table/index/GUC names) are validated and quoted; all values are bound query parameters.
50
+
51
+ ## Capability matrix (33 MCP tools)
52
+
53
+ | Domain | Tools | Count | R/W |
54
+ |--------|-------|:-----:|:---:|
55
+ | **Overview** | `overview` | 1 | read |
56
+ | **Server** | `server_version`, `show_settings`, `list_extensions`, `list_databases`, `list_roles` | 5 | read |
57
+ | **Activity** | `list_activity`, `long_running_queries`, `list_locks` | 3 | read |
58
+ | **Queries** | `top_queries`, `explain_query` | 2 | read |
59
+ | **Indexes** | `unused_indexes`, `missing_index_hints`, `index_bloat`, `invalid_indexes` | 4 | read |
60
+ | **Tables** | `table_sizes`, `table_bloat`, `autovacuum_status` | 3 | read |
61
+ | **Replication** | `replication_status`, `replication_slots`, `wal_status` | 3 | read |
62
+ | **Analysis (flagship)** | `slow_query_rca`, `bloat_and_vacuum_analysis`, `blocking_lock_chain_rca` | 3 | read |
63
+ | **Writes** | `terminate_backend`, `cancel_query`, `drop_index` | 3 | write (high) |
64
+ | | `run_vacuum`, `run_analyze`, `create_index`, `reindex`, `update_setting`, `reset_query_stats` | 6 | write (medium) |
65
+
66
+ The flagship analyses accept injected records for pure/offline analysis, or pull
67
+ live from a configured target. `top_queries`/`slow_query_rca` require the
68
+ `pg_stat_statements` extension; the read role should have `pg_monitor`.
69
+
70
+ ## Quick start
71
+
72
+ ```bash
73
+ uv tool install postgres-aiops # or: pipx install postgres-aiops
74
+ postgres-aiops init # wizard: add a target + store the password (encrypted)
75
+ postgres-aiops doctor # verify config, secrets, connectivity
76
+ postgres-aiops overview # one-shot cluster health snapshot
77
+ postgres-aiops analyze slow-query # RCA the worst pg_stat_statements entry
78
+ postgres-aiops table bloat # dead-tuple bloat proxy per table
79
+ ```
80
+
81
+ Run as an MCP server (stdio):
82
+
83
+ ```bash
84
+ export POSTGRES_AIOPS_MASTER_PASSWORD=... # unlock secrets non-interactively
85
+ postgres-aiops-mcp
86
+ ```
87
+
88
+ ## Governance
89
+
90
+ Every MCP tool passes through the bundled `@governed_tool` harness:
91
+
92
+ - **Audit** — every call (params, result, status, duration, risk tier, approver,
93
+ rationale) is logged to `~/.postgres-aiops/audit.db` (relocatable via
94
+ `POSTGRES_AIOPS_HOME`).
95
+ - **Budget / runaway guard** — token and call budgets trip a circuit breaker.
96
+ - **Risk tiers** — graduated autonomy; high-risk ops can require a named approver
97
+ (`POSTGRES_AUDIT_APPROVED_BY` / `POSTGRES_AUDIT_RATIONALE` — the env-var names
98
+ the bundled harness reads).
99
+ - **Undo recording** — reversible writes record an inverse descriptor built from
100
+ the fetched before-state.
101
+
102
+ ## Scope
103
+
104
+ This is the **PostgreSQL DBA-ops** member of the AIops-tools family (governed
105
+ AI-ops with audit + budget + undo + risk tiers). Do **NOT** use it for OT /
106
+ industrial edge (Modbus, OPC-UA, PROFINET) — see the separate `industrial-aiops`
107
+ line — nor for application-schema migrations or ORM management.
108
+
109
+ ## Missing a capability?
110
+
111
+ Coverage is intentionally a curated subset of PostgreSQL's catalogs and
112
+ maintenance surface. Missing a view, a metric, or a maintenance command? **Open
113
+ an issue or PR** — contributions welcome.
114
+
115
+ ## Status
116
+
117
+ **Preview — mock-validated only, not run against a live cluster.** The catalog
118
+ queries are modelled from the documented `pg_catalog` / `pg_stat_*` shapes and
119
+ need live verification. `postgres-aiops doctor` is the fastest live check.
@@ -0,0 +1,103 @@
1
+ <!-- mcp-name: io.github.AIops-tools/postgres-aiops -->
2
+
3
+ # Postgres AIops (preview)
4
+
5
+ > **Disclaimer**: Community-maintained open-source project. **Not affiliated with, endorsed by, or sponsored by the PostgreSQL Global Development Group or any vendor.** "PostgreSQL" and the elephant logo are trademarks of the PostgreSQL Community Association; all product/trademark names belong to their respective owners. MIT licensed.
6
+
7
+ Governed AI-ops for **PostgreSQL DBA operations** — connecting to a server with
8
+ **psycopg 3** and reading the system catalogs and `pg_stat_*` views — with a
9
+ **built-in governance harness**: unified audit log, policy engine, token/runaway
10
+ budget guard, undo-token recording, and graduated-autonomy risk tiers.
11
+ **Preview — mock-validated only, not run against a live cluster.**
12
+
13
+ ## What it does
14
+
15
+ Three flagship signature analyses, plus the guarded reads and writes around them:
16
+
17
+ - **Slow-query RCA** — take the worst `pg_stat_statements` entry (plus an optional
18
+ `EXPLAIN` plan) and map its numbers — mean time, cache-hit ratio, temp spill,
19
+ call count, plan node types — to a cited cause and a concrete action. Every
20
+ finding carries its measured number, not a black-box verdict.
21
+ - **Bloat & vacuum analysis** — combine per-table dead-tuple ratio and autovacuum
22
+ recency into a ranked, cited recommendation (VACUUM / tune autovacuum).
23
+ - **Blocking lock-chain RCA** — build the wait-for tree from `pg_blocking_pids`,
24
+ name the **root blocker** (blocks others, waits on none), and give the action;
25
+ a cycle is flagged as a likely deadlock.
26
+
27
+ ## What works
28
+
29
+ - **CLI** (`postgres-aiops ...`): `init`, `overview`, `server`, `activity`, `query`, `index`, `table`, `repl`, `analyze`, `remediate`, `secret`, `doctor`, `mcp`.
30
+ - **MCP server** (`postgres-aiops mcp` or `postgres-aiops-mcp`): **33 tools** (24 read, 9 write), every one wrapped with the bundled `@governed_tool` harness.
31
+ - **Encrypted credentials**: the role password lives in an encrypted store `~/.postgres-aiops/secrets.enc` (Fernet + scrypt) — **never plaintext on disk**. Unlock with a master password from `POSTGRES_AIOPS_MASTER_PASSWORD` (MCP/CI) or an interactive prompt (CLI).
32
+ - **Reversibility**: mutating writes fetch the **real before-state first** and record a faithful inverse — `create_index`↔`drop_index`; `drop_index` captures `pg_get_indexdef` so undo recreates it exactly; `update_setting` captures the prior value so undo sets it back. Irreversible ops (`terminate_backend`, `cancel_query`, `run_vacuum`, `run_analyze`, `reindex`, `reset_query_stats`) record prior stats for audit but declare no undo.
33
+ - **Safety**: every state-changing CLI op supports `--dry-run` and requires double confirmation; every write MCP tool takes a `dry_run` preview. All identifiers that cannot be parameterised (table/index/GUC names) are validated and quoted; all values are bound query parameters.
34
+
35
+ ## Capability matrix (33 MCP tools)
36
+
37
+ | Domain | Tools | Count | R/W |
38
+ |--------|-------|:-----:|:---:|
39
+ | **Overview** | `overview` | 1 | read |
40
+ | **Server** | `server_version`, `show_settings`, `list_extensions`, `list_databases`, `list_roles` | 5 | read |
41
+ | **Activity** | `list_activity`, `long_running_queries`, `list_locks` | 3 | read |
42
+ | **Queries** | `top_queries`, `explain_query` | 2 | read |
43
+ | **Indexes** | `unused_indexes`, `missing_index_hints`, `index_bloat`, `invalid_indexes` | 4 | read |
44
+ | **Tables** | `table_sizes`, `table_bloat`, `autovacuum_status` | 3 | read |
45
+ | **Replication** | `replication_status`, `replication_slots`, `wal_status` | 3 | read |
46
+ | **Analysis (flagship)** | `slow_query_rca`, `bloat_and_vacuum_analysis`, `blocking_lock_chain_rca` | 3 | read |
47
+ | **Writes** | `terminate_backend`, `cancel_query`, `drop_index` | 3 | write (high) |
48
+ | | `run_vacuum`, `run_analyze`, `create_index`, `reindex`, `update_setting`, `reset_query_stats` | 6 | write (medium) |
49
+
50
+ The flagship analyses accept injected records for pure/offline analysis, or pull
51
+ live from a configured target. `top_queries`/`slow_query_rca` require the
52
+ `pg_stat_statements` extension; the read role should have `pg_monitor`.
53
+
54
+ ## Quick start
55
+
56
+ ```bash
57
+ uv tool install postgres-aiops # or: pipx install postgres-aiops
58
+ postgres-aiops init # wizard: add a target + store the password (encrypted)
59
+ postgres-aiops doctor # verify config, secrets, connectivity
60
+ postgres-aiops overview # one-shot cluster health snapshot
61
+ postgres-aiops analyze slow-query # RCA the worst pg_stat_statements entry
62
+ postgres-aiops table bloat # dead-tuple bloat proxy per table
63
+ ```
64
+
65
+ Run as an MCP server (stdio):
66
+
67
+ ```bash
68
+ export POSTGRES_AIOPS_MASTER_PASSWORD=... # unlock secrets non-interactively
69
+ postgres-aiops-mcp
70
+ ```
71
+
72
+ ## Governance
73
+
74
+ Every MCP tool passes through the bundled `@governed_tool` harness:
75
+
76
+ - **Audit** — every call (params, result, status, duration, risk tier, approver,
77
+ rationale) is logged to `~/.postgres-aiops/audit.db` (relocatable via
78
+ `POSTGRES_AIOPS_HOME`).
79
+ - **Budget / runaway guard** — token and call budgets trip a circuit breaker.
80
+ - **Risk tiers** — graduated autonomy; high-risk ops can require a named approver
81
+ (`POSTGRES_AUDIT_APPROVED_BY` / `POSTGRES_AUDIT_RATIONALE` — the env-var names
82
+ the bundled harness reads).
83
+ - **Undo recording** — reversible writes record an inverse descriptor built from
84
+ the fetched before-state.
85
+
86
+ ## Scope
87
+
88
+ This is the **PostgreSQL DBA-ops** member of the AIops-tools family (governed
89
+ AI-ops with audit + budget + undo + risk tiers). Do **NOT** use it for OT /
90
+ industrial edge (Modbus, OPC-UA, PROFINET) — see the separate `industrial-aiops`
91
+ line — nor for application-schema migrations or ORM management.
92
+
93
+ ## Missing a capability?
94
+
95
+ Coverage is intentionally a curated subset of PostgreSQL's catalogs and
96
+ maintenance surface. Missing a view, a metric, or a maintenance command? **Open
97
+ an issue or PR** — contributions welcome.
98
+
99
+ ## Status
100
+
101
+ **Preview — mock-validated only, not run against a live cluster.** The catalog
102
+ queries are modelled from the documented `pg_catalog` / `pg_stat_*` shapes and
103
+ need live verification. `postgres-aiops doctor` is the fastest live check.
@@ -0,0 +1,52 @@
1
+ # Postgres AIops v0.1.0 — preview
2
+
3
+ Governed AI-ops for **PostgreSQL DBA operations** for AI agents — connecting via
4
+ **psycopg 3** and reading the system catalogs and `pg_stat_*` views — with a
5
+ built-in governance harness (audit, policy, token/runaway budget, undo-token
6
+ recording, graduated risk tiers) and an encrypted credential store. Standalone —
7
+ no external skill-family dependency.
8
+
9
+ > **Preview / mock-only.** All behaviour is validated against a mocked psycopg
10
+ > cursor/connection; it has **not** been run against a live PostgreSQL cluster.
11
+ > The fastest live check is `postgres-aiops doctor`.
12
+ >
13
+ > Community-maintained; **not affiliated with or endorsed by the PostgreSQL
14
+ > Global Development Group.** "PostgreSQL" and related trademarks belong to their
15
+ > owners.
16
+
17
+ ## Highlights
18
+
19
+ - **33 MCP tools** (24 read, 9 write), every one wrapped with `@governed_tool`.
20
+ - Read: cluster `overview`; server (5); activity (3); query stats (2); index
21
+ health (4); table health (3); replication (3); and three flagship analyses.
22
+ - Write: `terminate_backend`/`cancel_query`/`drop_index` (high);
23
+ `run_vacuum`/`run_analyze`/`create_index`/`reindex`/`update_setting`/
24
+ `reset_query_stats` (medium).
25
+ - **Three signature analyses** — `slow_query_rca` (worst `pg_stat_statements`
26
+ entry + EXPLAIN → cited cause/action), `bloat_and_vacuum_analysis` (dead-tuple
27
+ ratio + autovacuum recency → recommendation), and `blocking_lock_chain_rca`
28
+ (build the wait-for tree, name the root blocker).
29
+ - **Encrypted password store** (`~/.postgres-aiops/secrets.enc`, Fernet + scrypt)
30
+ — never plaintext on disk; legacy `PG_<TARGET>_PASSWORD` env fallback.
31
+ - **CLI** with an `init` onboarding wizard, `secret` management, and `doctor`.
32
+ - **psycopg 3 connection layer** — parameterised catalog reads, `dict_row`
33
+ results, autocommit for maintenance commands, and teaching error translation
34
+ (`PgError`). Reversible writes fetch the real before-state first.
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ uv tool install postgres-aiops
40
+ postgres-aiops init
41
+ postgres-aiops doctor
42
+ ```
43
+
44
+ ## Caveats
45
+
46
+ - The catalog / `pg_stat_*` queries are modelled from the documented shapes and
47
+ need live verification against a real cluster.
48
+ - `top_queries` / `slow_query_rca` require the `pg_stat_statements` extension;
49
+ the read role should have `pg_monitor`.
50
+ - Out of scope by design: application-schema migrations, ORM management, logical
51
+ backup/restore orchestration, and any bulk destructive DDL.
52
+ - Missing a view, metric, or maintenance command? Open an issue or PR.
@@ -0,0 +1,89 @@
1
+ # Security Policy
2
+
3
+ ## Disclaimer
4
+
5
+ Community-maintained open-source project. **Not affiliated with, endorsed by, or
6
+ sponsored by the PostgreSQL Global Development Group or any vendor.**
7
+ "PostgreSQL" and related trademarks belong to their respective owners. Source is
8
+ publicly auditable under the MIT license.
9
+
10
+ ## Reporting Vulnerabilities
11
+
12
+ Report privately via a GitHub Security Advisory on
13
+ [github.com/AIops-tools/Postgres-AIops](https://github.com/AIops-tools/Postgres-AIops/security/advisories)
14
+ or email zhouwei008@gmail.com. Please do not open public issues for security
15
+ reports.
16
+
17
+ ## Security Design
18
+
19
+ ### Credential Management
20
+ - Per-target PostgreSQL role passwords live **encrypted** in
21
+ `~/.postgres-aiops/secrets.enc` (Fernet/AES-128 + scrypt-derived key; chmod
22
+ 600), never in `config.yaml` and never in source. The master password is never
23
+ stored — only a per-store random salt and the ciphertext are on disk.
24
+ - A legacy plaintext env var `PG_<TARGET_NAME_UPPER>_PASSWORD` is still honoured
25
+ as a fallback with a deprecation warning (migrate with `postgres-aiops secret
26
+ migrate`).
27
+ - The password is passed to `psycopg.connect` at connect time and held only in
28
+ memory. It is never logged or echoed; `config.yaml` holds only host, port,
29
+ dbname, user, and `sslmode`. The redacted DSN (`dsn_redacted`) masks it.
30
+
31
+ ### SQL-Injection Defenses
32
+ - **All values are bound query parameters** (pids, thresholds, limits, setting
33
+ values) — never string-formatted into SQL.
34
+ - The few identifiers that cannot be parameterised (table/index/schema names, GUC
35
+ names, `ORDER BY` columns, index methods, `REINDEX` kinds) are validated
36
+ against strict allow-lists (`postgres_aiops.ops._util`) and double-quoted
37
+ before the single interpolation site; anything that is not a plain identifier
38
+ is rejected, not interpolated.
39
+ - `EXPLAIN` rejects multi-statement input (an embedded `;` is refused).
40
+
41
+ ### Governed Operations
42
+ Every MCP tool runs through the bundled `@governed_tool` harness
43
+ (`postgres_aiops.governance`):
44
+ - **Audit** — every call logged to a local SQLite DB under `~/.postgres-aiops/`
45
+ (relocatable via `POSTGRES_AIOPS_HOME`), agent-attributed, secret-redacted.
46
+ - **Token/runaway budget** — hard ceilings (`POSTGRES_MAX_TOOL_CALLS` /
47
+ `POSTGRES_MAX_TOOL_SECONDS` — the env-var names the bundled harness reads) plus
48
+ an on-by-default guard that trips a tight poll/retry loop.
49
+ - **Graduated risk tiers** — `~/.postgres-aiops/rules.yaml` `risk_tiers` gate
50
+ writes by environment/tag; the highest tiers require a recorded approver
51
+ (`POSTGRES_AUDIT_APPROVED_BY` / `POSTGRES_AUDIT_RATIONALE`).
52
+ - **Undo-token recording** — reversible writes fetch the **real before-state
53
+ first** and record a faithful inverse (`create_index`↔`drop_index`, where drop
54
+ captures `pg_get_indexdef`; `update_setting` restores the prior value).
55
+
56
+ ### State-Changing Operations
57
+ Every write supports `--dry-run` (CLI) / `dry_run=True` (MCP) and requires double
58
+ confirmation at the CLI layer. Destructive/irreversible ops (`terminate_backend`,
59
+ `cancel_query`, `drop_index`) are `risk_level=high`; mutating maintenance ops
60
+ (`run_vacuum`, `run_analyze`, `create_index`, `reindex`, `update_setting`,
61
+ `reset_query_stats`) are `medium`. Irreversible ops capture prior stats for the
62
+ audit record but record no undo token. `ALTER SYSTEM SET` writes
63
+ `postgresql.auto.conf` and reports (but does not auto-run) the required
64
+ `pg_reload_conf()` / restart.
65
+
66
+ ### SSL/TLS
67
+ `sslmode` follows libpq semantics (default `prefer`); set `require`/`verify-full`
68
+ for untrusted networks.
69
+
70
+ ### Prompt-Injection Protection
71
+ All catalog- and query-returned text (query text, object names, descriptions) is
72
+ passed through a `sanitize()` truncate + control-character strip before reaching
73
+ the agent.
74
+
75
+ ### Network Scope
76
+ No webhooks, no telemetry, no outbound calls beyond the configured PostgreSQL
77
+ connection. No post-install scripts or background services.
78
+
79
+ ## Static Analysis
80
+
81
+ ```bash
82
+ uvx bandit -r postgres_aiops/ mcp_server/
83
+ uv run ruff check .
84
+ ```
85
+
86
+ ## Supported Versions
87
+
88
+ The latest released version receives security fixes. This is a preview (0.x);
89
+ pin a version in production.
@@ -0,0 +1 @@
1
+ """MCP server package for postgres-aiops."""
@@ -0,0 +1,101 @@
1
+ """Shared MCP server primitives: the FastMCP instance, connection helper,
2
+ error sanitisation, and the ``@tool_errors`` decorator.
3
+
4
+ Tool modules under ``mcp_server/tools/`` import ``mcp`` from here and register
5
+ their ``@mcp.tool()`` functions onto it. ``mcp_server/server.py`` then imports
6
+ those modules and runs the server.
7
+
8
+ Keep ``Optional[X]`` (never PEP 604 ``X | None``) in any FastMCP-reflected
9
+ tool signature — on older mcp/pydantic the union eval'd to ``types.UnionType``
10
+ crashes FastMCP's ``issubclass`` check.
11
+ """
12
+
13
+ import functools
14
+ import logging
15
+ import os
16
+ from collections.abc import Callable
17
+ from pathlib import Path
18
+ from typing import Any, Optional
19
+
20
+ from mcp.server.fastmcp import FastMCP
21
+
22
+ from postgres_aiops.config import load_config
23
+ from postgres_aiops.connection import ConnectionManager, PgError
24
+ from postgres_aiops.governance import sanitize
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ _DOCTOR_HINT = "Run 'postgres-aiops doctor' to verify connectivity and credentials."
29
+
30
+
31
+ def _safe_error(exc: Exception, tool: str) -> str:
32
+ """Return an agent-safe error string; log full detail server-side only."""
33
+ logger.error("Tool %s failed", tool, exc_info=True)
34
+ _passthrough = (
35
+ ValueError,
36
+ FileNotFoundError,
37
+ KeyError,
38
+ PermissionError,
39
+ TimeoutError,
40
+ ConnectionError,
41
+ PgError,
42
+ )
43
+ if isinstance(exc, _passthrough):
44
+ return sanitize(str(exc), 300)
45
+ return f"{type(exc).__name__}: operation failed."
46
+
47
+
48
+ def tool_errors(shape: str = "dict") -> Callable:
49
+ """Wrap a tool body in the canonical try/except → ``_safe_error`` pattern.
50
+
51
+ Place this *between* ``@governed_tool`` and the function so the audit
52
+ decorator and FastMCP still see the original signature.
53
+ """
54
+
55
+ def decorator(func: Callable) -> Callable:
56
+ name = func.__name__
57
+
58
+ @functools.wraps(func)
59
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
60
+ try:
61
+ return func(*args, **kwargs)
62
+ except Exception as e: # noqa: BLE001 — sanitised below
63
+ msg = _safe_error(e, name)
64
+ if shape == "list":
65
+ return [{"error": msg, "hint": _DOCTOR_HINT}]
66
+ if shape == "str":
67
+ return f"Error: {msg} {_DOCTOR_HINT}"
68
+ return {"error": msg, "hint": _DOCTOR_HINT}
69
+
70
+ return wrapper
71
+
72
+ return decorator
73
+
74
+
75
+ mcp = FastMCP(
76
+ "postgres-aiops",
77
+ instructions=(
78
+ "Governed PostgreSQL DBA operations (preview): a one-shot cluster "
79
+ "'overview'; server reads (version/settings/extensions/databases/roles); "
80
+ "activity (sessions, long-running queries, locks); query stats "
81
+ "(pg_stat_statements top-N, EXPLAIN); index and table health (unused / "
82
+ "missing / bloat / autovacuum); replication (lag, slots, WAL); three "
83
+ "flagship analyses — 'slow_query_rca', 'bloat_and_vacuum_analysis', and "
84
+ "'blocking_lock_chain_rca'; and guarded writes (terminate/cancel, "
85
+ "vacuum/analyze, create/drop index, reindex, ALTER SYSTEM). Every tool "
86
+ "runs through the postgres-aiops governance harness (audit / budget / "
87
+ "risk-tier / undo). Do NOT use for OT/industrial edge — see industrial-aiops."
88
+ ),
89
+ )
90
+
91
+ _conn_mgr: Optional[ConnectionManager] = None
92
+
93
+
94
+ def _get_connection(target: Optional[str] = None) -> Any:
95
+ """Return a PostgreSQL connection, lazily initialising the manager."""
96
+ global _conn_mgr # noqa: PLW0603
97
+ if _conn_mgr is None:
98
+ config_path_str = os.environ.get("POSTGRES_AIOPS_CONFIG")
99
+ config_path = Path(config_path_str) if config_path_str else None
100
+ _conn_mgr = ConnectionManager(load_config(config_path))
101
+ return _conn_mgr.connect(target)
@@ -0,0 +1,37 @@
1
+ """MCP server wrapping postgres-aiops operations (stdio transport).
2
+
3
+ Thin adapter layer: each ``@mcp.tool()`` function (in ``mcp_server/tools/``)
4
+ delegates to the ``postgres_aiops`` ops package and is wrapped with the
5
+ postgres-aiops ``@governed_tool`` harness (audit / budget / undo / risk-tier).
6
+
7
+ Standalone, self-governed PostgreSQL DBA operations (preview).
8
+ For PostgreSQL servers/clusters via psycopg 3.
9
+
10
+ Source: https://github.com/AIops-tools/Postgres-AIops
11
+ License: MIT
12
+ """
13
+
14
+ import logging
15
+
16
+ from mcp_server._shared import _safe_error, mcp, tool_errors
17
+
18
+ # Importing the tool modules registers every @mcp.tool() onto the shared
19
+ # `mcp` instance. Order does not matter; each module is self-contained.
20
+ from mcp_server.tools import ( # noqa: F401 — side effects
21
+ activity,
22
+ analysis,
23
+ indexes,
24
+ queries,
25
+ remediation,
26
+ replication,
27
+ server,
28
+ tables,
29
+ )
30
+
31
+ __all__ = ["mcp", "main", "_safe_error", "tool_errors"]
32
+
33
+
34
+ def main() -> None:
35
+ """Run the MCP server over stdio."""
36
+ logging.basicConfig(level=logging.INFO)
37
+ mcp.run(transport="stdio")
@@ -0,0 +1 @@
1
+ """MCP tool modules. Importing each registers its @mcp.tool() functions."""