mcp-postgresql-ops 3.4.0__tar.gz → 3.4.2__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 (76) hide show
  1. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/workflows/pypi-publish.yml +33 -8
  2. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/workflows/test.yml +1 -1
  3. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/PKG-INFO +4 -4
  4. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/README.md +3 -3
  5. mcp_postgresql_ops-3.4.2/scripts/check_release.py +69 -0
  6. mcp_postgresql_ops-3.4.2/scripts/publish_release.py +139 -0
  7. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops.egg-info/PKG-INFO +4 -4
  8. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops.egg-info/SOURCES.txt +3 -0
  9. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops.egg-info/scm_file_list.json +3 -0
  10. mcp_postgresql_ops-3.4.2/src/mcp_postgresql_ops.egg-info/scm_version.json +8 -0
  11. mcp_postgresql_ops-3.4.2/tests/test_release.py +102 -0
  12. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/uv.lock +3 -3
  13. mcp_postgresql_ops-3.4.0/src/mcp_postgresql_ops.egg-info/scm_version.json +0 -8
  14. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.env.example +0 -0
  15. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/CONTRIBUTING.md +0 -0
  16. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/FUNDING.yml +0 -0
  17. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  18. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  19. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/ISSUE_TEMPLATE/documentation.md +0 -0
  20. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  21. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/ISSUE_TEMPLATE/question.md +0 -0
  22. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/copilot-instructions.md +0 -0
  23. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/dependabot.yml +0 -0
  24. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.github/pull_request_template.md +0 -0
  25. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.gitignore +0 -0
  26. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.gitleaks.toml +0 -0
  27. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.pre-commit-config.yaml +0 -0
  28. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/.python-version +0 -0
  29. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/Dockerfile.MCP-Server +0 -0
  30. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/Dockerfile.MCPO-Proxy +0 -0
  31. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/LICENSE +0 -0
  32. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/MANIFEST.in +0 -0
  33. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/SECURITY.md +0 -0
  34. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/_config.yml +0 -0
  35. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/_includes/head-custom-google-analytics.html +0 -0
  36. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/build-mcp-server-docker-image.sh +0 -0
  37. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/build-mcpo-server-docker-image.sh +0 -0
  38. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/docker-compose.custom-db.yml +0 -0
  39. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/docker-compose.yml +0 -0
  40. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/img/MCP-Workflow-of-Quickstart-Tutorial.png +0 -0
  41. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/img/screenshot-000.png +0 -0
  42. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/img/screenshot-001.png +0 -0
  43. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/img/screenshot-002.png +0 -0
  44. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/img/screenshot-004.png +0 -0
  45. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/img/screenshot-005.png +0 -0
  46. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/img/screenshot-claude-desktop-airflow-connections-html.png +0 -0
  47. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/img/screenshot-claude-desktop-mermaid-diagram.png +0 -0
  48. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/img/screenshot-claude-desktop-question-001.png +0 -0
  49. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/img/screenshot-swagger-api.png +0 -0
  50. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/mcp-config.json.http +0 -0
  51. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/mcp-config.json.stdio +0 -0
  52. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/pyproject.toml +0 -0
  53. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/run-mcp-inspector-local.sh +0 -0
  54. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/run-mcp-inspector-pypi.sh +0 -0
  55. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/scripts/create-test-data.sh +0 -0
  56. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/scripts/create-test-data.sql +0 -0
  57. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/scripts/enable-pgsql-extensions.sh +0 -0
  58. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/scripts/mcp-server-docker-cmd.sh +0 -0
  59. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/setup.cfg +0 -0
  60. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops/__init__.py +0 -0
  61. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops/__main__.py +0 -0
  62. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops/functions.py +0 -0
  63. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops/mcp_main.py +0 -0
  64. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops/prompt_template.md +0 -0
  65. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops/version_compat.py +0 -0
  66. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops.egg-info/dependency_links.txt +0 -0
  67. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops.egg-info/entry_points.txt +0 -0
  68. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops.egg-info/requires.txt +0 -0
  69. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/src/mcp_postgresql_ops.egg-info/top_level.txt +0 -0
  70. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/tests/__init__.py +0 -0
  71. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/tests/conftest.py +0 -0
  72. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/tests/docker/docker-compose.test.yml +0 -0
  73. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/tests/docker/init-test-db.sql +0 -0
  74. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/tests/test_tool_schema_null_defaults.py +0 -0
  75. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/tests/test_tools_integration.py +0 -0
  76. {mcp_postgresql_ops-3.4.0 → mcp_postgresql_ops-3.4.2}/tests/test_version_compat.py +0 -0
