pglens 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,33 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: astral-sh/setup-uv@v4
15
+ - run: uv sync --group dev
16
+ - run: uv run ruff check .
17
+ - run: uv run ruff format --check .
18
+
19
+ typecheck:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - uses: astral-sh/setup-uv@v4
24
+ - run: uv sync --group dev
25
+ - run: uv run mypy .
26
+
27
+ test:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: astral-sh/setup-uv@v4
32
+ - run: uv sync --group dev
33
+ - run: uv run pytest tests/ -v
@@ -0,0 +1,50 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build:
10
+ name: Build distribution
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v5
18
+ with:
19
+ enable-cache: true
20
+
21
+ - name: Build sdist and wheel
22
+ run: uv build
23
+
24
+ - name: Upload dist artifacts
25
+ uses: actions/upload-artifact@v4
26
+ with:
27
+ name: dist
28
+ path: dist/
29
+
30
+ publish:
31
+ name: Publish to PyPI
32
+ needs: build
33
+ runs-on: ubuntu-latest
34
+
35
+ environment:
36
+ name: pypi
37
+ url: https://pypi.org/project/pglens/
38
+
39
+ permissions:
40
+ id-token: write
41
+
42
+ steps:
43
+ - name: Download dist artifacts
44
+ uses: actions/download-artifact@v4
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+
49
+ - name: Publish to PyPI
50
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+
8
+ # Virtual environments
9
+ .venv/
10
+
11
+ # IDE
12
+ .idea/
13
+ .vscode/
14
+ *.swp
15
+
16
+ # Testing
17
+ .pytest_cache/
18
+ .coverage
19
+ htmlcov/
20
+
21
+ # mypy
22
+ .mypy_cache/
23
+
24
+ # ruff
25
+ .ruff_cache/
pglens-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 JeyBee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pglens-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.4
2
+ Name: pglens
3
+ Version: 0.1.0
4
+ Summary: PostgreSQL MCP server for schema inspection, data exploration, and query execution.
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: asyncpg>=0.29
9
+ Requires-Dist: mcp>=1.0
10
+ Description-Content-Type: text/markdown
11
+
12
+ # pglens
13
+
14
+ A PostgreSQL MCP server with tools for schema inspection, data exploration, query execution, and database health monitoring.
15
+
16
+ ## Motivation
17
+
18
+ Most Postgres MCP servers expose `query` and `list_tables`, and that's about it. Agents end up guessing column names, enum values, and join paths, which leads to multiple failed attempts before landing on working SQL.
19
+
20
+ pglens adds the tools that close those gaps: checking what values actually exist in a column, discovering foreign-key relationships, previewing sample data, and validating query plans. The idea is straightforward: let the agent look before it leaps.
21
+
22
+ ## Tools
23
+
24
+ ### Schema and discovery
25
+
26
+ | Tool | What it does |
27
+ |---|---|
28
+ | `list_tables` | Tables with row counts and descriptions |
29
+ | `list_views` | Views with their SQL definitions |
30
+ | `list_extensions` | Installed extensions and versions |
31
+ | `describe_table` | Columns, types, PKs, FKs, indexes, check constraints |
32
+ | `find_related_tables` | FK relationships in both directions |
33
+ | `list_functions` | Stored functions/procedures with source code |
34
+ | `list_triggers` | Triggers on a table with definitions and status |
35
+ | `list_policies` | Row-level security policies on a table |
36
+
37
+ ### Data exploration
38
+
39
+ | Tool | What it does |
40
+ |---|---|
41
+ | `sample_rows` | Random rows from a table |
42
+ | `column_values` | Distinct values with frequency counts |
43
+ | `search_data` | Case-insensitive search across text columns |
44
+ | `search_columns` | Find columns by name across all tables |
45
+ | `search_enum_values` | Enum types and their allowed values |
46
+
47
+ ### Query execution
48
+
49
+ | Tool | What it does |
50
+ |---|---|
51
+ | `explain_query` | Query plan without execution |
52
+ | `query` | Read-only SQL, capped at 500 rows |
53
+
54
+ ### Performance and health
55
+
56
+ | Tool | What it does |
57
+ |---|---|
58
+ | `table_stats` | Index hit rates, dead tuples, vacuum timestamps |
59
+ | `table_sizes` | Disk usage per table, ranked by size |
60
+ | `unused_indexes` | Indexes that are never scanned |
61
+ | `bloat_stats` | Dead tuples, vacuum status, wraparound risk |
62
+ | `active_queries` | Currently running sessions and their queries |
63
+ | `blocking_locks` | Lock wait chains (who blocks whom) |
64
+ | `sequence_health` | Sequences approaching exhaustion |
65
+ | `matview_status` | Materialized view freshness and refresh eligibility |
66
+
67
+ ### Safety before DDL
68
+
69
+ | Tool | What it does |
70
+ |---|---|
71
+ | `object_dependencies` | What depends on a given object (views, functions, constraints) |
72
+
73
+ There is also a `query_guide` prompt that describes a reasonable workflow for using these tools together.
74
+
75
+ ### A note on `column_values`
76
+
77
+ Agents frequently write `WHERE status = 'active'` when the actual value is `'Active'` or `'enabled'`. `column_values` returns the real distinct values in a column with counts, so the agent can pick the right one instead of guessing.
78
+
79
+ ## Installation
80
+
81
+ ```bash
82
+ pip install pglens
83
+ ```
84
+
85
+ Or with [uv](https://docs.astral.sh/uv/):
86
+
87
+ ```bash
88
+ uv pip install pglens
89
+ ```
90
+
91
+ ## Usage
92
+
93
+ pglens reads standard PostgreSQL environment variables. No config files, no flags.
94
+
95
+ ```bash
96
+ export PGHOST=localhost
97
+ export PGUSER=myuser
98
+ export PGPASSWORD=mypassword
99
+ export PGDATABASE=mydb
100
+
101
+ pglens
102
+ ```
103
+
104
+ The server uses stdio transport.
105
+
106
+ ### Claude Desktop
107
+
108
+ ```json
109
+ {
110
+ "mcpServers": {
111
+ "pglens": {
112
+ "command": "pglens",
113
+ "env": {
114
+ "PGHOST": "localhost",
115
+ "PGPORT": "5432",
116
+ "PGUSER": "myuser",
117
+ "PGPASSWORD": "mypassword",
118
+ "PGDATABASE": "mydb"
119
+ }
120
+ }
121
+ }
122
+ }
123
+ ```
124
+
125
+ ### Claude Code
126
+
127
+ ```json
128
+ {
129
+ "mcpServers": {
130
+ "pglens": {
131
+ "command": "pglens",
132
+ "env": {
133
+ "PGHOST": "localhost",
134
+ "PGDATABASE": "mydb"
135
+ }
136
+ }
137
+ }
138
+ }
139
+ ```
140
+
141
+ ### Zed
142
+
143
+ ```json
144
+ {
145
+ "context_servers": {
146
+ "pglens": {
147
+ "command": {
148
+ "path": "pglens",
149
+ "args": []
150
+ }
151
+ }
152
+ }
153
+ }
154
+ ```
155
+
156
+ ## Architecture
157
+
158
+ ```
159
+ mcp_adapter.py (MCP tool definitions, lifespan)
160
+ |
161
+ asyncpg_adapter.py (SQL queries, asyncpg pool)
162
+ |
163
+ PostgreSQL
164
+ ```
165
+
166
+ `AsyncpgDatabase` holds the asyncpg pool and all query methods. The MCP layer is a thin wrapper that delegates to it. All queries use pure `pg_catalog` introspection — no PostgreSQL extensions required.
167
+
168
+ ## Adding a tool
169
+
170
+ 1. Add a method to `AsyncpgDatabase` in `adapters/asyncpg_adapter.py`
171
+ 2. Add a `@mcp.tool()` function in `adapters/mcp_adapter.py`
172
+
173
+ ## Safety
174
+
175
+ - All user-influenced queries run inside `readonly=True` transactions
176
+ - Table and column identifiers are escaped via PostgreSQL's `quote_ident()`
177
+ - No DDL tools are exposed
178
+
179
+ ## Requirements
180
+
181
+ - Python 3.11+
182
+ - PostgreSQL
183
+
184
+ ## License
185
+
186
+ MIT
pglens-0.1.0/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # pglens
2
+
3
+ A PostgreSQL MCP server with tools for schema inspection, data exploration, query execution, and database health monitoring.
4
+
5
+ ## Motivation
6
+
7
+ Most Postgres MCP servers expose `query` and `list_tables`, and that's about it. Agents end up guessing column names, enum values, and join paths, which leads to multiple failed attempts before landing on working SQL.
8
+
9
+ pglens adds the tools that close those gaps: checking what values actually exist in a column, discovering foreign-key relationships, previewing sample data, and validating query plans. The idea is straightforward: let the agent look before it leaps.
10
+
11
+ ## Tools
12
+
13
+ ### Schema and discovery
14
+
15
+ | Tool | What it does |
16
+ |---|---|
17
+ | `list_tables` | Tables with row counts and descriptions |
18
+ | `list_views` | Views with their SQL definitions |
19
+ | `list_extensions` | Installed extensions and versions |
20
+ | `describe_table` | Columns, types, PKs, FKs, indexes, check constraints |
21
+ | `find_related_tables` | FK relationships in both directions |
22
+ | `list_functions` | Stored functions/procedures with source code |
23
+ | `list_triggers` | Triggers on a table with definitions and status |
24
+ | `list_policies` | Row-level security policies on a table |
25
+
26
+ ### Data exploration
27
+
28
+ | Tool | What it does |
29
+ |---|---|
30
+ | `sample_rows` | Random rows from a table |
31
+ | `column_values` | Distinct values with frequency counts |
32
+ | `search_data` | Case-insensitive search across text columns |
33
+ | `search_columns` | Find columns by name across all tables |
34
+ | `search_enum_values` | Enum types and their allowed values |
35
+
36
+ ### Query execution
37
+
38
+ | Tool | What it does |
39
+ |---|---|
40
+ | `explain_query` | Query plan without execution |
41
+ | `query` | Read-only SQL, capped at 500 rows |
42
+
43
+ ### Performance and health
44
+
45
+ | Tool | What it does |
46
+ |---|---|
47
+ | `table_stats` | Index hit rates, dead tuples, vacuum timestamps |
48
+ | `table_sizes` | Disk usage per table, ranked by size |
49
+ | `unused_indexes` | Indexes that are never scanned |
50
+ | `bloat_stats` | Dead tuples, vacuum status, wraparound risk |
51
+ | `active_queries` | Currently running sessions and their queries |
52
+ | `blocking_locks` | Lock wait chains (who blocks whom) |
53
+ | `sequence_health` | Sequences approaching exhaustion |
54
+ | `matview_status` | Materialized view freshness and refresh eligibility |
55
+
56
+ ### Safety before DDL
57
+
58
+ | Tool | What it does |
59
+ |---|---|
60
+ | `object_dependencies` | What depends on a given object (views, functions, constraints) |
61
+
62
+ There is also a `query_guide` prompt that describes a reasonable workflow for using these tools together.
63
+
64
+ ### A note on `column_values`
65
+
66
+ Agents frequently write `WHERE status = 'active'` when the actual value is `'Active'` or `'enabled'`. `column_values` returns the real distinct values in a column with counts, so the agent can pick the right one instead of guessing.
67
+
68
+ ## Installation
69
+
70
+ ```bash
71
+ pip install pglens
72
+ ```
73
+
74
+ Or with [uv](https://docs.astral.sh/uv/):
75
+
76
+ ```bash
77
+ uv pip install pglens
78
+ ```
79
+
80
+ ## Usage
81
+
82
+ pglens reads standard PostgreSQL environment variables. No config files, no flags.
83
+
84
+ ```bash
85
+ export PGHOST=localhost
86
+ export PGUSER=myuser
87
+ export PGPASSWORD=mypassword
88
+ export PGDATABASE=mydb
89
+
90
+ pglens
91
+ ```
92
+
93
+ The server uses stdio transport.
94
+
95
+ ### Claude Desktop
96
+
97
+ ```json
98
+ {
99
+ "mcpServers": {
100
+ "pglens": {
101
+ "command": "pglens",
102
+ "env": {
103
+ "PGHOST": "localhost",
104
+ "PGPORT": "5432",
105
+ "PGUSER": "myuser",
106
+ "PGPASSWORD": "mypassword",
107
+ "PGDATABASE": "mydb"
108
+ }
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ ### Claude Code
115
+
116
+ ```json
117
+ {
118
+ "mcpServers": {
119
+ "pglens": {
120
+ "command": "pglens",
121
+ "env": {
122
+ "PGHOST": "localhost",
123
+ "PGDATABASE": "mydb"
124
+ }
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Zed
131
+
132
+ ```json
133
+ {
134
+ "context_servers": {
135
+ "pglens": {
136
+ "command": {
137
+ "path": "pglens",
138
+ "args": []
139
+ }
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
145
+ ## Architecture
146
+
147
+ ```
148
+ mcp_adapter.py (MCP tool definitions, lifespan)
149
+ |
150
+ asyncpg_adapter.py (SQL queries, asyncpg pool)
151
+ |
152
+ PostgreSQL
153
+ ```
154
+
155
+ `AsyncpgDatabase` holds the asyncpg pool and all query methods. The MCP layer is a thin wrapper that delegates to it. All queries use pure `pg_catalog` introspection — no PostgreSQL extensions required.
156
+
157
+ ## Adding a tool
158
+
159
+ 1. Add a method to `AsyncpgDatabase` in `adapters/asyncpg_adapter.py`
160
+ 2. Add a `@mcp.tool()` function in `adapters/mcp_adapter.py`
161
+
162
+ ## Safety
163
+
164
+ - All user-influenced queries run inside `readonly=True` transactions
165
+ - Table and column identifiers are escaped via PostgreSQL's `quote_ident()`
166
+ - No DDL tools are exposed
167
+
168
+ ## Requirements
169
+
170
+ - Python 3.11+
171
+ - PostgreSQL
172
+
173
+ ## License
174
+
175
+ MIT
@@ -0,0 +1,42 @@
1
+ [project]
2
+ name = "pglens"
3
+ version = "0.1.0"
4
+ description = "PostgreSQL MCP server for schema inspection, data exploration, and query execution."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ dependencies = [
9
+ "asyncpg>=0.29",
10
+ "mcp>=1.0",
11
+ ]
12
+
13
+ [project.scripts]
14
+ pglens = "pglens.__main__:main"
15
+
16
+ [dependency-groups]
17
+ dev = [
18
+ "asyncpg-stubs>=0.29",
19
+ "mypy>=1.10",
20
+ "pytest>=8.0",
21
+ "pytest-asyncio>=0.24",
22
+ "ruff>=0.8",
23
+ ]
24
+
25
+ [build-system]
26
+ requires = ["hatchling"]
27
+ build-backend = "hatchling.build"
28
+
29
+ [tool.hatch.build.targets.wheel]
30
+ packages = ["src/pglens"]
31
+
32
+ [tool.ruff]
33
+ line-length = 100
34
+
35
+ [tool.ruff.lint]
36
+ select = ["E", "F", "I", "UP"]
37
+
38
+ [tool.pytest.ini_options]
39
+ asyncio_mode = "auto"
40
+
41
+ [tool.mypy]
42
+ strict = true
@@ -0,0 +1,3 @@
1
+ from pglens.adapters.mcp_adapter import mcp
2
+
3
+ __all__ = ["mcp"]
@@ -0,0 +1,11 @@
1
+ """Entry point: python -m pglens / uv run pglens."""
2
+
3
+ from pglens.adapters.mcp_adapter import mcp
4
+
5
+
6
+ def main() -> None:
7
+ mcp.run(transport="stdio")
8
+
9
+
10
+ if __name__ == "__main__":
11
+ main()
File without changes