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.
- pglens-0.1.0/.github/workflows/ci.yml +33 -0
- pglens-0.1.0/.github/workflows/release.yml +50 -0
- pglens-0.1.0/.gitignore +25 -0
- pglens-0.1.0/LICENSE +21 -0
- pglens-0.1.0/PKG-INFO +186 -0
- pglens-0.1.0/README.md +175 -0
- pglens-0.1.0/pyproject.toml +42 -0
- pglens-0.1.0/src/pglens/__init__.py +3 -0
- pglens-0.1.0/src/pglens/__main__.py +11 -0
- pglens-0.1.0/src/pglens/adapters/__init__.py +0 -0
- pglens-0.1.0/src/pglens/adapters/asyncpg_adapter.py +777 -0
- pglens-0.1.0/src/pglens/adapters/mcp_adapter.py +321 -0
- pglens-0.1.0/src/pglens/core/__init__.py +0 -0
- pglens-0.1.0/src/pglens/core/types.py +43 -0
- pglens-0.1.0/tests/__init__.py +0 -0
- pglens-0.1.0/tests/test_asyncpg_adapter.py +224 -0
- pglens-0.1.0/tests/test_object_type_to_relkind.py +19 -0
- pglens-0.1.0/uv.lock +982 -0
|
@@ -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
|
pglens-0.1.0/.gitignore
ADDED
|
@@ -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
|
|
File without changes
|