@@ -1,7 +1,4 @@
1
- # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2
- # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3
-
4
- name: Publish to PyPI on tag
1
+ name: Publish Packages and GitHub Release on Tag
5
2
 
6
3
  on:
7
4
  push:
@@ -9,7 +6,6 @@ on:
9
6
 
10
7
  permissions:
11
8
  contents: read
12
- id-token: write
13
9
 
14
10
  jobs:
15
11
  build:
@@ -26,10 +22,15 @@ jobs:
26
22
  run: pip install --upgrade build
27
23
  - name: Build distributions
28
24
  run: python -m build
25
+ - name: Verify tag and distribution versions
26
+ env:
27
+ RELEASE_TAG: ${{ github.ref_name }}
28
+ run: python -m scripts.check_release "$RELEASE_TAG" dist
29
29
  - uses: actions/upload-artifact@v7
30
30
  with:
31
31
  name: dist
32
32
  path: dist/
33
+ if-no-files-found: error
33
34
 
34
35
  publish-to-testpypi:
35
36
  name: Publish to TestPyPI
@@ -55,8 +56,7 @@ jobs:
55
56
  name: Publish to PyPI
56
57
  if: startsWith(github.ref, 'refs/tags/')
57
58
  runs-on: ubuntu-latest
58
- needs: build
59
- #needs: publish-to-testpypi
59
+ needs: publish-to-testpypi
60
60
  permissions:
61
61
  id-token: write
62
62
  environment:
@@ -70,4 +70,29 @@ jobs:
70
70
  - name: Publish to PyPI
71
71
  uses: pypa/gh-action-pypi-publish@release/v1
72
72
  with:
73
- repository-url: https://upload.pypi.org/legacy/
73
+ repository-url: https://upload.pypi.org/legacy/
74
+
75
+ release:
76
+ name: Publish GitHub Release
77
+ needs: publish-to-pypi
78
+ runs-on: ubuntu-latest
79
+ timeout-minutes: 5
80
+ permissions:
81
+ contents: write
82
+ concurrency:
83
+ group: github-release-${{ github.repository }}
84
+ cancel-in-progress: false
85
+ queue: max
86
+ env:
87
+ GH_TOKEN: ${{ github.token }}
88
+ RELEASE_TAG: ${{ github.ref_name }}
89
+ steps:
90
+ - uses: actions/checkout@v7
91
+ with:
92
+ persist-credentials: false
93
+ fetch-depth: 0
94
+ - uses: actions/setup-python@v7
95
+ with:
96
+ python-version: "3.12.x"
97
+ - name: Publish release with commit messages
98
+ run: python -m scripts.publish_release "$RELEASE_TAG"
@@ -32,7 +32,7 @@ jobs:
32
32
  run: uv sync --extra dev
33
33
 
34
34
  - name: Run unit tests
35
- run: uv run pytest tests/test_version_compat.py tests/test_tool_schema_null_defaults.py -v --tb=short
35
+ run: uv run pytest tests/test_version_compat.py tests/test_tool_schema_null_defaults.py tests/test_release.py -v --tb=short
36
36
  integration-tests:
37
37
  name: Integration Tests (PG 12-18)
