mcp-postgresql-ops 3.3.8__tar.gz → 3.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/workflows/pypi-publish.yml +1 -1
  2. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/workflows/test.yml +3 -3
  3. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/PKG-INFO +13 -6
  4. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/README.md +13 -6
  5. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops/mcp_main.py +33 -33
  6. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops.egg-info/PKG-INFO +13 -6
  7. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops.egg-info/SOURCES.txt +1 -0
  8. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops.egg-info/scm_file_list.json +44 -43
  9. mcp_postgresql_ops-3.4.0/src/mcp_postgresql_ops.egg-info/scm_version.json +8 -0
  10. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/tests/conftest.py +1 -1
  11. mcp_postgresql_ops-3.4.0/tests/test_tool_schema_null_defaults.py +33 -0
  12. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/tests/test_tools_integration.py +1 -1
  13. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/uv.lock +214 -85
  14. mcp_postgresql_ops-3.3.8/src/mcp_postgresql_ops.egg-info/scm_version.json +0 -8
  15. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.env.example +0 -0
  16. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/CONTRIBUTING.md +0 -0
  17. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/FUNDING.yml +0 -0
  18. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  19. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  20. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/ISSUE_TEMPLATE/documentation.md +0 -0
  21. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  22. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
  23. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/copilot-instructions.md +0 -0
  24. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/dependabot.yml +0 -0
  25. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.github/pull_request_template.md +0 -0
  26. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.gitignore +0 -0
  27. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.gitleaks.toml +0 -0
  28. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.pre-commit-config.yaml +0 -0
  29. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/.python-version +0 -0
  30. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/Dockerfile.MCP-Server +0 -0
  31. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/Dockerfile.MCPO-Proxy +0 -0
  32. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/LICENSE +0 -0
  33. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/MANIFEST.in +0 -0
  34. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/SECURITY.md +0 -0
  35. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/_config.yml +0 -0
  36. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/_includes/head-custom-google-analytics.html +0 -0
  37. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/build-mcp-server-docker-image.sh +0 -0
  38. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/build-mcpo-server-docker-image.sh +0 -0
  39. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/docker-compose.custom-db.yml +0 -0
  40. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/docker-compose.yml +0 -0
  41. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/img/MCP-Workflow-of-Quickstart-Tutorial.png +0 -0
  42. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/img/screenshot-000.png +0 -0
  43. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/img/screenshot-001.png +0 -0
  44. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/img/screenshot-002.png +0 -0
  45. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/img/screenshot-004.png +0 -0
  46. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/img/screenshot-005.png +0 -0
  47. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/img/screenshot-claude-desktop-airflow-connections-html.png +0 -0
  48. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/img/screenshot-claude-desktop-mermaid-diagram.png +0 -0
  49. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/img/screenshot-claude-desktop-question-001.png +0 -0
  50. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/img/screenshot-swagger-api.png +0 -0
  51. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/mcp-config.json.http +0 -0
  52. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/mcp-config.json.stdio +0 -0
  53. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/pyproject.toml +0 -0
  54. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/run-mcp-inspector-local.sh +0 -0
  55. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/run-mcp-inspector-pypi.sh +0 -0
  56. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/scripts/create-test-data.sh +0 -0
  57. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/scripts/create-test-data.sql +0 -0
  58. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/scripts/enable-pgsql-extensions.sh +0 -0
  59. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/scripts/mcp-server-docker-cmd.sh +0 -0
  60. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/setup.cfg +0 -0
  61. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops/__init__.py +0 -0
  62. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops/__main__.py +0 -0
  63. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops/functions.py +0 -0
  64. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops/prompt_template.md +0 -0
  65. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops/version_compat.py +0 -0
  66. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops.egg-info/dependency_links.txt +0 -0
  67. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops.egg-info/entry_points.txt +0 -0
  68. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops.egg-info/requires.txt +0 -0
  69. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/src/mcp_postgresql_ops.egg-info/top_level.txt +0 -0
  70. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/tests/__init__.py +0 -0
  71. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/tests/docker/docker-compose.test.yml +0 -0
  72. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/tests/docker/init-test-db.sql +0 -0
  73. {mcp_postgresql_ops-3.3.8 → mcp_postgresql_ops-3.4.0}/tests/test_version_compat.py +0 -0
@@ -19,7 +19,7 @@ jobs:
19
19
  with:
20
20
  persist-credentials: false
21
21
  fetch-depth: 0
22
- - uses: actions/setup-python@v6
22
+ - uses: actions/setup-python@v7
23
23
  with:
24
24
  python-version: "3.12.x"
25
25
  - name: Install build tool
@@ -24,7 +24,7 @@ jobs:
24
24
  enable-cache: true
25
25
 
26
26
  - name: Setup Python 3.12
27
- uses: actions/setup-python@v6
27
+ uses: actions/setup-python@v7
28
28
  with:
29
29
  python-version: "3.12"
30
30
 
