postgres-aiops 0.6.0__tar.gz → 0.7.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.6.0 → postgres_aiops-0.7.0}/CHANGELOG.md +10 -0
- postgres_aiops-0.7.0/Dockerfile +9 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/PKG-INFO +2 -2
- postgres_aiops-0.7.0/glama.json +6 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/_shared.py +11 -7
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/tools/undo.py +15 -1
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/governance/decorators.py +10 -2
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/pyproject.toml +2 -2
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/server.json +2 -2
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_undo_executor.py +37 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/uv.lock +54 -51
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/.github/workflows/mcp-publish.yml +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/.github/workflows/publish.yml +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/.gitignore +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/LICENSE +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/README.md +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/RELEASE_NOTES.md +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/SECURITY.md +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/docs/VERIFICATION.md +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/__init__.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/server.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/tools/__init__.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/tools/activity.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/tools/analysis.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/tools/indexes.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/tools/queries.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/tools/remediation.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/tools/replication.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/tools/server.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/mcp_server/tools/tables.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/__init__.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/__init__.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/_common.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/_root.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/activity.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/analyze.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/doctor.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/index.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/init.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/overview.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/query.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/remediate.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/replication.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/secret.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/server.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/table.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/cli/undo.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/config.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/connection.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/doctor.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/governance/__init__.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/governance/audit.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/governance/budget.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/governance/outcome.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/governance/paths.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/governance/patterns.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/governance/policy.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/governance/sanitize.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/governance/undo.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/ops/__init__.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/ops/_util.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/ops/activity.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/ops/analysis.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/ops/indexes.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/ops/overview.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/ops/queries.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/ops/remediation.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/ops/replication.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/ops/server.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/ops/tables.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/postgres_aiops/secretstore.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/skills/postgres-aiops/SKILL.md +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/skills/postgres-aiops/references/agent-guardrails.md +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/skills/postgres-aiops/references/capabilities.md +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/skills/postgres-aiops/references/cli-reference.md +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/skills/postgres-aiops/references/setup-guide.md +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/smithery.yaml +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/conftest.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_analysis.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_cli_reads.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_cli_remediate.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_cli_secret.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_cli_writes.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_config.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_connection.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_connection_lost.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_connection_more.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_doctor.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_gov_audit.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_gov_decorators.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_gov_patterns.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_gov_policy.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_governance_persistence.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_init.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_mcp_tools.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_ops_more.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_optional_fields.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_reads.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_secretstore.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_secretstore_more.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_self_lockout.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_smoke.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_truncation.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_undo_replay.py +0 -0
- {postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/tests/test_writes.py +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.7.0 — 2026-08-02
|
|
4
|
+
|
|
5
|
+
### Changed (BREAKING)
|
|
6
|
+
- **Requires MCP SDK 2.0** (`mcp[cli]>=2.0,<3.0`). `mcp.server.fastmcp` no longer exists in 2.0; the server is now built with `MCPServer` and reports its package version in the stdio handshake.
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- **`undo apply` works from the CLI.** Every write tool is imported lazily inside its own CLI command, so a CLI-driven undo ran in a process where the inverse tool was never registered and failed with "inverse tool is not registered" — for every write tool. Only the MCP entry point, which imports the whole server, worked. Found while live-verifying against a real cluster.
|
|
10
|
+
- **An undetermined outcome is audited `unknown`, not `ok`.** The harness only classified a result as undetermined when the payload *also* carried an `error` key, so a write that looked successful but had not been confirmed was recorded as a success.
|
|
11
|
+
|
|
12
|
+
|
|
3
13
|
## v0.6.0 — 2026-07-21
|
|
4
14
|
|
|
5
15
|
### Changed (BREAKING)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
# Minimal image for Glama introspection: starts the MCP server over stdio.
|
|
3
|
+
# The tools/list introspection handshake needs no live PostgreSQL credentials.
|
|
4
|
+
FROM python:3.12-slim
|
|
5
|
+
|
|
6
|
+
RUN pip install --no-cache-dir postgres-aiops
|
|
7
|
+
|
|
8
|
+
# MCP server speaks JSON-RPC over stdio.
|
|
9
|
+
ENTRYPOINT ["postgres-aiops-mcp"]
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: postgres-aiops
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.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
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Requires-Python: >=3.11
|
|
9
9
|
Requires-Dist: cryptography>=42.0
|
|
10
|
-
Requires-Dist: mcp[cli]<
|
|
10
|
+
Requires-Dist: mcp[cli]<3.0,>=2.0
|
|
11
11
|
Requires-Dist: psycopg[binary]<4.0,>=3.1
|
|
12
12
|
Requires-Dist: pyyaml<7.0,>=6.0
|
|
13
13
|
Requires-Dist: rich<16.0,>=13.0
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
"""Shared MCP server primitives: the
|
|
1
|
+
"""Shared MCP server primitives: the MCPServer instance, connection helper,
|
|
2
2
|
error sanitisation, and the ``@tool_errors`` decorator.
|
|
3
3
|
|
|
4
4
|
Tool modules under ``mcp_server/tools/`` import ``mcp`` from here and register
|
|
5
5
|
their ``@mcp.tool()`` functions onto it. ``mcp_server/server.py`` then imports
|
|
6
6
|
those modules and runs the server.
|
|
7
7
|
|
|
8
|
-
Keep ``Optional[X]`` (never PEP 604 ``X | None``) in any
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Keep ``Optional[X]`` (never PEP 604 ``X | None``) in any reflected tool
|
|
9
|
+
signature. mcp>=2.0 accepts the union, but the legacy form is retained for
|
|
10
|
+
consistency and to stay safe under any pydantic reflection path (踩坑 #33 —
|
|
11
|
+
older mcp/pydantic eval'd the union to ``types.UnionType`` and crashed the
|
|
12
|
+
``issubclass`` check).
|
|
11
13
|
"""
|
|
12
14
|
|
|
13
15
|
import functools
|
|
@@ -17,8 +19,9 @@ from collections.abc import Callable
|
|
|
17
19
|
from pathlib import Path
|
|
18
20
|
from typing import Any, Optional
|
|
19
21
|
|
|
20
|
-
from mcp.server
|
|
22
|
+
from mcp.server import MCPServer
|
|
21
23
|
|
|
24
|
+
from postgres_aiops import __version__
|
|
22
25
|
from postgres_aiops.config import load_config
|
|
23
26
|
from postgres_aiops.connection import ConnectionManager, PgConnectionLostError, PgError
|
|
24
27
|
from postgres_aiops.governance import mark_unknown, sanitize
|
|
@@ -65,7 +68,7 @@ def tool_errors(shape: str = "dict") -> Callable:
|
|
|
65
68
|
"""Wrap a tool body in the canonical try/except → ``_safe_error`` pattern.
|
|
66
69
|
|
|
67
70
|
Place this *between* ``@governed_tool`` and the function so the audit
|
|
68
|
-
decorator and
|
|
71
|
+
decorator and MCPServer still see the original signature.
|
|
69
72
|
"""
|
|
70
73
|
|
|
71
74
|
def decorator(func: Callable) -> Callable:
|
|
@@ -94,8 +97,9 @@ def tool_errors(shape: str = "dict") -> Callable:
|
|
|
94
97
|
return decorator
|
|
95
98
|
|
|
96
99
|
|
|
97
|
-
mcp =
|
|
100
|
+
mcp = MCPServer(
|
|
98
101
|
"postgres-aiops",
|
|
102
|
+
version=__version__,
|
|
99
103
|
instructions=(
|
|
100
104
|
"Governed PostgreSQL DBA operations: a one-shot cluster "
|
|
101
105
|
"'overview'; server reads (version/settings/extensions/databases/roles); "
|
|
@@ -21,8 +21,22 @@ from postgres_aiops.governance.undo import get_undo_store
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def _resolve_tool(name: str) -> Any:
|
|
24
|
-
"""Return the governed callable registered under ``name`` (or None).
|
|
24
|
+
"""Return the governed callable registered under ``name`` (or None).
|
|
25
|
+
|
|
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
|
|
28
|
+
its own CLI command and so is absent here. Without forcing a full load,
|
|
29
|
+
``undo_apply`` would fail with "inverse tool not registered" for every write
|
|
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`` —
|
|
32
|
+
which registers every ``@mcp.tool()`` as an import side effect — and retry.
|
|
33
|
+
Imported lazily to avoid the undo↔server cycle.
|
|
34
|
+
"""
|
|
25
35
|
tool = mcp._tool_manager._tools.get(name)
|
|
36
|
+
if tool is None:
|
|
37
|
+
import mcp_server.server # noqa: F401 — registers all tools as a side effect
|
|
38
|
+
|
|
39
|
+
tool = mcp._tool_manager._tools.get(name)
|
|
26
40
|
return getattr(tool, "fn", None) if tool else None
|
|
27
41
|
|
|
28
42
|
|
|
@@ -382,8 +382,16 @@ def _finalize(state: _CallState) -> None:
|
|
|
382
382
|
# compliance exception reports are built from this status. 'unknown' is a
|
|
383
383
|
# distinct verdict, not a softer 'error': the request may have taken effect,
|
|
384
384
|
# so the row must not assert a failure the tool cannot actually vouch for.
|
|
385
|
-
if state.status == "ok" and isinstance(state.result, dict)
|
|
386
|
-
|
|
385
|
+
if state.status == "ok" and isinstance(state.result, dict):
|
|
386
|
+
# is_unknown is checked FIRST and independently of ``error``. A write
|
|
387
|
+
# can be undetermined while looking perfectly successful — a Proxmox
|
|
388
|
+
# task that has not finished yet returns a normal payload — and gating
|
|
389
|
+
# this on ``error`` audited those as 'ok', re-creating the very lie the
|
|
390
|
+
# unknown verdict exists to prevent.
|
|
391
|
+
if is_unknown(state.result):
|
|
392
|
+
state.status = "unknown"
|
|
393
|
+
elif state.result.get("error"):
|
|
394
|
+
state.status = "error"
|
|
387
395
|
|
|
388
396
|
duration = int((time.time() - state.start) * 1000)
|
|
389
397
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "postgres-aiops"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.7.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"
|
|
@@ -16,7 +16,7 @@ dependencies = [
|
|
|
16
16
|
"rich>=13.0,<16.0",
|
|
17
17
|
"pyyaml>=6.0,<7.0",
|
|
18
18
|
"cryptography>=42.0",
|
|
19
|
-
"mcp[cli]>=
|
|
19
|
+
"mcp[cli]>=2.0,<3.0",
|
|
20
20
|
]
|
|
21
21
|
|
|
22
22
|
[dependency-groups]
|
|
@@ -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.7.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "pypi",
|
|
14
14
|
"identifier": "postgres-aiops",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.7.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|
|
@@ -172,3 +172,40 @@ def test_undo_apply_audits_both_wrapper_and_inverse(gov_home):
|
|
|
172
172
|
conn.close()
|
|
173
173
|
assert "undo_apply" in tools
|
|
174
174
|
assert "_undo_probe" in tools
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@pytest.mark.unit
|
|
178
|
+
def test_resolve_tool_loads_full_registry_under_cli_only_import():
|
|
179
|
+
"""Regression (live-found 2026-07-31): CLI ``undo apply`` runs in a process
|
|
180
|
+
that imports only ``mcp_server.tools.undo`` — every write tool is imported
|
|
181
|
+
lazily inside its own CLI command, so the inverse was "not registered" and
|
|
182
|
+
CLI-initiated undo failed for every write tool. ``_resolve_tool`` must force
|
|
183
|
+
a full server load on a miss.
|
|
184
|
+
|
|
185
|
+
Reproduced in a FRESH interpreter, because this pytest process has already
|
|
186
|
+
imported the whole server, which would mask the bug.
|
|
187
|
+
"""
|
|
188
|
+
import subprocess
|
|
189
|
+
import sys
|
|
190
|
+
import textwrap
|
|
191
|
+
|
|
192
|
+
script = textwrap.dedent(
|
|
193
|
+
"""
|
|
194
|
+
from mcp_server._shared import mcp
|
|
195
|
+
import mcp_server.tools.undo as u
|
|
196
|
+
before = set(mcp._tool_manager._tools)
|
|
197
|
+
# A miss must trigger the full-registry load, not just return None.
|
|
198
|
+
u._resolve_tool("__definitely_not_a_tool__")
|
|
199
|
+
after = set(mcp._tool_manager._tools)
|
|
200
|
+
new = after - before
|
|
201
|
+
assert new, "fallback loaded no additional tools"
|
|
202
|
+
sample = sorted(new)[0]
|
|
203
|
+
assert u._resolve_tool(sample) is not None, sample
|
|
204
|
+
print("OK", len(before), len(after))
|
|
205
|
+
"""
|
|
206
|
+
)
|
|
207
|
+
r = subprocess.run(
|
|
208
|
+
[sys.executable, "-c", script], capture_output=True, text=True
|
|
209
|
+
)
|
|
210
|
+
assert r.returncode == 0, r.stderr
|
|
211
|
+
assert r.stdout.startswith("OK"), r.stdout
|
|
@@ -48,15 +48,6 @@ wheels = [
|
|
|
48
48
|
{ url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" },
|
|
49
49
|
]
|
|
50
50
|
|
|
51
|
-
[[package]]
|
|
52
|
-
name = "certifi"
|
|
53
|
-
version = "2026.6.17"
|
|
54
|
-
source = { registry = "https://pypi.org/simple" }
|
|
55
|
-
sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" }
|
|
56
|
-
wheels = [
|
|
57
|
-
{ url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" },
|
|
58
|
-
]
|
|
59
|
-
|
|
60
51
|
[[package]]
|
|
61
52
|
name = "cffi"
|
|
62
53
|
version = "2.1.0"
|
|
@@ -331,40 +322,32 @@ wheels = [
|
|
|
331
322
|
]
|
|
332
323
|
|
|
333
324
|
[[package]]
|
|
334
|
-
name = "
|
|
335
|
-
version = "
|
|
325
|
+
name = "httpcore2"
|
|
326
|
+
version = "2.9.1"
|
|
336
327
|
source = { registry = "https://pypi.org/simple" }
|
|
337
328
|
dependencies = [
|
|
338
|
-
{ name = "certifi" },
|
|
339
329
|
{ name = "h11" },
|
|
330
|
+
{ name = "truststore" },
|
|
340
331
|
]
|
|
341
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
332
|
+
sdist = { url = "https://files.pythonhosted.org/packages/39/a8/20ed1ed79cbc2ecdf5301c0968ab7c85547212e2a7bd126ddd2d986e206e/httpcore2-2.9.1.tar.gz", hash = "sha256:4d8acbf8b306f48c9d6046591fd5ba4037d1b1b1000d140fc2c3eab1e9a0c0e2", size = 67089, upload-time = "2026-07-24T09:21:03.867Z" }
|
|
342
333
|
wheels = [
|
|
343
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
334
|
+
{ url = "https://files.pythonhosted.org/packages/9f/fb/46c52b781975c335a2bcf1072c7bbc007cbdc8d674217f5ee1daba2c848b/httpcore2-2.9.1-py3-none-any.whl", hash = "sha256:6182472379e855fe4221246a2bb7ecede403bc61c6798062ae1787d051ccde26", size = 82809, upload-time = "2026-07-24T09:21:01.178Z" },
|
|
344
335
|
]
|
|
345
336
|
|
|
346
337
|
[[package]]
|
|
347
|
-
name = "
|
|
348
|
-
version = "
|
|
338
|
+
name = "httpx2"
|
|
339
|
+
version = "2.9.1"
|
|
349
340
|
source = { registry = "https://pypi.org/simple" }
|
|
350
341
|
dependencies = [
|
|
351
342
|
{ name = "anyio" },
|
|
352
|
-
{ name = "
|
|
353
|
-
{ name = "httpcore" },
|
|
343
|
+
{ name = "httpcore2" },
|
|
354
344
|
{ name = "idna" },
|
|
345
|
+
{ name = "truststore" },
|
|
346
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
|
355
347
|
]
|
|
356
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
357
|
-
wheels = [
|
|
358
|
-
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
|
359
|
-
]
|
|
360
|
-
|
|
361
|
-
[[package]]
|
|
362
|
-
name = "httpx-sse"
|
|
363
|
-
version = "0.4.3"
|
|
364
|
-
source = { registry = "https://pypi.org/simple" }
|
|
365
|
-
sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" }
|
|
348
|
+
sdist = { url = "https://files.pythonhosted.org/packages/21/14/38128fbafd7e0ed41d874df6c9a653d47c2d111cfe59e2b4ac95161b4abd/httpx2-2.9.1.tar.gz", hash = "sha256:1932a768737e3666291582833da748cc4e563c337cf96706fccc04fa6e58764a", size = 95458, upload-time = "2026-07-24T09:21:04.972Z" }
|
|
366
349
|
wheels = [
|
|
367
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
350
|
+
{ url = "https://files.pythonhosted.org/packages/13/b8/cfd91c4ab9134d386d48f0b6ac662ff3d4be6efdee59ee1c67ebc3c0487c/httpx2-2.9.1-py3-none-any.whl", hash = "sha256:1820fe14a9ab1107bfeff39259987429450b070ec0ff38cc87eb0d8c97fdc71a", size = 91191, upload-time = "2026-07-24T09:21:02.6Z" },
|
|
368
351
|
]
|
|
369
352
|
|
|
370
353
|
[[package]]
|
|
@@ -426,15 +409,15 @@ wheels = [
|
|
|
426
409
|
|
|
427
410
|
[[package]]
|
|
428
411
|
name = "mcp"
|
|
429
|
-
version = "
|
|
412
|
+
version = "2.0.0"
|
|
430
413
|
source = { registry = "https://pypi.org/simple" }
|
|
431
414
|
dependencies = [
|
|
432
415
|
{ name = "anyio" },
|
|
433
|
-
{ name = "
|
|
434
|
-
{ name = "httpx-sse" },
|
|
416
|
+
{ name = "httpx2" },
|
|
435
417
|
{ name = "jsonschema" },
|
|
418
|
+
{ name = "mcp-types" },
|
|
419
|
+
{ name = "opentelemetry-api" },
|
|
436
420
|
{ name = "pydantic" },
|
|
437
|
-
{ name = "pydantic-settings" },
|
|
438
421
|
{ name = "pyjwt", extra = ["crypto"] },
|
|
439
422
|
{ name = "python-multipart" },
|
|
440
423
|
{ name = "pywin32", marker = "sys_platform == 'win32'" },
|
|
@@ -444,9 +427,9 @@ dependencies = [
|
|
|
444
427
|
{ name = "typing-inspection" },
|
|
445
428
|
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
|
|
446
429
|
]
|
|
447
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
430
|
+
sdist = { url = "https://files.pythonhosted.org/packages/74/33/32d4dff2c95bb5d897c3ef4c83649a08996b17b58f0a326d2495d4c81179/mcp-2.0.0.tar.gz", hash = "sha256:0f440e735c13ece8bb19bc62cf0b86f4313448432fbb77d35e14034f4e050728", size = 1662284, upload-time = "2026-07-28T13:45:32.346Z" }
|
|
448
431
|
wheels = [
|
|
449
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
432
|
+
{ url = "https://files.pythonhosted.org/packages/67/72/7d7897418912c1d12e87556630dfb7bf0eac71160e9bef8b447960804ee3/mcp-2.0.0-py3-none-any.whl", hash = "sha256:1cb4c75d2d2c7b8c1d756355e5d82a39f2822cc7f13e22a2051d7ca3592349d6", size = 349980, upload-time = "2026-07-28T13:45:28.853Z" },
|
|
450
433
|
]
|
|
451
434
|
|
|
452
435
|
[package.optional-dependencies]
|
|
@@ -455,6 +438,19 @@ cli = [
|
|
|
455
438
|
{ name = "typer" },
|
|
456
439
|
]
|
|
457
440
|
|
|
441
|
+
[[package]]
|
|
442
|
+
name = "mcp-types"
|
|
443
|
+
version = "2.0.0"
|
|
444
|
+
source = { registry = "https://pypi.org/simple" }
|
|
445
|
+
dependencies = [
|
|
446
|
+
{ name = "pydantic" },
|
|
447
|
+
{ name = "typing-extensions" },
|
|
448
|
+
]
|
|
449
|
+
sdist = { url = "https://files.pythonhosted.org/packages/bb/56/9b8e1c152f61f6c6b07c4b5896c88c7d0ae90bac6ee6306f852fcc5c1eb0/mcp_types-2.0.0.tar.gz", hash = "sha256:d7d939b9285c9961ae8866ba75ef85da34d12bafe276efbf4eb6a131786d8379", size = 66632, upload-time = "2026-07-28T13:45:33.804Z" }
|
|
450
|
+
wheels = [
|
|
451
|
+
{ url = "https://files.pythonhosted.org/packages/f5/4c/c78d78c3d52b0ac594ad7cc8ef5972adfe070e3597a8a4c6ce0cd39196ea/mcp_types-2.0.0-py3-none-any.whl", hash = "sha256:6b2de797ca2797f568b79529e1b25948e34de511bcc0bd82fef1039a6d1b8eb0", size = 69649, upload-time = "2026-07-28T13:45:30.713Z" },
|
|
452
|
+
]
|
|
453
|
+
|
|
458
454
|
[[package]]
|
|
459
455
|
name = "mdurl"
|
|
460
456
|
version = "0.1.2"
|
|
@@ -464,6 +460,18 @@ wheels = [
|
|
|
464
460
|
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
|
465
461
|
]
|
|
466
462
|
|
|
463
|
+
[[package]]
|
|
464
|
+
name = "opentelemetry-api"
|
|
465
|
+
version = "1.44.0"
|
|
466
|
+
source = { registry = "https://pypi.org/simple" }
|
|
467
|
+
dependencies = [
|
|
468
|
+
{ name = "typing-extensions" },
|
|
469
|
+
]
|
|
470
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ee/8b/aa9e2d8b8dfa7c946f7dec5d1f8f6ba8eca062f43509a06bdb5ce93d26c0/opentelemetry_api-1.44.0.tar.gz", hash = "sha256:67647e5e9566edcf421166fdf022b3537f818635daa852b289e34604dc6fb33a", size = 72406, upload-time = "2026-07-16T15:25:32.678Z" }
|
|
471
|
+
wheels = [
|
|
472
|
+
{ url = "https://files.pythonhosted.org/packages/ca/6f/a04e900f465ff3221ccc395522503e2d10e79fa21f2723c8e177aae1e0d1/opentelemetry_api-1.44.0-py3-none-any.whl", hash = "sha256:94b98c893a91b88657eaac1e3ba89618cdb85be6918196705354f34728b2cdef", size = 60018, upload-time = "2026-07-16T15:25:11.657Z" },
|
|
473
|
+
]
|
|
474
|
+
|
|
467
475
|
[[package]]
|
|
468
476
|
name = "packaging"
|
|
469
477
|
version = "26.2"
|
|
@@ -484,7 +492,7 @@ wheels = [
|
|
|
484
492
|
|
|
485
493
|
[[package]]
|
|
486
494
|
name = "postgres-aiops"
|
|
487
|
-
version = "0.
|
|
495
|
+
version = "0.7.0"
|
|
488
496
|
source = { editable = "." }
|
|
489
497
|
dependencies = [
|
|
490
498
|
{ name = "cryptography" },
|
|
@@ -505,7 +513,7 @@ dev = [
|
|
|
505
513
|
[package.metadata]
|
|
506
514
|
requires-dist = [
|
|
507
515
|
{ name = "cryptography", specifier = ">=42.0" },
|
|
508
|
-
{ name = "mcp", extras = ["cli"], specifier = ">=
|
|
516
|
+
{ name = "mcp", extras = ["cli"], specifier = ">=2.0,<3.0" },
|
|
509
517
|
{ name = "psycopg", extras = ["binary"], specifier = ">=3.1,<4.0" },
|
|
510
518
|
{ name = "pyyaml", specifier = ">=6.0,<7.0" },
|
|
511
519
|
{ name = "rich", specifier = ">=13.0,<16.0" },
|
|
@@ -714,20 +722,6 @@ wheels = [
|
|
|
714
722
|
{ url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" },
|
|
715
723
|
]
|
|
716
724
|
|
|
717
|
-
[[package]]
|
|
718
|
-
name = "pydantic-settings"
|
|
719
|
-
version = "2.14.2"
|
|
720
|
-
source = { registry = "https://pypi.org/simple" }
|
|
721
|
-
dependencies = [
|
|
722
|
-
{ name = "pydantic" },
|
|
723
|
-
{ name = "python-dotenv" },
|
|
724
|
-
{ name = "typing-inspection" },
|
|
725
|
-
]
|
|
726
|
-
sdist = { url = "https://files.pythonhosted.org/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", size = 237700, upload-time = "2026-06-19T13:44:56.324Z" }
|
|
727
|
-
wheels = [
|
|
728
|
-
{ url = "https://files.pythonhosted.org/packages/77/c1/6e422f34e569cf8e18df68d1939c81c099d2b61e4f7d9621c8a77560799c/pydantic_settings-2.14.2-py3-none-any.whl", hash = "sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440", size = 61715, upload-time = "2026-06-19T13:44:55.02Z" },
|
|
729
|
-
]
|
|
730
|
-
|
|
731
725
|
[[package]]
|
|
732
726
|
name = "pygments"
|
|
733
727
|
version = "2.20.0"
|
|
@@ -1140,6 +1134,15 @@ wheels = [
|
|
|
1140
1134
|
{ url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
|
|
1141
1135
|
]
|
|
1142
1136
|
|
|
1137
|
+
[[package]]
|
|
1138
|
+
name = "truststore"
|
|
1139
|
+
version = "0.10.4"
|
|
1140
|
+
source = { registry = "https://pypi.org/simple" }
|
|
1141
|
+
sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" }
|
|
1142
|
+
wheels = [
|
|
1143
|
+
{ url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" },
|
|
1144
|
+
]
|
|
1145
|
+
|
|
1143
1146
|
[[package]]
|
|
1144
1147
|
name = "typer"
|
|
1145
1148
|
version = "0.26.8"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/skills/postgres-aiops/references/agent-guardrails.md
RENAMED
|
File without changes
|
{postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/skills/postgres-aiops/references/capabilities.md
RENAMED
|
File without changes
|
{postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/skills/postgres-aiops/references/cli-reference.md
RENAMED
|
File without changes
|
{postgres_aiops-0.6.0 → postgres_aiops-0.7.0}/skills/postgres-aiops/references/setup-guide.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|