38
38
  runs-on: ubuntu-latest
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-postgresql-ops
3
- Version: 3.4.0
3
+ Version: 3.4.2
4
4
  Summary: Add your description here
5
5
  Author-email: JungJungIn <call518@gmail.com>
6
6
  Requires-Python: >=3.12
@@ -34,7 +34,7 @@ Dynamic: license-file
34
34
 
35
35
  ## Architecture & Internal (DeepWiki)
36
36
 
37
- [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/call518/MCP-PostgreSQL-Ops)
37
+ [![Ask DeepWiki](https://img.shields.io/badge/Ask-DeepWiki-blue)](https://deepwiki.com/call518/MCP-PostgreSQL-Ops)
38
38
 
39
39
  ---
40
40
 
@@ -71,7 +71,7 @@ Dynamic: license-file
71
71
 
72
72
  ## Tool Usage Examples
73
73
 
74
- ### 📸 **[More Examples with Screenshots →](https://github.com/call518/MCP-PostgreSQL-Ops/wiki/Tool-Usage-Example)**
74
+ ### 📸 **[More Examples with Screenshots →](https://github.com/call518/MCP-PostgreSQL-Ops/wiki)**
75
75
 
76
76
  ---
77
77
 
@@ -181,7 +181,7 @@ http://localhost:3003/
181
181
 
182
182
  #### 📖 **Next Steps:**
183
183
  - Browse the **[Example Queries section](#usage-examples)** below for more query examples
184
- - Check out **[Tool Usage Examples with Screenshots](https://github.com/call518/MCP-PostgreSQL-Ops/wiki/Tool-Usage-Example)** for visual guides
184
+ - Check out **[Tool Usage Examples with Screenshots](https://github.com/call518/MCP-PostgreSQL-Ops/wiki)** for visual guides
185
185
  - Explore the **[Tool Compatibility Matrix](#tool-compatibility-matrix)** to understand available features
186
186
 
187
187
  ---
@@ -16,7 +16,7 @@
16
16
 
17
17
  ## Architecture & Internal (DeepWiki)
18
18
 
19
- [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/call518/MCP-PostgreSQL-Ops)
19
+ [![Ask DeepWiki](https://img.shields.io/badge/Ask-DeepWiki-blue)](https://deepwiki.com/call518/MCP-PostgreSQL-Ops)
20
20
 
21
21
  ---
22
22
 
@@ -53,7 +53,7 @@
53
53
 
54
54
  ## Tool Usage Examples
55
55
 
56
- ### 📸 **[More Examples with Screenshots →](https://github.com/call518/MCP-PostgreSQL-Ops/wiki/Tool-Usage-Example)**
56
+ ### 📸 **[More Examples with Screenshots →](https://github.com/call518/MCP-PostgreSQL-Ops/wiki)**
57
57
 
58
58
  ---
59
59
 
@@ -163,7 +163,7 @@ http://localhost:3003/
163
163
 
164
164
  #### 📖 **Next Steps:**
165
165
  - Browse the **[Example Queries section](#usage-examples)** below for more query examples
166
- - Check out **[Tool Usage Examples with Screenshots](https://github.com/call518/MCP-PostgreSQL-Ops/wiki/Tool-Usage-Example)** for visual guides
166
+ - Check out **[Tool Usage Examples with Screenshots](https://github.com/call518/MCP-PostgreSQL-Ops/wiki)** for visual guides
167
167
  - Explore the **[Tool Compatibility Matrix](#tool-compatibility-matrix)** to understand available features
168
168
 
169
169
  ---
@@ -0,0 +1,69 @@
1
+ """Require both distribution metadata versions to match the release tag."""
2
+
3
+ import argparse
4
+ import re
5
+ import tarfile
6
+ import zipfile
7
+ from email.parser import BytesParser
8
+ from pathlib import Path
9
+
10
+
11
+ def release_version(tag: str) -> str:
12
+ """Validate a canonical release tag and return its package version."""
13
+ number = r"(?:0|[1-9][0-9]*)"
14
+ if not re.fullmatch(rf"{number}\.{number}\.{number}", tag):
15
+ raise ValueError("Use a numeric major.minor.patch tag such as 0.5.7")
16
+ return tag
17
+
18
+
19
+ def check_release(tag: str, directory: Path) -> None:
20
+ """Validate a canonical release tag and the wheel/sdist prepared for upload."""
21
+ version = release_version(tag)
22
+ wheels = list(directory.glob("*.whl"))
23
+ sdists = list(directory.glob("*.tar.gz"))
24
+ if len(wheels) != 1 or len(sdists) != 1:
25
+ raise ValueError("Expected exactly one wheel and one source distribution")
26
+
27
+ with zipfile.ZipFile(wheels[0]) as archive:
28
+ metadata_files = [
29
+ name for name in archive.namelist() if name.endswith(".dist-info/METADATA")
30
+ ]
31
+ if len(metadata_files) != 1:
32
+ raise ValueError("Expected one wheel metadata file")
33
+ wheel_metadata = archive.read(metadata_files[0])
34
+ with tarfile.open(sdists[0], "r:gz") as archive:
35
+ metadata_files = [
36
+ item
37
+ for item in archive.getmembers()
38
+ if len(Path(item.name).parts) == 2 and item.name.endswith("/PKG-INFO")
39
+ ]
40
+ if len(metadata_files) != 1:
41
+ raise ValueError("Expected one source distribution metadata file")
42
+ stream = archive.extractfile(metadata_files[0])
43
+ if stream is None:
44
+ raise ValueError("Source distribution metadata is not a file")
45
+ with stream:
46
+ sdist_metadata = stream.read()
47
+
48
+ for path, raw in zip((wheels[0], sdists[0]), (wheel_metadata, sdist_metadata)):
49
+ metadata = BytesParser().parsebytes(raw)
50
+ if metadata.get("Name") != "mcp-postgresql-ops" or metadata.get("Version") != version:
51
+ raise ValueError(f"{path.name}: expected mcp-postgresql-ops {version}")
52
+ print(f"Verified {path.name}: mcp-postgresql-ops {version}")
53
+
54
+
55
+ def main() -> None:
56
+ """Check release artifacts without uploading or modifying them."""
57
+ parser = argparse.ArgumentParser(description=__doc__)
58
+ parser.add_argument("tag")
59
+ parser.add_argument("directory", type=Path)
60
+ args = parser.parse_args()
61
+ try:
62
+ check_release(args.tag, args.directory)
63
+ except ValueError as exc:
64
+ parser.exit(1, f"Release validation failed: {exc}\n")
65
+
66
+
67
+ if __name__ == "__main__":
68
+ main()
69
+
@@ -0,0 +1,139 @@
1
+ """Publish a tag's GitHub Release after package publication has succeeded."""
2
+
3
+ import argparse
4
+ import json
5
+ import os
6
+ import re
7
+ import subprocess
8
+ import tempfile
9
+ from pathlib import Path
10
+ from typing import Any
11
+ from urllib.parse import quote
12
+
13
+ from scripts.check_release import release_version
14
+
15
+
16
+ def command(*args: str) -> str:
17
+ """Run an argument list without interpreting commit messages as shell code."""
18
+ return subprocess.run(
19
+ args, check=True, capture_output=True, text=True
20
+ ).stdout.strip()
21
+
22
+
23
+ def release_notes(repository: str, tag: str, previous: str | None) -> str:
24
+ """Collect full commit messages in oldest-first topological order."""
25
+ current = command("git", "rev-parse", "--verify", f"refs/tags/{tag}^{{commit}}")
26
+ revision = current
27
+ if previous:
28
+ base = command(
29
+ "git", "rev-parse", "--verify", f"refs/tags/{previous}^{{commit}}"
30
+ )
31
+ command("git", "merge-base", "--is-ancestor", base, current)
32
+ revision = f"{base}..{current}"
33
+ raw = command(
34
+ "git",
35
+ "log",
36
+ "--reverse",
37
+ "--topo-order",
38
+ "--format=%H%x00%B%x00",
39
+ revision,
40
+ "--",
41
+ )
42
+ parts = raw.split("\0")
43
+ notes = [
44
+ f"## {tag}",
45
+ f"Changes since {previous}." if previous else "Initial release.",
46
+ "",
47
+ ]
48
+ for index in range(0, len(parts) - 1, 2):
49
+ sha, message = parts[index].strip(), parts[index + 1].strip()
50
+ # A longer fence preserves messages containing Markdown code fences.
51
+ fence = "`" * max(
52
+ 3, max((len(m) + 1 for m in re.findall(r"`+", message)), default=3)
53
+ )
54
+ notes.extend(
55
+ [
56
+ f"### [{sha[:7]}](https://github.com/{repository}/commit/{sha})",
57
+ f"{fence}text",
58
+ message,
59
+ fence,
60
+ "",
61
+ ]
62
+ )
63
+ if previous:
64
+ notes.append(
65
+ f"[Full Changelog](https://github.com/{repository}/compare/{quote(previous, safe='')}...{quote(tag, safe='')})"
66
+ )
67
+ return "\n".join(notes) + "\n"
68
+
69
+
70
+ def publish_release(repository: str, tag: str) -> None:
71
+ """Create a release once, using the last published release as the baseline."""
72
+ release_version(tag)
73
+ current = command("git", "rev-parse", "--verify", f"refs/tags/{tag}^{{commit}}")
74
+ remote = command("gh", "api", f"repos/{repository}/commits/{tag}", "--jq", ".sha")
75
+ if current != remote or current != command("git", "rev-parse", "HEAD"):
76
+ raise ValueError(
77
+ "The remote tag, local tag, and checked-out commit must match."
78
+ )
79
+ pages = json.loads(
80
+ command(
81
+ "gh",
82
+ "api",
83
+ "--paginate",
84
+ "--slurp",
85
+ f"repos/{repository}/releases?per_page=100",
86
+ )
87
+ )
88
+ releases: list[dict[str, Any]] = [release for page in pages for release in page]
89
+ existing = next(
90
+ (release for release in releases if release["tag_name"] == tag), None
91
+ )
92
+ if existing:
93
+ if existing["draft"]:
94
+ raise ValueError("A draft already exists for this tag; review it manually.")
95
+ print(f"Release {tag} already exists; leaving its content unchanged.")
96
+ return
97
+ published = [release for release in releases if not release["draft"]]
98
+ previous = (
99
+ max(published, key=lambda release: release["published_at"])["tag_name"]
100
+ if published
101
+ else None
102
+ )
103
+ notes = release_notes(repository, tag, previous)
104
+ with tempfile.TemporaryDirectory(prefix="mcp-postgresql-ops-release-") as temporary:
105
+ path = Path(temporary) / "release-notes.md"
106
+ path.write_text(notes, encoding="utf-8")
107
+ args = [
108
+ "gh",
109
+ "release",
110
+ "create",
111
+ tag,
112
+ "--repo",
113
+ repository,
114
+ "--verify-tag",
115
+ "--title",
116
+ tag,
117
+ "--notes-file",
118
+ str(path),
119
+ ]
120
+ command(*args)
121
+ print(f"Published GitHub Release {tag} (previous release: {previous or 'none'}).")
122
+
123
+
124
+ def main() -> None:
125
+ """Publish only when explicitly called by the final release job."""
126
+ parser = argparse.ArgumentParser(description=__doc__)
127
+ parser.add_argument("tag")
128
+ args = parser.parse_args()
129
+ try:
130
+ publish_release(os.environ["GITHUB_REPOSITORY"], args.tag)
131
+ except subprocess.CalledProcessError as error:
132
+ parser.exit(1, f"Release command failed: {error.stderr or error}\n")
133
+ except (ValueError, KeyError, OSError) as error:
134
+ parser.exit(1, f"GitHub Release failed: {error}\n")
135
+
136
+
137
+ if __name__ == "__main__":
138
+ main()
139
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-postgresql-ops
3
- Version: 3.4.0
3
+ Version: 3.4.2
4
4
  Summary: Add your description here
5
5
  Author-email: JungJungIn <call518@gmail.com>
6
6
  Requires-Python: >=3.12
@@ -34,7 +34,7 @@ Dynamic: license-file
34
34
 
35
35
  ## Architecture & Internal (DeepWiki)
36
36
 
37
- [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/call518/MCP-PostgreSQL-Ops)
37
+ [![Ask DeepWiki](https://img.shields.io/badge/Ask-DeepWiki-blue)](https://deepwiki.com/call518/MCP-PostgreSQL-Ops)
38
38
 
39
39
  ---
40
40
 
@@ -71,7 +71,7 @@ Dynamic: license-file
71
71
 
72
72
  ## Tool Usage Examples
73
73
 
74
- ### 📸 **[More Examples with Screenshots →](https://github.com/call518/MCP-PostgreSQL-Ops/wiki/Tool-Usage-Example)**
74
+ ### 📸 **[More Examples with Screenshots →](https://github.com/call518/MCP-PostgreSQL-Ops/wiki)**
75
75
 
76
76
  ---
77
77
 
@@ -181,7 +181,7 @@ http://localhost:3003/
181
181
 
182
182
  #### 📖 **Next Steps:**
183
183
  - Browse the **[Example Queries section](#usage-examples)** below for more query examples
184
- - Check out **[Tool Usage Examples with Screenshots](https://github.com/call518/MCP-PostgreSQL-Ops/wiki/Tool-Usage-Example)** for visual guides
184
+ - Check out **[Tool Usage Examples with Screenshots](https://github.com/call518/MCP-PostgreSQL-Ops/wiki)** for visual guides
185
185
  - Explore the **[Tool Compatibility Matrix](#tool-compatibility-matrix)** to understand available features
186
186
 
187
187
  ---
@@ -43,10 +43,12 @@ img/screenshot-claude-desktop-airflow-connections-html.png
43
43
  img/screenshot-claude-desktop-mermaid-diagram.png
44
44
  img/screenshot-claude-desktop-question-001.png
45
45
  img/screenshot-swagger-api.png
46
+ scripts/check_release.py
46
47
  scripts/create-test-data.sh
47
48
  scripts/create-test-data.sql
48
49
  scripts/enable-pgsql-extensions.sh
49
50
  scripts/mcp-server-docker-cmd.sh
51
+ scripts/publish_release.py
50
52
  src/mcp_postgresql_ops/__init__.py
51
53
  src/mcp_postgresql_ops/__main__.py
52
54
  src/mcp_postgresql_ops/functions.py
@@ -63,6 +65,7 @@ src/mcp_postgresql_ops.egg-info/scm_version.json
63
65
  src/mcp_postgresql_ops.egg-info/top_level.txt
64
66
  tests/__init__.py
65
67
  tests/conftest.py
68
+ tests/test_release.py
66
69
  tests/test_tool_schema_null_defaults.py
67
70
  tests/test_tools_integration.py
68
71
  tests/test_version_compat.py
@@ -44,10 +44,12 @@
44
44
  "pyproject.toml",
45
45
  "run-mcp-inspector-local.sh",
46
46
  "run-mcp-inspector-pypi.sh",
47
+ "scripts/check_release.py",
47
48
  "scripts/create-test-data.sh",
48
49
  "scripts/create-test-data.sql",
49
50
  "scripts/enable-pgsql-extensions.sh",
50
51
  "scripts/mcp-server-docker-cmd.sh",
52
+ "scripts/publish_release.py",
51
53
  "src/mcp_postgresql_ops/__init__.py",
52
54
  "src/mcp_postgresql_ops/__main__.py",
53
55
  "src/mcp_postgresql_ops/functions.py",
@@ -58,6 +60,7 @@
58
60
  "tests/conftest.py",
59
61
  "tests/docker/docker-compose.test.yml",
60
62
  "tests/docker/init-test-db.sql",
63
+ "tests/test_release.py",
61
64
  "tests/test_tool_schema_null_defaults.py",
62
65
  "tests/test_tools_integration.py",
63
66
  "tests/test_version_compat.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "3.4.2",
3
+ "distance": 0,
4
+ "node": "g0b8e72718a54b7db8b4997f0828e4498c3366eae",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-24"
8
+ }
@@ -0,0 +1,102 @@
1
+ """Release validation and publication checks without GitHub writes."""
2
+
3
+ import io
4
+ import json
5
+ import sys
6
+ import tarfile
7
+ import zipfile
8
+ from pathlib import Path
9
+ from unittest.mock import patch
10
+
11
+ import pytest
12
+
13
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
14
+
15
+ from scripts.check_release import check_release, release_version
16
+ from scripts.publish_release import publish_release, release_notes
17
+
18
+
19
+ @pytest.mark.parametrize("tag", ["0.0.0", "1.2.3", "10.20.30"])
20
+ def test_release_version(tag):
21
+ assert release_version(tag) == tag
22
+
23
+
24
+ @pytest.mark.parametrize(
25
+ "tag",
26
+ [
27
+ "latest", "1.2", "1.2.3.4", "v1.2.3", "01.2.3", "1.02.3", "1.2.03",
28
+ "1.2.3a1", "1.2.3b1", "1.2.3rc1", "1.2.3+local", "1..3", "1.2.3",
29
+ ],
30
+ )
31
+ def test_invalid_release_version(tag):
32
+ with pytest.raises(ValueError):
33
+ release_version(tag)
34
+
35
+
36
+ @pytest.mark.parametrize("version", ["1.2.3", "1.2.4"])
37
+ def test_distribution_versions(tmp_path, version):
38
+ metadata = f"Name: mcp-postgresql-ops\nVersion: {version}\n".encode()
39
+ with zipfile.ZipFile(tmp_path / "package.whl", "w") as archive:
40
+ archive.writestr("package.dist-info/METADATA", metadata)
41
+ with tarfile.open(tmp_path / "package.tar.gz", "w:gz") as archive:
42
+ info = tarfile.TarInfo("package/PKG-INFO")
43
+ info.size = len(metadata)
44
+ archive.addfile(info, io.BytesIO(metadata))
45
+ if version == "1.2.3":
46
+ check_release("1.2.3", tmp_path)
47
+ else:
48
+ with pytest.raises(ValueError, match="expected"):
49
+ check_release("1.2.3", tmp_path)
50
+
51
+
52
+ def test_notes_use_previous_release_and_preserve_messages():
53
+ message = "Fix issue\n\n```\n$(touch unwanted)\n```"
54
+ with patch("scripts.publish_release.command") as command:
55
+ command.side_effect = ["new", "old", "", f"abcdef123\0{message}\0"]
56
+ notes = release_notes("owner/repo", "1.2.3", "1.2.2")
57
+ assert message in notes
58
+ assert "````text" in notes
59
+ assert "compare/1.2.2...1.2.3" in notes
60
+ assert command.call_args.args[-2:] == ("old..new", "--")
61
+
62
+
63
+ @pytest.mark.parametrize("draft", [False, True])
64
+ def test_existing_release_is_not_overwritten(draft):
65
+ with patch("scripts.publish_release.command") as command:
66
+ command.side_effect = [
67
+ "sha", "sha", "sha",
68
+ json.dumps([[{"tag_name": "1.2.3", "draft": draft}]]),
69
+ ]
70
+ if draft:
71
+ with pytest.raises(ValueError, match="draft"):
72
+ publish_release("owner/repo", "1.2.3")
73
+ else:
74
+ publish_release("owner/repo", "1.2.3")
75
+ assert command.call_count == 4
76
+
77
+
78
+ def test_first_release_creation():
79
+ with patch("scripts.publish_release.command") as command:
80
+ command.side_effect = ["sha", "sha", "sha", "[[]]", "sha", "", ""]
81
+ publish_release("owner/repo", "1.2.3")
82
+ args = command.call_args.args
83
+ assert args[:3] == ("gh", "release", "create")
84
+ assert args[3] == "1.2.3"
85
+ assert "--prerelease" not in args
86
+ assert "--latest=false" not in args
87
+
88
+
89
+ def test_remote_tag_mismatch_stops_publication():
90
+ with patch("scripts.publish_release.command") as command:
91
+ command.side_effect = ["local", "remote"]
92
+ with pytest.raises(ValueError, match="must match"):
93
+ publish_release("owner/repo", "1.2.3")
94
+ assert command.call_count == 2
95
+
96
+
97
+ @pytest.mark.parametrize("tag", ["v1.2.3", "1.2.3rc1"])
98
+ def test_invalid_tag_stops_publication(tag):
99
+ with patch("scripts.publish_release.command") as command:
100
+ with pytest.raises(ValueError):
101
+ publish_release("owner/repo", tag)
102
+ command.assert_not_called()
@@ -641,7 +641,7 @@ wheels = [
641
641
 
642
642
  [[package]]
643
643
  name = "mcp"
644
- version = "1.28.1"
644
+ version = "1.30.0"
645
645
  source = { registry = "https://pypi.org/simple" }
646
646
  dependencies = [
647
647
  { name = "anyio" },
@@ -661,9 +661,9 @@ dependencies = [
661
661
  { name = "typing-inspection", version = "0.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" },
662
662
  { name = "uvicorn", marker = "sys_platform != 'emscripten'" },
663
663
  ]
664
- sdist = { url = "https://files.pythonhosted.org/packages/6e/77/9450b8f251a13affb6281997d0523c4615f8a8b35d0b21ff30db3a5aac9d/mcp-1.28.1.tar.gz", hash = "sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683", size = 638501, upload-time = "2026-06-26T12:57:29.093Z" }
664
+ sdist = { url = "https://files.pythonhosted.org/packages/ba/93/0142dc84a666daf8ad51a34268f34c12fd6fda4f3810c4be2504eecc8212/mcp-1.30.0.tar.gz", hash = "sha256:445414625fce5c295faa505bb11bacece661ab6f4028d57c935db57820b7a3e4", size = 680511, upload-time = "2026-09-07T14:34:15.845Z" }
665
665
  wheels = [
666
- { url = "https://files.pythonhosted.org/packages/e2/5e/d118fce19f87a2e7d8101c35c8ae0ec289098a4df0ff244cec23e415aca0/mcp-1.28.1-py3-none-any.whl", hash = "sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df", size = 222620, upload-time = "2026-06-26T12:57:27.218Z" },
666
+ { url = "https://files.pythonhosted.org/packages/f5/f4/e58bc33317c92a0203664daaf00bf6f41166cc0149e5d6870a03f7cd004a/mcp-1.30.0-py3-none-any.whl", hash = "sha256:666edb5009503e1047c9d60346a756f94b261f05cc2625f23d41c728ffc484d0", size = 234581, upload-time = "2026-09-07T14:34:14.266Z" },
667
667
  ]
668
668
 
669
669
  [[package]]
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "3.4.0",
3
- "distance": 0,
4
- "node": "g290b364e43b7b9174f1b4b7969e48af8ab3dc3f6",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-10-09"
8
- }