@@ -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 -v --tb=short
35
+ run: uv run pytest tests/test_version_compat.py tests/test_tool_schema_null_defaults.py -v --tb=short
36
36
  integration-tests:
37
37
  name: Integration Tests (PG 12-18)
38
38
  runs-on: ubuntu-latest
@@ -67,7 +67,7 @@ jobs:
67
67
  enable-cache: true
68
68
 
69
69
  - name: Setup Python 3.12
70
- uses: actions/setup-python@v6
70
+ uses: actions/setup-python@v7
71
71
  with:
72
72
  python-version: "3.12"
73
73
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-postgresql-ops
3
- Version: 3.3.8
3
+ Version: 3.4.0
4
4
  Summary: Add your description here
5
5
  Author-email: JungJungIn <call518@gmail.com>
6
6
  Requires-Python: >=3.12
@@ -18,6 +18,8 @@ Dynamic: license-file
18
18
 
19
19
  # MCP Server for PostgreSQL Operations and Monitoring
20
20
 
21
+ [![MCP Toplist](https://mcptoplist.com/badge/smithery%2Fcall518%2Fmcp-postgresql-ops.svg)](https://mcptoplist.com/server/smithery%2Fcall518%2Fmcp-postgresql-ops)
22
+
21
23
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
22
24
  ![Python](https://img.shields.io/badge/Python-3776AB?style=flat&logo=python&logoColor=white)
23
25
  ![Docker Pulls](https://img.shields.io/docker/pulls/call518/mcp-server-postgresql-ops)
@@ -145,7 +147,8 @@ docker-compose logs -f
145
147
  docker-compose ps
146
148
 
147
149
  # Verify PostgreSQL is ready
148
- docker-compose logs postgres | grep "ready to accept connections"
150
+ docker compose exec -T postgres sh -c \
151
+ 'pg_isready -h 127.0.0.1 -p 5432 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'
149
152
  ```
150
153
 
151
154
  ### 3. Access to OpenWebUI
@@ -950,11 +953,12 @@ mcp-postgresql-ops --log-level DEBUG
950
953
 
951
954
  ### Running Tests
952
955
 
953
- Two test suites are available:
956
+ The following test suites are available:
954
957
 
955
958
  | Suite | File | Requires Docker |
956
959
  |-------|------|----------------|
957
960
  | Unit tests (version compatibility logic) | `tests/test_version_compat.py` | No |
961
+ | Unit tests (null-default tool argument schemas) | `tests/test_tool_schema_null_defaults.py` | No |
958
962
  | Integration tests (all tools × PG 12–18) | `tests/test_tools_integration.py` | Yes |
959
963
 
960
964
  `uv run pytest` automatically starts the Docker test containers (PG 12–18), waits for them to be fully initialized, runs all tests, then tears everything down.
@@ -963,14 +967,17 @@ Two test suites are available:
963
967
  # Run all tests (unit + integration) — Docker is managed automatically
964
968
  uv run pytest -v
965
969
 
966
- # Unit tests only (no Docker needed)
967
- uv run pytest tests/test_version_compat.py -v
970
+ # Unit tests only (no Docker or PostgreSQL needed)
971
+ uv run pytest tests/test_version_compat.py tests/test_tool_schema_null_defaults.py -v
972
+
973
+ # Tool argument schema tests only (no Docker or PostgreSQL needed)
974
+ uv run pytest tests/test_tool_schema_null_defaults.py -v
968
975
 
969
976
  # Integration tests only
970
977
  uv run pytest tests/test_tools_integration.py -v
971
978
  ```
972
979
 
973
- > **Note**: Docker must be running. The test stack uses ports 5412–5418 (PG 12–18).
980
+ > **Note**: Docker must be running for integration tests. The test stack uses ports 5412–5418 (PG 12–18).
974
981
 
975
982
  ### Version Compatibility Testing
976
983
 
@@ -1,5 +1,7 @@
1
1
  # MCP Server for PostgreSQL Operations and Monitoring
2
2
 
3
+ [![MCP Toplist](https://mcptoplist.com/badge/smithery%2Fcall518%2Fmcp-postgresql-ops.svg)](https://mcptoplist.com/server/smithery%2Fcall518%2Fmcp-postgresql-ops)
4
+
3
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
4
6
  ![Python](https://img.shields.io/badge/Python-3776AB?style=flat&logo=python&logoColor=white)
5
7
  ![Docker Pulls](https://img.shields.io/docker/pulls/call518/mcp-server-postgresql-ops)
@@ -127,7 +129,8 @@ docker-compose logs -f
127
129
  docker-compose ps
128
130
 
129
131
  # Verify PostgreSQL is ready
130
- docker-compose logs postgres | grep "ready to accept connections"
132
+ docker compose exec -T postgres sh -c \
133
+ 'pg_isready -h 127.0.0.1 -p 5432 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'
131
134
  ```
132
135
 
133
136
  ### 3. Access to OpenWebUI
@@ -932,11 +935,12 @@ mcp-postgresql-ops --log-level DEBUG
932
935
 
933
936
  ### Running Tests
934
937
 
935
- Two test suites are available:
938
+ The following test suites are available:
936
939
 
937
940
  | Suite | File | Requires Docker |
938
941
  |-------|------|----------------|
939
942
  | Unit tests (version compatibility logic) | `tests/test_version_compat.py` | No |
943
+ | Unit tests (null-default tool argument schemas) | `tests/test_tool_schema_null_defaults.py` | No |
940
944
  | Integration tests (all tools × PG 12–18) | `tests/test_tools_integration.py` | Yes |
941
945
 
942
946
  `uv run pytest` automatically starts the Docker test containers (PG 12–18), waits for them to be fully initialized, runs all tests, then tears everything down.
@@ -945,14 +949,17 @@ Two test suites are available:
945
949
  # Run all tests (unit + integration) — Docker is managed automatically
946
950
  uv run pytest -v
947
951
 
948
- # Unit tests only (no Docker needed)
949
- uv run pytest tests/test_version_compat.py -v
952
+ # Unit tests only (no Docker or PostgreSQL needed)
953
+ uv run pytest tests/test_version_compat.py tests/test_tool_schema_null_defaults.py -v
954
+
955
+ # Tool argument schema tests only (no Docker or PostgreSQL needed)
956
+ uv run pytest tests/test_tool_schema_null_defaults.py -v
950
957
 
951
958
  # Integration tests only
952
959
  uv run pytest tests/test_tools_integration.py -v
953
960
  ```
954
961
 
955
- > **Note**: Docker must be running. The test stack uses ports 5412–5418 (PG 12–18).
962
+ > **Note**: Docker must be running for integration tests. The test stack uses ports 5412–5418 (PG 12–18).
956
963
 
957
964
  ### Version Compatibility Testing
958
965
 
@@ -1233,4 +1240,4 @@ Freely use, modify, and distribute under the **MIT License**.
1233
1240
  - [MCP-Airflow-API](https://github.com/call518/MCP-Airflow-API)
1234
1241
  - [MCP-Ambari-API](https://github.com/call518/MCP-Ambari-API)
1235
1242
  - [MCP-OpenStack-API](https://github.com/call518/MCP-OpenStack-API)
1236
- - [LogSentinelAI - LLB-Based Log Analyzer](https://github.com/call518/LogSentinelAI)
1243
+ - [LogSentinelAI - LLB-Based Log Analyzer](https://github.com/call518/LogSentinelAI)
@@ -97,12 +97,12 @@ PROMPT_TEMPLATE_PATH = os.path.join(os.path.dirname(__file__), "prompt_template.
97
97
 
98
98
  @mcp.tool()
99
99
  async def get_lock_monitoring(
100
- database_name: str = None,
101
- granted: str = None,
102
- state: str = None,
103
- mode: str = None,
104
- locktype: str = None,
105
- username: str = None
100
+ database_name: Optional[str] = None,
101
+ granted: Optional[str] = None,
102
+ state: Optional[str] = None,
103
+ mode: Optional[str] = None,
104
+ locktype: Optional[str] = None,
105
+ username: Optional[str] = None
106
106
  ) -> str:
107
107
  """
108
108
  [Tool Purpose]: Monitor current locks and potential deadlocks in PostgreSQL
@@ -492,7 +492,7 @@ async def get_server_info() -> str:
492
492
 
493
493
 
494
494
  @mcp.tool()
495
- async def get_current_database_info(database_name: str = None) -> str:
495
+ async def get_current_database_info(database_name: Optional[str] = None) -> str:
496
496
  """
497
497
  [Tool Purpose]: Get information about the current database connection
498
498
 
@@ -613,7 +613,7 @@ async def get_database_list() -> str:
613
613
 
614
614
 
615
615
  @mcp.tool()
616
- async def get_table_list(database_name: str = None) -> str:
616
+ async def get_table_list(database_name: Optional[str] = None) -> str:
617
617
  """
618
618
  [Tool Purpose]: Retrieve list of all tables and their information from specified database (or current DB)
619
619
 
@@ -713,7 +713,7 @@ async def get_user_list() -> str:
713
713
 
714
714
 
715
715
  @mcp.tool()
716
- async def get_table_schema_info(database_name: str, table_name: str = None, schema_name: str = "public") -> str:
716
+ async def get_table_schema_info(database_name: str, table_name: Optional[str] = None, schema_name: str = "public") -> str:
717
717
  """
718
718
  [Tool Purpose]: Retrieve detailed schema information for specific table or all tables in a database
719
719
 
@@ -971,7 +971,7 @@ async def get_table_schema_info(database_name: str, table_name: str = None, sche
971
971
 
972
972
 
973
973
  @mcp.tool()
974
- async def get_database_schema_info(database_name: str, schema_name: str = None) -> str:
974
+ async def get_database_schema_info(database_name: str, schema_name: Optional[str] = None) -> str:
975
975
  """
976
976
  [Tool Purpose]: Retrieve detailed information about database schemas (namespaces) and their contents
977
977
 
@@ -1178,7 +1178,7 @@ async def get_database_schema_info(database_name: str, schema_name: str = None)
1178
1178
 
1179
1179
 
1180
1180
  @mcp.tool()
1181
- async def get_table_relationships(database_name: str, table_name: str = None, schema_name: str = "public", relationship_type: str = "all") -> str:
1181
+ async def get_table_relationships(database_name: str, table_name: Optional[str] = None, schema_name: str = "public", relationship_type: str = "all") -> str:
1182
1182
  """
1183
1183
  [Tool Purpose]: Analyze table relationships including foreign keys, dependencies, and inheritance
1184
1184
 
@@ -1557,7 +1557,7 @@ async def get_active_connections() -> str:
1557
1557
 
1558
1558
 
1559
1559
  @mcp.tool()
1560
- async def get_pg_stat_statements_top_queries(limit: int = 20, database_name: str = None) -> str:
1560
+ async def get_pg_stat_statements_top_queries(limit: int = 20, database_name: Optional[str] = None) -> str:
1561
1561
  """
1562
1562
  [Tool Purpose]: Analyze top queries that consumed the most time using pg_stat_statements extension
1563
1563
 
@@ -1605,7 +1605,7 @@ async def get_pg_stat_statements_top_queries(limit: int = 20, database_name: str
1605
1605
 
1606
1606
 
1607
1607
  @mcp.tool()
1608
- async def get_pg_stat_monitor_recent_queries(limit: int = 20, database_name: str = None) -> str:
1608
+ async def get_pg_stat_monitor_recent_queries(limit: int = 20, database_name: Optional[str] = None) -> str:
1609
1609
  """
1610
1610
  [Tool Purpose]: Analyze recently executed queries and detailed monitoring information using pg_stat_monitor extension
1611
1611
 
@@ -1708,7 +1708,7 @@ async def get_database_size_info() -> str:
1708
1708
 
1709
1709
 
1710
1710
  @mcp.tool()
1711
- async def get_table_size_info(schema_name: str = "public", database_name: str = None) -> str:
1711
+ async def get_table_size_info(schema_name: str = "public", database_name: Optional[str] = None) -> str:
1712
1712
  """
1713
1713
  [Tool Purpose]: Analyze size information and index usage of all tables in specified schema
1714
1714
 
@@ -1778,7 +1778,7 @@ async def get_table_size_info(schema_name: str = "public", database_name: str =
1778
1778
 
1779
1779
 
1780
1780
  @mcp.tool()
1781
- async def get_postgresql_config(config_name: str = None, filter_text: str = None) -> str:
1781
+ async def get_postgresql_config(config_name: Optional[str] = None, filter_text: Optional[str] = None) -> str:
1782
1782
  """
1783
1783
  [Tool Purpose]: Retrieve and analyze PostgreSQL server configuration parameter values
1784
1784
 
@@ -1879,7 +1879,7 @@ async def get_postgresql_config(config_name: str = None, filter_text: str = None
1879
1879
 
1880
1880
 
1881
1881
  @mcp.tool()
1882
- async def get_index_usage_stats(database_name: str = None) -> str:
1882
+ async def get_index_usage_stats(database_name: Optional[str] = None) -> str:
1883
1883
  """
1884
1884
  [Tool Purpose]: Analyze usage rate and performance statistics of all indexes in database
1885
1885
 
@@ -1937,7 +1937,7 @@ async def get_index_usage_stats(database_name: str = None) -> str:
1937
1937
 
1938
1938
 
1939
1939
  @mcp.tool()
1940
- async def get_vacuum_analyze_stats(database_name: str = None) -> str:
1940
+ async def get_vacuum_analyze_stats(database_name: Optional[str] = None) -> str:
1941
1941
  """
1942
1942
  [Tool Purpose]: Analyze VACUUM and ANALYZE execution history and statistics per table
1943
1943
 
@@ -2007,7 +2007,7 @@ async def get_vacuum_analyze_stats(database_name: str = None) -> str:
2007
2007
 
2008
2008
 
2009
2009
  @mcp.tool()
2010
- async def get_table_bloat_analysis(database_name: str = None, schema_name: str = None, table_pattern: str = None, min_dead_tuples: int = 1, limit: int = 20) -> str:
2010
+ async def get_table_bloat_analysis(database_name: Optional[str] = None, schema_name: Optional[str] = None, table_pattern: Optional[str] = None, min_dead_tuples: int = 1, limit: int = 20) -> str:
2011
2011
  """
2012
2012
  [Tool Purpose]: Analyze table bloat based on dead tuple statistics and size information
2013
2013
 
@@ -2160,7 +2160,7 @@ async def get_table_bloat_analysis(database_name: str = None, schema_name: str =
2160
2160
 
2161
2161
 
2162
2162
  @mcp.tool()
2163
- async def get_database_bloat_overview(database_name: str = None, limit: int = 20) -> str:
2163
+ async def get_database_bloat_overview(database_name: Optional[str] = None, limit: int = 20) -> str:
2164
2164
  """
2165
2165
  [Tool Purpose]: Provide database-wide bloat overview and summary statistics
2166
2166
 
@@ -2247,7 +2247,7 @@ async def get_database_bloat_overview(database_name: str = None, limit: int = 20
2247
2247
 
2248
2248
 
2249
2249
  @mcp.tool()
2250
- async def get_autovacuum_status(database_name: str = None, schema_name: str = None, table_pattern: str = None, limit: int = 50) -> str:
2250
+ async def get_autovacuum_status(database_name: Optional[str] = None, schema_name: Optional[str] = None, table_pattern: Optional[str] = None, limit: int = 50) -> str:
2251
2251
  """
2252
2252
  [Tool Purpose]: Analyze autovacuum configuration and current maintenance status for tables
2253
2253
 
@@ -2395,7 +2395,7 @@ async def get_autovacuum_status(database_name: str = None, schema_name: str = No
2395
2395
 
2396
2396
 
2397
2397
  @mcp.tool()
2398
- async def get_autovacuum_activity(database_name: str = None, schema_name: str = None, hours_back: int = 24, limit: int = 50) -> str:
2398
+ async def get_autovacuum_activity(database_name: Optional[str] = None, schema_name: Optional[str] = None, hours_back: int = 24, limit: int = 50) -> str:
2399
2399
  """
2400
2400
  [Tool Purpose]: Monitor recent autovacuum and autoanalyze activity patterns and execution history
2401
2401
 
@@ -2544,7 +2544,7 @@ async def get_autovacuum_activity(database_name: str = None, schema_name: str =
2544
2544
 
2545
2545
 
2546
2546
  @mcp.tool()
2547
- async def get_running_vacuum_operations(database_name: str = None) -> str:
2547
+ async def get_running_vacuum_operations(database_name: Optional[str] = None) -> str:
2548
2548
  """
2549
2549
  [Tool Purpose]: Monitor currently running VACUUM and ANALYZE operations in real-time
2550
2550
 
@@ -2650,7 +2650,7 @@ async def get_running_vacuum_operations(database_name: str = None) -> str:
2650
2650
 
2651
2651
 
2652
2652
  @mcp.tool()
2653
- async def get_vacuum_effectiveness_analysis(database_name: str = None, schema_name: str = None, limit: int = 30) -> str:
2653
+ async def get_vacuum_effectiveness_analysis(database_name: Optional[str] = None, schema_name: Optional[str] = None, limit: int = 30) -> str:
2654
2654
  """
2655
2655
  [Tool Purpose]: Analyze VACUUM effectiveness and maintenance patterns using existing statistics
2656
2656
 
@@ -3012,7 +3012,7 @@ async def get_bgwriter_stats() -> str:
3012
3012
 
3013
3013
 
3014
3014
  @mcp.tool()
3015
- async def get_io_stats(limit: int = 20, database_name: str = None) -> str:
3015
+ async def get_io_stats(limit: int = 20, database_name: Optional[str] = None) -> str:
3016
3016
  """
3017
3017
  [Tool Purpose]: Analyze comprehensive I/O statistics across all database operations with version compatibility
3018
3018
 
@@ -3151,7 +3151,7 @@ async def get_io_stats(limit: int = 20, database_name: str = None) -> str:
3151
3151
 
3152
3152
 
3153
3153
  @mcp.tool()
3154
- async def get_table_io_stats(database_name: str = None, schema_name: str = "public") -> str:
3154
+ async def get_table_io_stats(database_name: Optional[str] = None, schema_name: str = "public") -> str:
3155
3155
  """
3156
3156
  [Tool Purpose]: Analyze I/O performance statistics for tables (disk reads vs buffer cache hits)
3157
3157
 
@@ -3248,7 +3248,7 @@ async def get_table_io_stats(database_name: str = None, schema_name: str = "publ
3248
3248
 
3249
3249
 
3250
3250
  @mcp.tool()
3251
- async def get_index_io_stats(database_name: str = None, schema_name: str = "public") -> str:
3251
+ async def get_index_io_stats(database_name: Optional[str] = None, schema_name: str = "public") -> str:
3252
3252
  """
3253
3253
  [Tool Purpose]: Analyze I/O performance statistics for indexes (disk reads vs buffer cache hits)
3254
3254
 
@@ -3335,7 +3335,7 @@ async def get_index_io_stats(database_name: str = None, schema_name: str = "publ
3335
3335
 
3336
3336
 
3337
3337
  @mcp.tool()
3338
- async def get_all_tables_stats(database_name: str = None, include_system: bool = False) -> str:
3338
+ async def get_all_tables_stats(database_name: Optional[str] = None, include_system: bool = False) -> str:
3339
3339
  """
3340
3340
  [Tool Purpose]: Get comprehensive statistics for all tables (including system tables if requested)
3341
3341
 
@@ -3382,7 +3382,7 @@ async def get_all_tables_stats(database_name: str = None, include_system: bool =
3382
3382
 
3383
3383
 
3384
3384
  @mcp.tool()
3385
- async def get_user_functions_stats(database_name: str = None) -> str:
3385
+ async def get_user_functions_stats(database_name: Optional[str] = None) -> str:
3386
3386
  """
3387
3387
  [Tool Purpose]: Analyze performance statistics for user-defined functions
3388
3388
 
@@ -3457,7 +3457,7 @@ async def get_user_functions_stats(database_name: str = None) -> str:
3457
3457
 
3458
3458
 
3459
3459
  @mcp.tool()
3460
- async def get_database_conflicts_stats(database_name: str = None) -> str:
3460
+ async def get_database_conflicts_stats(database_name: Optional[str] = None) -> str:
3461
3461
  """
3462
3462
  [Tool Purpose]: Analyze query conflicts in standby/replica database environments
3463
3463
 
@@ -3530,7 +3530,7 @@ async def get_database_conflicts_stats(database_name: str = None) -> str:
3530
3530
  # =============================================================================
3531
3531
 
3532
3532
  @mcp.tool()
3533
- async def get_wait_events(database_name: str = None, wait_event_type: str = None) -> str:
3533
+ async def get_wait_events(database_name: Optional[str] = None, wait_event_type: Optional[str] = None) -> str:
3534
3534
  """
3535
3535
  [Tool Purpose]: List available wait event types and their descriptions (PostgreSQL 17+)
3536
3536
 
@@ -3636,7 +3636,7 @@ async def get_wait_events(database_name: str = None, wait_event_type: str = None
3636
3636
 
3637
3637
 
3638
3638
  @mcp.tool()
3639
- async def get_wal_summarizer_status(database_name: str = None) -> str:
3639
+ async def get_wal_summarizer_status(database_name: Optional[str] = None) -> str:
3640
3640
  """
3641
3641
  [Tool Purpose]: Monitor WAL summarizer status for incremental backup support (PostgreSQL 17+)
3642
3642
 
@@ -3710,7 +3710,7 @@ async def get_wal_summarizer_status(database_name: str = None) -> str:
3710
3710
 
3711
3711
 
3712
3712
  @mcp.tool()
3713
- async def get_async_io_status(database_name: str = None) -> str:
3713
+ async def get_async_io_status(database_name: Optional[str] = None) -> str:
3714
3714
  """
3715
3715
  [Tool Purpose]: Monitor asynchronous I/O subsystem status (PostgreSQL 18+)
3716
3716
 
@@ -3772,7 +3772,7 @@ async def get_async_io_status(database_name: str = None) -> str:
3772
3772
 
3773
3773
 
3774
3774
  @mcp.tool()
3775
- async def get_per_backend_io_stats(database_name: str = None, limit: int = 20) -> str:
3775
+ async def get_per_backend_io_stats(database_name: Optional[str] = None, limit: int = 20) -> str:
3776
3776
  """
3777
3777
  [Tool Purpose]: Analyze per-backend I/O and WAL statistics (PostgreSQL 18+)
3778
3778
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-postgresql-ops
3
- Version: 3.3.8
3
+ Version: 3.4.0
4
4
  Summary: Add your description here
5
5
  Author-email: JungJungIn <call518@gmail.com>
6
6
  Requires-Python: >=3.12
@@ -18,6 +18,8 @@ Dynamic: license-file
18
18
 
19
19
  # MCP Server for PostgreSQL Operations and Monitoring
20
20
 
21
+ [![MCP Toplist](https://mcptoplist.com/badge/smithery%2Fcall518%2Fmcp-postgresql-ops.svg)](https://mcptoplist.com/server/smithery%2Fcall518%2Fmcp-postgresql-ops)
22
+
21
23
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
22
24
  ![Python](https://img.shields.io/badge/Python-3776AB?style=flat&logo=python&logoColor=white)
23
25
  ![Docker Pulls](https://img.shields.io/docker/pulls/call518/mcp-server-postgresql-ops)
@@ -145,7 +147,8 @@ docker-compose logs -f
145
147
  docker-compose ps
146
148
 
147
149
  # Verify PostgreSQL is ready
148
- docker-compose logs postgres | grep "ready to accept connections"
150
+ docker compose exec -T postgres sh -c \
151
+ 'pg_isready -h 127.0.0.1 -p 5432 -U "$POSTGRES_USER" -d "$POSTGRES_DB"'
149
152
  ```
150
153
 
151
154
  ### 3. Access to OpenWebUI
@@ -950,11 +953,12 @@ mcp-postgresql-ops --log-level DEBUG
950
953
 
951
954
  ### Running Tests
952
955
 
953
- Two test suites are available:
956
+ The following test suites are available:
954
957
 
955
958
  | Suite | File | Requires Docker |
956
959
  |-------|------|----------------|
957
960
  | Unit tests (version compatibility logic) | `tests/test_version_compat.py` | No |
961
+ | Unit tests (null-default tool argument schemas) | `tests/test_tool_schema_null_defaults.py` | No |
958
962
  | Integration tests (all tools × PG 12–18) | `tests/test_tools_integration.py` | Yes |
959
963
 
960
964
  `uv run pytest` automatically starts the Docker test containers (PG 12–18), waits for them to be fully initialized, runs all tests, then tears everything down.
@@ -963,14 +967,17 @@ Two test suites are available:
963
967
  # Run all tests (unit + integration) — Docker is managed automatically
964
968
  uv run pytest -v
965
969
 
966
- # Unit tests only (no Docker needed)
967
- uv run pytest tests/test_version_compat.py -v
970
+ # Unit tests only (no Docker or PostgreSQL needed)
971
+ uv run pytest tests/test_version_compat.py tests/test_tool_schema_null_defaults.py -v
972
+
973
+ # Tool argument schema tests only (no Docker or PostgreSQL needed)
974
+ uv run pytest tests/test_tool_schema_null_defaults.py -v
968
975
 
969
976
  # Integration tests only
970
977
  uv run pytest tests/test_tools_integration.py -v
971
978
  ```
972
979
 
973
- > **Note**: Docker must be running. The test stack uses ports 5412–5418 (PG 12–18).
980
+ > **Note**: Docker must be running for integration tests. The test stack uses ports 5412–5418 (PG 12–18).
974
981
 
975
982
  ### Version Compatibility Testing
976
983
 
@@ -63,6 +63,7 @@ src/mcp_postgresql_ops.egg-info/scm_version.json
63
63
  src/mcp_postgresql_ops.egg-info/top_level.txt
64
64
  tests/__init__.py
65
65
  tests/conftest.py
66
+ tests/test_tool_schema_null_defaults.py
66
67
  tests/test_tools_integration.py
67
68
  tests/test_version_compat.py
68
69
  tests/docker/docker-compose.test.yml
@@ -1,65 +1,66 @@
1
1
  {
2
2
  "files": [
3
+ ".env.example",
4
+ ".github/CONTRIBUTING.md",
5
+ ".github/FUNDING.yml",
6
+ ".github/ISSUE_TEMPLATE/bug_report.md",
7
+ ".github/ISSUE_TEMPLATE/config.yml",
8
+ ".github/ISSUE_TEMPLATE/documentation.md",
9
+ ".github/ISSUE_TEMPLATE/feature_request.md",
10
+ ".github/ISSUE_TEMPLATE/question.md",
11
+ ".github/copilot-instructions.md",
12
+ ".github/dependabot.yml",
13
+ ".github/pull_request_template.md",
14
+ ".github/workflows/pypi-publish.yml",
15
+ ".github/workflows/test.yml",
16
+ ".gitignore",
17
+ ".gitleaks.toml",
3
18
  ".pre-commit-config.yaml",
4
- "run-mcp-inspector-local.sh",
5
- "README.md",
6
- "uv.lock",
7
- "Dockerfile.MCPO-Proxy",
8
19
  ".python-version",
9
- "LICENSE",
10
20
  "Dockerfile.MCP-Server",
11
- "run-mcp-inspector-pypi.sh",
12
- "mcp-config.json.stdio",
13
- "pyproject.toml",
14
- "mcp-config.json.http",
15
- "SECURITY.md",
16
- "build-mcp-server-docker-image.sh",
21
+ "Dockerfile.MCPO-Proxy",
22
+ "LICENSE",
17
23
  "MANIFEST.in",
18
- ".env.example",
19
- ".gitleaks.toml",
24
+ "README.md",
25
+ "SECURITY.md",
20
26
  "_config.yml",
27
+ "_includes/head-custom-google-analytics.html",
28
+ "build-mcp-server-docker-image.sh",
29
+ "build-mcpo-server-docker-image.sh",
21
30
  "docker-compose.custom-db.yml",
22
- ".gitignore",
23
31
  "docker-compose.yml",
24
- "build-mcpo-server-docker-image.sh",
25
- "scripts/enable-pgsql-extensions.sh",
32
+ "img/MCP-Workflow-of-Quickstart-Tutorial.png",
33
+ "img/screenshot-000.png",
34
+ "img/screenshot-001.png",
35
+ "img/screenshot-002.png",
36
+ "img/screenshot-004.png",
37
+ "img/screenshot-005.png",
38
+ "img/screenshot-claude-desktop-airflow-connections-html.png",
39
+ "img/screenshot-claude-desktop-mermaid-diagram.png",
40
+ "img/screenshot-claude-desktop-question-001.png",
41
+ "img/screenshot-swagger-api.png",
42
+ "mcp-config.json.http",
43
+ "mcp-config.json.stdio",
44
+ "pyproject.toml",
45
+ "run-mcp-inspector-local.sh",
46
+ "run-mcp-inspector-pypi.sh",
26
47
  "scripts/create-test-data.sh",
27
48
  "scripts/create-test-data.sql",
49
+ "scripts/enable-pgsql-extensions.sh",
28
50
  "scripts/mcp-server-docker-cmd.sh",
29
51
  "src/mcp_postgresql_ops/__init__.py",
52
+ "src/mcp_postgresql_ops/__main__.py",
30
53
  "src/mcp_postgresql_ops/functions.py",
54
+ "src/mcp_postgresql_ops/mcp_main.py",
31
55
  "src/mcp_postgresql_ops/prompt_template.md",
32
56
  "src/mcp_postgresql_ops/version_compat.py",
33
- "src/mcp_postgresql_ops/mcp_main.py",
34
- "src/mcp_postgresql_ops/__main__.py",
35
- "img/screenshot-001.png",
36
- "img/screenshot-000.png",
37
- "img/screenshot-swagger-api.png",
38
- "img/screenshot-claude-desktop-airflow-connections-html.png",
39
- "img/screenshot-002.png",
40
- "img/screenshot-claude-desktop-question-001.png",
41
- "img/screenshot-004.png",
42
- "img/MCP-Workflow-of-Quickstart-Tutorial.png",
43
- "img/screenshot-claude-desktop-mermaid-diagram.png",
44
- "img/screenshot-005.png",
45
57
  "tests/__init__.py",
46
- "tests/test_version_compat.py",
47
58
  "tests/conftest.py",
48
- "tests/test_tools_integration.py",
49
59
  "tests/docker/docker-compose.test.yml",
50
60
  "tests/docker/init-test-db.sql",
51
- ".github/copilot-instructions.md",
52
- ".github/CONTRIBUTING.md",
53
- ".github/pull_request_template.md",
54
- ".github/dependabot.yml",
55
- ".github/FUNDING.yml",
56
- ".github/ISSUE_TEMPLATE/question.md",
57
- ".github/ISSUE_TEMPLATE/bug_report.md",
58
- ".github/ISSUE_TEMPLATE/documentation.md",
59
- ".github/ISSUE_TEMPLATE/config.yml",
60
- ".github/ISSUE_TEMPLATE/feature_request.md",
61
- ".github/workflows/pypi-publish.yml",
62
- ".github/workflows/test.yml",
63
- "_includes/head-custom-google-analytics.html"
61
+ "tests/test_tool_schema_null_defaults.py",
62
+ "tests/test_tools_integration.py",
63
+ "tests/test_version_compat.py",
64
+ "uv.lock"
64
65
  ]
65
66
  }
@@ -0,0 +1,8 @@
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
+ }
@@ -94,7 +94,7 @@ def _wait_for_all_pg(timeout: int = WAIT_TIMEOUT_SEC) -> None:
94
94
  )
95
95
 
96
96
 
97
- @pytest.fixture(scope="session", autouse=True)
97
+ @pytest.fixture(scope="session")
98
98
  def docker_compose_pg():
99
99
  """Start test PostgreSQL containers and tear them down after the session.
100
100
 
@@ -0,0 +1,33 @@
1
+ """Every tool argument that defaults to null must also accept null.
2
+
3
+ FastMCP advertises `"default": null` for an argument written as
4
+ `x: str = None`, but types it `"type": "string"`, so a client that sends the
5
+ advertised default back (some agent frameworks fill in every optional
6
+ argument) fails validation before the tool runs. Offline: no database needed.
7
+ """
8
+ import asyncio
9
+
10
+ from fastmcp import Client
11
+
12
+ from mcp_postgresql_ops.mcp_main import mcp
13
+
14
+
15
+ def _null_default_args_rejecting_null():
16
+ async def collect():
17
+ async with Client(mcp) as client:
18
+ tools = await client.list_tools()
19
+ bad = []
20
+ for tool in tools:
21
+ for name, prop in tool.inputSchema.get("properties", {}).items():
22
+ if "default" not in prop or prop["default"] is not None:
23
+ continue
24
+ types = [prop.get("type")] + [s.get("type") for s in prop.get("anyOf", [])]
25
+ if "null" not in types:
26
+ bad.append(f"{tool.name}.{name}")
27
+ return bad
28
+
29
+ return asyncio.run(collect())
30
+
31
+
32
+ def test_null_default_arguments_accept_null():
33
+ assert _null_default_args_rejecting_null() == []
@@ -52,7 +52,7 @@ get_async_io_status = _fn("get_async_io_status")
52
52
  get_per_backend_io_stats = _fn("get_per_backend_io_stats")
53
53
 
54
54
 
55
- pytestmark = pytest.mark.asyncio
55
+ pytestmark = [pytest.mark.asyncio, pytest.mark.usefixtures("docker_compose_pg")]
56
56
 
57
57
 
58
58
  def assert_tool_result(result, tool_name):