postgres-aiops 0.11.0__tar.gz → 0.12.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.
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/.claude-plugin/plugin.json +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/.mcp.json +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/CHANGELOG.md +13 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/PKG-INFO +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/SECURITY.md +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/openclaw.plugin.json +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/package.json +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/_root.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/query.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/remediate.py +8 -8
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/undo.py +3 -3
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/_shared.py +2 -2
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/server.py +4 -4
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/activity.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/analysis.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/indexes.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/queries.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/remediation.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/replication.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/server.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/tables.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/undo.py +4 -4
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/pyproject.toml +4 -4
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/server.json +2 -2
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_cli_audited.py +3 -2
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_cli_governed_exit.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_cli_remediate.py +3 -3
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_cli_writes.py +4 -4
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_connection_lost.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_gov_audit.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_gov_decorators.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_gov_patterns.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_governance_persistence.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_mcp_tools.py +18 -18
- postgres_aiops-0.12.0/tests/test_package_layout.py +62 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_smoke.py +15 -15
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_tool_annotations.py +17 -6
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_truncation.py +2 -2
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_undo_executor.py +5 -5
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_undo_replay.py +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/uv.lock +1 -1
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/.github/workflows/mcp-publish.yml +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/.github/workflows/publish.yml +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/.gitignore +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/Dockerfile +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/LICENSE +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/README.md +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/RELEASE_NOTES.md +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/docs/VERIFICATION.md +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/glama.json +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/__init__.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/__init__.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/_common.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/activity.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/analyze.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/doctor.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/index.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/init.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/overview.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/replication.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/secret.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/server.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/cli/table.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/config.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/connection.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/doctor.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/governance/__init__.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/governance/audit.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/governance/budget.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/governance/decorators.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/governance/outcome.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/governance/paths.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/governance/patterns.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/governance/policy.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/governance/sanitize.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/governance/undo.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/__init__.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/_annotations.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/__init__.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/ops/__init__.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/ops/_util.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/ops/activity.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/ops/analysis.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/ops/indexes.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/ops/overview.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/ops/queries.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/ops/remediation.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/ops/replication.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/ops/server.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/ops/tables.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/postgres_aiops/secretstore.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/skills/postgres-aiops/SKILL.md +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/skills/postgres-aiops/references/agent-guardrails.md +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/skills/postgres-aiops/references/capabilities.md +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/skills/postgres-aiops/references/cli-reference.md +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/skills/postgres-aiops/references/setup-guide.md +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/smithery.yaml +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/conftest.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_analysis.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_cli_reads.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_cli_secret.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_config.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_connection.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_connection_more.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_doctor.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_gov_policy.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_init.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_ops_more.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_optional_fields.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_reads.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_secretstore.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_secretstore_more.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_self_lockout.py +0 -0
- {postgres_aiops-0.11.0 → postgres_aiops-0.12.0}/tests/test_writes.py +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://anthropic.com/claude-code/plugin.schema.json",
|
|
3
3
|
"name": "postgres-aiops",
|
|
4
4
|
"displayName": "Postgres AIops",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.12.0",
|
|
6
6
|
"description": "Governed PostgreSQL DBA ops: slow-query RCA, bloat/vacuum & blocking-lock analysis; 35 MCP tools.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Wei Zhou",
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.12.0 — 2026-10-15
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- The MCP server moved from a top-level `mcp_server` package to
|
|
7
|
+
`postgres_aiops.mcp_server`. Every AIops tool used to ship its own top-level
|
|
8
|
+
`mcp_server`, so installing two of them into one environment merged the two
|
|
9
|
+
into one package: `postgres-aiops-mcp` could start another tool's server, and
|
|
10
|
+
`postgres-aiops undo` wrote audit and undo rows into the other tool's home
|
|
11
|
+
directory. Installs through `uvx`, the Claude Code plugin or the OpenClaw
|
|
12
|
+
bundle were isolated and unaffected. The `postgres-aiops-mcp` command is
|
|
13
|
+
unchanged; only code that imported `mcp_server` directly must now import
|
|
14
|
+
`postgres_aiops.mcp_server`.
|
|
15
|
+
|
|
3
16
|
## v0.11.0 — 2026-10-15
|
|
4
17
|
|
|
5
18
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: postgres-aiops
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
4
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
5
|
Author-email: Wei Zhou <zhouwei008@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -70,7 +70,7 @@ def query_reset(target: TargetOption = None, dry_run: DryRunOption = False) -> N
|
|
|
70
70
|
``@governed_tool``-wrapped MCP function, so each is audited on the same
|
|
71
71
|
governance path as MCP calls.
|
|
72
72
|
"""
|
|
73
|
-
from mcp_server.tools import queries as gov
|
|
73
|
+
from postgres_aiops.mcp_server.tools import queries as gov
|
|
74
74
|
|
|
75
75
|
if dry_run:
|
|
76
76
|
dry_run_preview(
|
|
@@ -32,7 +32,7 @@ def remediate_terminate(
|
|
|
32
32
|
dry_run: DryRunOption = False,
|
|
33
33
|
) -> None:
|
|
34
34
|
"""Terminate a backend (no undo; dry-run + confirm)."""
|
|
35
|
-
from mcp_server.tools import remediation as gov
|
|
35
|
+
from postgres_aiops.mcp_server.tools import remediation as gov
|
|
36
36
|
|
|
37
37
|
if dry_run:
|
|
38
38
|
# Through the governed call: terminate_backend refuses this tool's own
|
|
@@ -54,7 +54,7 @@ def remediate_cancel(
|
|
|
54
54
|
dry_run: DryRunOption = False,
|
|
55
55
|
) -> None:
|
|
56
56
|
"""Cancel a backend's running query (no undo; dry-run + confirm)."""
|
|
57
|
-
from mcp_server.tools import remediation as gov
|
|
57
|
+
from postgres_aiops.mcp_server.tools import remediation as gov
|
|
58
58
|
|
|
59
59
|
if dry_run:
|
|
60
60
|
dry_run_preview(
|
|
@@ -76,7 +76,7 @@ def remediate_vacuum(
|
|
|
76
76
|
dry_run: DryRunOption = False,
|
|
77
77
|
) -> None:
|
|
78
78
|
"""VACUUM a table (dry-run + confirm)."""
|
|
79
|
-
from mcp_server.tools import remediation as gov
|
|
79
|
+
from postgres_aiops.mcp_server.tools import remediation as gov
|
|
80
80
|
|
|
81
81
|
if dry_run:
|
|
82
82
|
dry_run_preview(
|
|
@@ -97,7 +97,7 @@ def remediate_analyze(
|
|
|
97
97
|
dry_run: DryRunOption = False,
|
|
98
98
|
) -> None:
|
|
99
99
|
"""ANALYZE a table (dry-run + confirm)."""
|
|
100
|
-
from mcp_server.tools import remediation as gov
|
|
100
|
+
from postgres_aiops.mcp_server.tools import remediation as gov
|
|
101
101
|
|
|
102
102
|
if dry_run:
|
|
103
103
|
dry_run_preview(
|
|
@@ -120,7 +120,7 @@ def remediate_create_index(
|
|
|
120
120
|
dry_run: DryRunOption = False,
|
|
121
121
|
) -> None:
|
|
122
122
|
"""Create an index (reversible; dry-run + confirm)."""
|
|
123
|
-
from mcp_server.tools import remediation as gov
|
|
123
|
+
from postgres_aiops.mcp_server.tools import remediation as gov
|
|
124
124
|
|
|
125
125
|
if dry_run:
|
|
126
126
|
# Through the governed call: create_index validates the table/columns vs
|
|
@@ -146,7 +146,7 @@ def remediate_drop_index(
|
|
|
146
146
|
dry_run: DryRunOption = False,
|
|
147
147
|
) -> None:
|
|
148
148
|
"""Drop an index (reversible; captures the definition first; dry-run + confirm)."""
|
|
149
|
-
from mcp_server.tools import remediation as gov
|
|
149
|
+
from postgres_aiops.mcp_server.tools import remediation as gov
|
|
150
150
|
|
|
151
151
|
if dry_run:
|
|
152
152
|
dry_run_preview(
|
|
@@ -168,7 +168,7 @@ def remediate_reindex(
|
|
|
168
168
|
dry_run: DryRunOption = False,
|
|
169
169
|
) -> None:
|
|
170
170
|
"""REINDEX an index/table/schema (dry-run + confirm)."""
|
|
171
|
-
from mcp_server.tools import remediation as gov
|
|
171
|
+
from postgres_aiops.mcp_server.tools import remediation as gov
|
|
172
172
|
|
|
173
173
|
if dry_run:
|
|
174
174
|
dry_run_preview(
|
|
@@ -195,7 +195,7 @@ def remediate_set(
|
|
|
195
195
|
dry_run: DryRunOption = False,
|
|
196
196
|
) -> None:
|
|
197
197
|
"""ALTER SYSTEM SET a parameter, or --reset it (reversible; dry-run + confirm)."""
|
|
198
|
-
from mcp_server.tools import remediation as gov
|
|
198
|
+
from postgres_aiops.mcp_server.tools import remediation as gov
|
|
199
199
|
|
|
200
200
|
action = f"ALTER SYSTEM RESET {name}" if reset else f"ALTER SYSTEM SET {name} ="
|
|
201
201
|
if dry_run:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""``postgres-aiops undo`` — list recorded undo tokens and apply them.
|
|
2
2
|
|
|
3
3
|
Real execution is delegated to the ``@governed_tool``-wrapped functions in
|
|
4
|
-
``mcp_server.tools.undo`` so an applied undo is audited on the SAME governance
|
|
4
|
+
``postgres_aiops.mcp_server.tools.undo`` so an applied undo is audited on the SAME governance
|
|
5
5
|
path as any other write (the inverse tool it dispatches is itself re-gated).
|
|
6
6
|
"""
|
|
7
7
|
|
|
@@ -38,7 +38,7 @@ def undo_list_cmd(
|
|
|
38
38
|
target: TargetOption = None,
|
|
39
39
|
) -> None:
|
|
40
40
|
"""List recorded, not-yet-applied undo tokens."""
|
|
41
|
-
from mcp_server.tools import undo as gov
|
|
41
|
+
from postgres_aiops.mcp_server.tools import undo as gov
|
|
42
42
|
|
|
43
43
|
console.print_json(json.dumps(gov.undo_list(limit=limit, target=target)))
|
|
44
44
|
|
|
@@ -49,7 +49,7 @@ def undo_apply_cmd(
|
|
|
49
49
|
undo_id: UndoIdArg, target: TargetOption = None, dry_run: DryRunOption = False
|
|
50
50
|
) -> None:
|
|
51
51
|
"""Apply a recorded undo (dispatches its inverse tool; dry-run + confirm)."""
|
|
52
|
-
from mcp_server.tools import undo as gov
|
|
52
|
+
from postgres_aiops.mcp_server.tools import undo as gov
|
|
53
53
|
|
|
54
54
|
if dry_run:
|
|
55
55
|
# undo_apply refuses an unknown or already-applied token, and refuses one
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Shared MCP server primitives: the MCPServer instance, connection helper,
|
|
2
2
|
error sanitisation, and the ``@tool_errors`` decorator.
|
|
3
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
|
|
4
|
+
Tool modules under ``postgres_aiops/mcp_server/tools/`` import ``mcp`` from here and register
|
|
5
|
+
their ``@mcp.tool()`` functions onto it. ``postgres_aiops/mcp_server/server.py`` then imports
|
|
6
6
|
those modules and runs the server.
|
|
7
7
|
|
|
8
8
|
Keep ``Optional[X]`` (never PEP 604 ``X | None``) in any reflected tool
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""MCP server wrapping postgres-aiops operations (stdio transport).
|
|
2
2
|
|
|
3
|
-
Thin adapter layer: each ``@mcp.tool()`` function (in ``mcp_server/tools/``)
|
|
3
|
+
Thin adapter layer: each ``@mcp.tool()`` function (in ``postgres_aiops/mcp_server/tools/``)
|
|
4
4
|
delegates to the ``postgres_aiops`` ops package and is wrapped with the
|
|
5
5
|
postgres-aiops ``@governed_tool`` harness (audit / budget / undo / risk-tier).
|
|
6
6
|
|
|
@@ -14,12 +14,12 @@ License: MIT
|
|
|
14
14
|
import logging
|
|
15
15
|
import os
|
|
16
16
|
|
|
17
|
-
from mcp_server._annotations import apply_risk_annotations
|
|
18
|
-
from mcp_server._shared import _safe_error, mcp, tool_errors
|
|
17
|
+
from postgres_aiops.mcp_server._annotations import apply_risk_annotations
|
|
18
|
+
from postgres_aiops.mcp_server._shared import _safe_error, mcp, tool_errors
|
|
19
19
|
|
|
20
20
|
# Importing the tool modules registers every @mcp.tool() onto the shared
|
|
21
21
|
# `mcp` instance. Order does not matter; each module is self-contained.
|
|
22
|
-
from mcp_server.tools import ( # noqa: F401 — side effects
|
|
22
|
+
from postgres_aiops.mcp_server.tools import ( # noqa: F401 — side effects
|
|
23
23
|
activity,
|
|
24
24
|
analysis,
|
|
25
25
|
indexes,
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from mcp_server._shared import _get_connection, mcp, tool_errors
|
|
6
5
|
from postgres_aiops.governance import governed_tool
|
|
6
|
+
from postgres_aiops.mcp_server._shared import _get_connection, mcp, tool_errors
|
|
7
7
|
from postgres_aiops.ops import activity as ops
|
|
8
8
|
|
|
9
9
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Optional
|
|
4
4
|
|
|
5
|
-
from mcp_server._shared import _get_connection, mcp, tool_errors
|
|
6
5
|
from postgres_aiops.governance import governed_tool
|
|
6
|
+
from postgres_aiops.mcp_server._shared import _get_connection, mcp, tool_errors
|
|
7
7
|
from postgres_aiops.ops import activity as activity_ops
|
|
8
8
|
from postgres_aiops.ops import analysis as ops
|
|
9
9
|
from postgres_aiops.ops import queries as query_ops
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from mcp_server._shared import _get_connection, mcp, tool_errors
|
|
6
5
|
from postgres_aiops.governance import governed_tool
|
|
6
|
+
from postgres_aiops.mcp_server._shared import _get_connection, mcp, tool_errors
|
|
7
7
|
from postgres_aiops.ops import indexes as ops
|
|
8
8
|
|
|
9
9
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from mcp_server._shared import _get_connection, mcp, tool_errors
|
|
6
5
|
from postgres_aiops.governance import governed_tool
|
|
6
|
+
from postgres_aiops.mcp_server._shared import _get_connection, mcp, tool_errors
|
|
7
7
|
from postgres_aiops.ops import queries as ops
|
|
8
8
|
|
|
9
9
|
|
{postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/remediation.py
RENAMED
|
@@ -12,8 +12,8 @@ Risk tiers:
|
|
|
12
12
|
|
|
13
13
|
from typing import Any, Optional
|
|
14
14
|
|
|
15
|
-
from mcp_server._shared import _get_connection, mcp, tool_errors
|
|
16
15
|
from postgres_aiops.governance import governed_tool
|
|
16
|
+
from postgres_aiops.mcp_server._shared import _get_connection, mcp, tool_errors
|
|
17
17
|
from postgres_aiops.ops import remediation as ops
|
|
18
18
|
|
|
19
19
|
# ── undo descriptors (built from the fetched before-state) ──────────────────
|
{postgres_aiops-0.11.0 → postgres_aiops-0.12.0/postgres_aiops}/mcp_server/tools/replication.py
RENAMED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from mcp_server._shared import _get_connection, mcp, tool_errors
|
|
6
5
|
from postgres_aiops.governance import governed_tool
|
|
6
|
+
from postgres_aiops.mcp_server._shared import _get_connection, mcp, tool_errors
|
|
7
7
|
from postgres_aiops.ops import replication as ops
|
|
8
8
|
|
|
9
9
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from mcp_server._shared import _get_connection, mcp, tool_errors
|
|
6
5
|
from postgres_aiops.governance import governed_tool
|
|
6
|
+
from postgres_aiops.mcp_server._shared import _get_connection, mcp, tool_errors
|
|
7
7
|
from postgres_aiops.ops import overview as overview_ops
|
|
8
8
|
from postgres_aiops.ops import server as ops
|
|
9
9
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from mcp_server._shared import _get_connection, mcp, tool_errors
|
|
6
5
|
from postgres_aiops.governance import governed_tool
|
|
6
|
+
from postgres_aiops.mcp_server._shared import _get_connection, mcp, tool_errors
|
|
7
7
|
from postgres_aiops.ops import tables as ops
|
|
8
8
|
|
|
9
9
|
|
|
@@ -15,26 +15,26 @@ design, inverses in this line key off ids/names, not credentials.
|
|
|
15
15
|
import json
|
|
16
16
|
from typing import Any, Optional
|
|
17
17
|
|
|
18
|
-
from mcp_server._shared import mcp, tool_errors
|
|
19
18
|
from postgres_aiops.governance import governed_tool
|
|
20
19
|
from postgres_aiops.governance.undo import get_undo_store
|
|
20
|
+
from postgres_aiops.mcp_server._shared import mcp, tool_errors
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def _resolve_tool(name: str) -> Any:
|
|
24
24
|
"""Return the governed callable registered under ``name`` (or None).
|
|
25
25
|
|
|
26
26
|
The CLI reaches this through ``undo apply``, whose process only imports
|
|
27
|
-
``mcp_server.tools.undo`` — every other tool module is imported lazily inside
|
|
27
|
+
``postgres_aiops.mcp_server.tools.undo`` — every other tool module is imported lazily inside
|
|
28
28
|
its own CLI command and so is absent here. Without forcing a full load,
|
|
29
29
|
``undo_apply`` would fail with "inverse tool not registered" for every write
|
|
30
30
|
tool when driven from the CLI (the MCP entry point loads all modules, so it
|
|
31
|
-
never hit this). If the first lookup misses, import ``mcp_server.server`` —
|
|
31
|
+
never hit this). If the first lookup misses, import ``postgres_aiops.mcp_server.server`` —
|
|
32
32
|
which registers every ``@mcp.tool()`` as an import side effect — and retry.
|
|
33
33
|
Imported lazily to avoid the undo↔server cycle.
|
|
34
34
|
"""
|
|
35
35
|
tool = mcp._tool_manager._tools.get(name)
|
|
36
36
|
if tool is None:
|
|
37
|
-
import mcp_server.server # noqa: F401 — registers all tools as a side effect
|
|
37
|
+
import postgres_aiops.mcp_server.server # noqa: F401 — registers all tools as a side effect
|
|
38
38
|
|
|
39
39
|
tool = mcp._tool_manager._tools.get(name)
|
|
40
40
|
return getattr(tool, "fn", None) if tool else None
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "postgres-aiops"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.12.0"
|
|
8
8
|
description = "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)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -28,10 +28,10 @@ dev = [
|
|
|
28
28
|
|
|
29
29
|
[project.scripts]
|
|
30
30
|
postgres-aiops = "postgres_aiops.cli:app"
|
|
31
|
-
postgres-aiops-mcp = "mcp_server.server:main"
|
|
31
|
+
postgres-aiops-mcp = "postgres_aiops.mcp_server.server:main"
|
|
32
32
|
|
|
33
33
|
[tool.hatch.build.targets.wheel]
|
|
34
|
-
packages = ["postgres_aiops"
|
|
34
|
+
packages = ["postgres_aiops"]
|
|
35
35
|
|
|
36
36
|
[tool.ruff]
|
|
37
37
|
line-length = 100
|
|
@@ -47,7 +47,7 @@ select = ["E", "F", "I", "N", "W", "UP"]
|
|
|
47
47
|
"postgres_aiops/governance/*.py" = ["E501", "N818"]
|
|
48
48
|
# MCP server files are reflected by FastMCP/Pydantic; PEP 604 unions (X | None)
|
|
49
49
|
# crash on older mcp/pydantic, so they intentionally use Optional[X].
|
|
50
|
-
"mcp_server/**/*.py" = ["UP007", "UP045"]
|
|
50
|
+
"postgres_aiops/mcp_server/**/*.py" = ["UP007", "UP045"]
|
|
51
51
|
# The MCP entry keeps an explicit Python-version guard even though the declared
|
|
52
52
|
# minimum is already 3.11.
|
|
53
53
|
"postgres_aiops/cli/_root.py" = ["UP036"]
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/AIops-tools/Postgres-AIops",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.12.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "pypi",
|
|
14
14
|
"identifier": "postgres-aiops",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.12.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|
|
@@ -52,11 +52,12 @@ def _chain(fn):
|
|
|
52
52
|
|
|
53
53
|
def _calls_an_mcp_tool(fn) -> bool:
|
|
54
54
|
source = inspect.getsource(fn)
|
|
55
|
-
if "mcp_server" in source:
|
|
55
|
+
if "postgres_aiops.mcp_server" in source:
|
|
56
56
|
return True
|
|
57
57
|
names = set()
|
|
58
58
|
for node in ast.parse(open(inspect.getsourcefile(fn)).read()).body:
|
|
59
|
-
if isinstance(node, ast.ImportFrom)
|
|
59
|
+
module = (node.module or "") if isinstance(node, ast.ImportFrom) else ""
|
|
60
|
+
if module.startswith("postgres_aiops.mcp_server"):
|
|
60
61
|
names |= {a.asname or a.name for a in node.names}
|
|
61
62
|
used = {n.id for n in ast.walk(ast.parse(textwrap.dedent(source))) if isinstance(n, ast.Name)}
|
|
62
63
|
return bool(names & used)
|
|
@@ -28,7 +28,7 @@ import typer
|
|
|
28
28
|
from postgres_aiops.cli._common import checked
|
|
29
29
|
|
|
30
30
|
CLI_DIR = pathlib.Path(__file__).resolve().parents[1] / "postgres_aiops" / "cli"
|
|
31
|
-
TOOLS_DIR = pathlib.Path(__file__).resolve().parents[1] / "mcp_server" / "tools"
|
|
31
|
+
TOOLS_DIR = pathlib.Path(__file__).resolve().parents[1] / "postgres_aiops" / "mcp_server" / "tools"
|
|
32
32
|
|
|
33
33
|
#: Helpers that end the command non-zero when the payload reports a failure.
|
|
34
34
|
CHECKERS = ['checked', 'dry_run_preview']
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""CLI ``remediate`` sub-commands — dry-run previews and error translation.
|
|
2
2
|
|
|
3
3
|
Every ``--dry-run`` routes its preview through the ``@governed_tool``-wrapped
|
|
4
|
-
twin in ``mcp_server.tools``, so the guards the real write would hit run against
|
|
4
|
+
twin in ``postgres_aiops.mcp_server.tools``, so the guards the real write would hit run against
|
|
5
5
|
the real target and an audit row lands for the preview itself. A preview of a
|
|
6
6
|
call that will be refused must report the refusal rather than a green banner:
|
|
7
7
|
otherwise the caller reads the later refusal as transient and retries.
|
|
@@ -58,7 +58,7 @@ def gov_home(tmp_path, monkeypatch):
|
|
|
58
58
|
@pytest.fixture
|
|
59
59
|
def guarded_conn(monkeypatch):
|
|
60
60
|
"""Wire the governed remediation tools to a fake whose own backend pid is known."""
|
|
61
|
-
import mcp_server.tools.remediation as gov
|
|
61
|
+
import postgres_aiops.mcp_server.tools.remediation as gov
|
|
62
62
|
|
|
63
63
|
fake = FakePg(
|
|
64
64
|
{"FROM pg_stat_activity": [{"pid": 42, "username": "app"}],
|
|
@@ -167,7 +167,7 @@ def test_drop_index_dry_run_reports_an_unreachable_target(monkeypatch):
|
|
|
167
167
|
Before the reroute this printed a confident green DRY-RUN for a target the
|
|
168
168
|
CLI had never contacted — the exact green-preview-then-refusal trap.
|
|
169
169
|
"""
|
|
170
|
-
import mcp_server.tools.remediation as gov
|
|
170
|
+
import postgres_aiops.mcp_server.tools.remediation as gov
|
|
171
171
|
from postgres_aiops.cli import app
|
|
172
172
|
from postgres_aiops.connection import PgError
|
|
173
173
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""CLI confirmed-write path — past dry-run, through governance, onto disk.
|
|
2
2
|
|
|
3
3
|
The CLI write commands delegate real execution to the ``@governed_tool``
|
|
4
|
-
functions in ``mcp_server.tools``. These tests drive a write command PAST the
|
|
4
|
+
functions in ``postgres_aiops.mcp_server.tools``. These tests drive a write command PAST the
|
|
5
5
|
dry-run branch and the double-confirm prompts and assert the call really went
|
|
6
6
|
through the governed path (audit row on disk) — the regression test for the
|
|
7
7
|
"CLI writes were unaudited" line-wide fix.
|
|
@@ -56,7 +56,7 @@ def test_cli_query_reset_dry_run_never_writes_but_is_audited(gov_home, monkeypat
|
|
|
56
56
|
from postgres_aiops.cli import app
|
|
57
57
|
|
|
58
58
|
fake = fake_pg()
|
|
59
|
-
import mcp_server.tools.queries as gov_queries
|
|
59
|
+
import postgres_aiops.mcp_server.tools.queries as gov_queries
|
|
60
60
|
|
|
61
61
|
monkeypatch.setattr(gov_queries, "_get_connection", lambda target=None: fake)
|
|
62
62
|
result = CliRunner().invoke(app, ["query", "reset", "--dry-run"])
|
|
@@ -74,7 +74,7 @@ def test_cli_query_reset_confirmed_goes_through_governance(gov_home, monkeypatch
|
|
|
74
74
|
from postgres_aiops.cli import app
|
|
75
75
|
|
|
76
76
|
fake = fake_pg(responses={"pg_stat_statements_reset": [{"reset": True}]})
|
|
77
|
-
import mcp_server.tools.queries as gov_queries
|
|
77
|
+
import postgres_aiops.mcp_server.tools.queries as gov_queries
|
|
78
78
|
|
|
79
79
|
monkeypatch.setattr(gov_queries, "_get_connection", lambda target=None: fake)
|
|
80
80
|
result = CliRunner().invoke(app, ["query", "reset"], input="y\ny\n")
|
|
@@ -87,7 +87,7 @@ def test_cli_query_reset_aborts_without_double_confirm(gov_home, monkeypatch, fa
|
|
|
87
87
|
from postgres_aiops.cli import app
|
|
88
88
|
|
|
89
89
|
fake = fake_pg()
|
|
90
|
-
import mcp_server.tools.queries as gov_queries
|
|
90
|
+
import postgres_aiops.mcp_server.tools.queries as gov_queries
|
|
91
91
|
|
|
92
92
|
monkeypatch.setattr(gov_queries, "_get_connection", lambda target=None: fake)
|
|
93
93
|
result = CliRunner().invoke(app, ["query", "reset"], input="y\nn\n")
|
|
@@ -15,13 +15,13 @@ from __future__ import annotations
|
|
|
15
15
|
import psycopg
|
|
16
16
|
import pytest
|
|
17
17
|
|
|
18
|
-
from mcp_server._shared import _UNDETERMINED_ERRORS
|
|
19
18
|
from postgres_aiops.config import TargetConfig
|
|
20
19
|
from postgres_aiops.connection import (
|
|
21
20
|
PgConnection,
|
|
22
21
|
PgConnectionLostError,
|
|
23
22
|
PgError,
|
|
24
23
|
)
|
|
24
|
+
from postgres_aiops.mcp_server._shared import _UNDETERMINED_ERRORS
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class _RaisingCursor:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Portable across the whole tool line: references ONLY
|
|
4
4
|
``postgres_aiops.governance.audit`` and binds state to an isolated home via
|
|
5
|
-
``POSTGRES_AIOPS_HOME``. No ops / cli / connection / mcp_server imports.
|
|
5
|
+
``POSTGRES_AIOPS_HOME``. No ops / cli / connection / postgres_aiops.mcp_server imports.
|
|
6
6
|
|
|
7
7
|
Exercises row writes with every column, query filters, stats aggregation,
|
|
8
8
|
size-based rotation + archive cleanup, permission hardening, in-place column
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Portable across the whole tool line: references ONLY
|
|
4
4
|
``postgres_aiops.governance.*`` and binds state to an isolated home via
|
|
5
5
|
``POSTGRES_AIOPS_HOME``. Every governed function under test is SYNTHETIC and
|
|
6
|
-
defined inline — no real ops / cli / connection / mcp_server tool is imported.
|
|
6
|
+
defined inline — no real ops / cli / connection / postgres_aiops.mcp_server tool is imported.
|
|
7
7
|
|
|
8
8
|
Exercises the async wrapper (audit parity with sync), the error-capture path
|
|
9
9
|
with secret redaction, BudgetExceeded propagation, positional /
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Portable across the whole tool line: references ONLY
|
|
4
4
|
``postgres_aiops.governance.patterns`` and binds state to an isolated home via
|
|
5
|
-
``POSTGRES_AIOPS_HOME``. No ops / cli / connection / mcp_server imports.
|
|
5
|
+
``POSTGRES_AIOPS_HOME``. No ops / cli / connection / postgres_aiops.mcp_server imports.
|
|
6
6
|
|
|
7
7
|
Exercises pattern loading + signature validation, the armable preconditions,
|
|
8
8
|
skill/tool/target matching, rate limiting, the circuit breaker's state
|
|
@@ -127,7 +127,7 @@ def test_real_write_tool_persists_priorstate_undo(gov_home, monkeypatch, fake_pg
|
|
|
127
127
|
indexdef = "CREATE INDEX idx_orders_ts ON public.orders USING btree (ts)"
|
|
128
128
|
fake = fake_pg(scalars={"pg_get_indexdef": indexdef})
|
|
129
129
|
|
|
130
|
-
from mcp_server.tools import remediation as gov
|
|
130
|
+
from postgres_aiops.mcp_server.tools import remediation as gov
|
|
131
131
|
|
|
132
132
|
monkeypatch.setattr(gov, "_get_connection", lambda target=None: fake)
|
|
133
133
|
result = gov.drop_index(name="idx_orders_ts")
|