db-conn-mcp 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.
Files changed (35) hide show
  1. db_conn_mcp-0.1.0/.cursorrules +57 -0
  2. db_conn_mcp-0.1.0/.github/workflows/publish.yml +48 -0
  3. db_conn_mcp-0.1.0/.gitignore +43 -0
  4. db_conn_mcp-0.1.0/AGENT_RULES.md +57 -0
  5. db_conn_mcp-0.1.0/ARCHITECTURE.md +312 -0
  6. db_conn_mcp-0.1.0/CLAUDE.md +57 -0
  7. db_conn_mcp-0.1.0/LICENSE +21 -0
  8. db_conn_mcp-0.1.0/PKG-INFO +214 -0
  9. db_conn_mcp-0.1.0/PLAN.md +69 -0
  10. db_conn_mcp-0.1.0/PRD.md +79 -0
  11. db_conn_mcp-0.1.0/README.md +185 -0
  12. db_conn_mcp-0.1.0/docs/superpowers/plans/2026-06-03-db-conn-mcp-v1.md +1367 -0
  13. db_conn_mcp-0.1.0/docs/superpowers/specs/2026-06-03-db-conn-mcp-design.md +246 -0
  14. db_conn_mcp-0.1.0/pyproject.toml +72 -0
  15. db_conn_mcp-0.1.0/src/db_conn_mcp/__init__.py +7 -0
  16. db_conn_mcp-0.1.0/src/db_conn_mcp/__main__.py +10 -0
  17. db_conn_mcp-0.1.0/src/db_conn_mcp/cli.py +571 -0
  18. db_conn_mcp-0.1.0/src/db_conn_mcp/config.py +124 -0
  19. db_conn_mcp-0.1.0/src/db_conn_mcp/diagnostics.py +130 -0
  20. db_conn_mcp-0.1.0/src/db_conn_mcp/dialects/__init__.py +5 -0
  21. db_conn_mcp-0.1.0/src/db_conn_mcp/dialects/base.py +39 -0
  22. db_conn_mcp-0.1.0/src/db_conn_mcp/dialects/postgres.py +148 -0
  23. db_conn_mcp-0.1.0/src/db_conn_mcp/dialects/registry.py +33 -0
  24. db_conn_mcp-0.1.0/src/db_conn_mcp/handlers.py +134 -0
  25. db_conn_mcp-0.1.0/src/db_conn_mcp/models.py +33 -0
  26. db_conn_mcp-0.1.0/src/db_conn_mcp/safety.py +40 -0
  27. db_conn_mcp-0.1.0/src/db_conn_mcp/server.py +109 -0
  28. db_conn_mcp-0.1.0/tests/__init__.py +0 -0
  29. db_conn_mcp-0.1.0/tests/test_cli.py +557 -0
  30. db_conn_mcp-0.1.0/tests/test_config.py +157 -0
  31. db_conn_mcp-0.1.0/tests/test_diagnostics.py +73 -0
  32. db_conn_mcp-0.1.0/tests/test_postgres.py +143 -0
  33. db_conn_mcp-0.1.0/tests/test_safety.py +46 -0
  34. db_conn_mcp-0.1.0/tests/test_server.py +203 -0
  35. db_conn_mcp-0.1.0/tests/test_smoke.py +33 -0
@@ -0,0 +1,57 @@
1
+ # AI Agent Rules (CLAUDE.md / .cursorrules)
2
+
3
+ Welcome, AI Agent (Claude, Agy, Cursor, or otherwise)!
4
+ When assisting the user with the `db-conn-mcp` project, please strictly adhere to the following rules to maintain the project's core philosophy.
5
+
6
+ > **Note:** This file is canonical. `CLAUDE.md` and `.cursorrules` are exact copies — when you change a rule, update all three so they stay byte-identical.
7
+
8
+ ## 1. Simplicity First (No Over-engineering)
9
+ - **DO NOT** introduce complex architectures, custom OAuth servers, or JWT minting.
10
+ - **DO NOT** use complex AST parsers to validate read vs. write queries.
11
+ - **DO** rely on native database features. Read-only enforcement lives inside each dialect (for Postgres v1: set the session read-only with `SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;`).
12
+
13
+ ## 2. Code Quality & Standards (Production Grade)
14
+ - **Ruff:** Use `ruff` as the strict standard for all linting and formatting. Ensure all code passes `ruff check` and `ruff format`.
15
+ - **SOLID & KISS:** Code must be highly modular but radically simple. Follow SOLID principles to keep concerns separated (e.g., config parsing vs. database connection vs. MCP transport), but never at the expense of KISS (Keep It Simple, Stupid).
16
+ - **DevX & AgentX:** The codebase must be highly readable and easy to modify for *both* human developers and AI agents. Keep files reasonably small, avoid deep nesting, and use explicit typing (`typing` module) everywhere.
17
+ - **Documentation:** Well-documented from day one. Every class, module, and non-trivial function must have a clear Python docstring.
18
+
19
+ ## 3. Configuration via JSON
20
+ - The single source of truth for database connections is `connections.json`.
21
+ - The schema is a top-level object `{ "connections": [ ... ] }`; each connection has `name`, `dsn`, `mode` (`"read"` or `"write"`), and an optional `yolo` (boolean, default `false`).
22
+ - Feel free to programmatically read, update, or create `connections.json` for the user if they ask you to add a database.
23
+
24
+ ## 4. Technology Stack
25
+ - Keep the project in Python.
26
+ - Use the official `mcp` SDK (`pip install mcp`).
27
+ - Use standard, modern async libraries where applicable (e.g., `asyncpg`).
28
+ - **`pyproject.toml` is the single source of truth for dependencies** — declare all packages there. Do NOT maintain a separate `requirements.txt`.
29
+ - **v1 ships PostgreSQL only**, but all DB-specific code lives behind a `Dialect` abstraction (`dialects/`) so adding MySQL/SQLite later is a single new file. Never leak a specific database's SQL above the dialect layer.
30
+
31
+ ## 5. MCP Transport Modes
32
+ - The application must support both `stdio` and `http` (SSE) transports.
33
+ - Ensure command-line arguments (using `argparse`) dictate which transport is launched.
34
+
35
+ ## 6. Security & Safety
36
+ - **Never log DSNs** or passwords to the console. Connection diagnostics must be **sanitized** — describe the *category* of failure and how to fix it, never echo the DSN, host, user, or password.
37
+ - Treat the `mode` attribute in `connections.json` as an absolute security boundary. If `mode` is `read`, the AI must not be able to bypass it.
38
+ - Writes are gated server-side in this order: `mode` (hard, native) → `yolo` (per-database persisted trust) → `user_consent` (explicit per-operation approval). `yolo` and `user_consent` only relax the *prompt* on a DB that is already `write`; they can NEVER make a `read` DB writable.
39
+
40
+ ## 7. Living Documentation (Always Keep Docs Updated)
41
+ - **ALWAYS, ALWAYS, ALWAYS** keep the docs in sync with decisions. The moment a design, scope, or architecture decision is made or accepted, update the relevant docs in the SAME change — never defer it:
42
+ - `PRD.md` — product scope, features, tools.
43
+ - `PLAN.md` — implementation roadmap / phases.
44
+ - `ARCHITECTURE.md` — components, flows, diagrams.
45
+ - These rule files (`AGENT_RULES.md` → copied to `CLAUDE.md` and `.cursorrules`) when a rule itself changes.
46
+ - Docs and code must never drift. A change that alters behavior or scope without updating the docs is **incomplete**.
47
+
48
+ ## 8. Commit Hygiene (No Agent Watermarks)
49
+ - **NEVER** add agent/AI watermarks, attributions, or trailers to commits, PRs, or any other output — e.g. no `Co-Authored-By: Claude …`, no "Generated with Claude Code", no 🤖 lines. Commits and messages must read like a normal human developer's work.
50
+
51
+ ## 9. Environment & Sanitation
52
+ - **Always work inside a project-local virtual environment** (`.venv`): `python -m venv .venv`, activate it, and install there. NEVER install dependencies into the global/system Python. `.venv` is git-ignored.
53
+ - **Install from `pyproject.toml`** (the single source of truth — see Rule 4): `pip install -e .`. Do not hand-maintain a `requirements.txt`.
54
+ - **Input sanitation:** validate and safely quote any identifier (database/table/column names) before placing it into catalog/introspection queries — never naive string concatenation. Route user-supplied *values* through driver parameterization. (Raw SQL in `execute_read_query`/`execute_write_query` is intentional and governed by the `mode` → `yolo` → `user_consent` gate.)
55
+ - **Secrets sanitation:** never commit `connections.json` or `.env` (both git-ignored); never print or log DSNs, hosts, or credentials; keep all error/diagnostic output sanitized (see Rule 6).
56
+
57
+ Follow these rules rigidly to ensure `db-conn-mcp` remains the cleanest, most straightforward open-source database MCP server available.
@@ -0,0 +1,48 @@
1
+ name: Publish to PyPI
2
+
3
+ # Publishes to PyPI via Trusted Publishing (OIDC) — no API token stored.
4
+ # Triggers when a GitHub Release is published (create one from a vX.Y.Z tag).
5
+ on:
6
+ release:
7
+ types: [published]
8
+ workflow_dispatch: {}
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ build:
15
+ name: Build distributions
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+ - name: Build wheel and sdist
23
+ run: |
24
+ python -m pip install --upgrade build
25
+ python -m build
26
+ - name: Check metadata
27
+ run: |
28
+ python -m pip install --upgrade twine
29
+ python -m twine check dist/*
30
+ - uses: actions/upload-artifact@v4
31
+ with:
32
+ name: dist
33
+ path: dist/
34
+
35
+ publish:
36
+ name: Publish to PyPI
37
+ needs: build
38
+ runs-on: ubuntu-latest
39
+ environment: pypi
40
+ permissions:
41
+ id-token: write # required for trusted publishing (OIDC)
42
+ steps:
43
+ - uses: actions/download-artifact@v4
44
+ with:
45
+ name: dist
46
+ path: dist/
47
+ - name: Publish
48
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,43 @@
1
+ # ---- Local-only files ----
2
+ progress.txt
3
+
4
+ # ---- Secrets / local config (NEVER commit real DSNs or credentials) ----
5
+ connections.json
6
+ .env
7
+ .env.*
8
+
9
+ # ---- Python bytecode / caches ----
10
+ __pycache__/
11
+ *.py[cod]
12
+ *$py.class
13
+ *.so
14
+
15
+ # ---- Virtual environments ----
16
+ .venv/
17
+ venv/
18
+ env/
19
+ ENV/
20
+
21
+ # ---- Packaging / build artifacts ----
22
+ build/
23
+ dist/
24
+ *.egg
25
+ *.egg-info/
26
+ .eggs/
27
+ wheels/
28
+
29
+ # ---- Tooling caches ----
30
+ .ruff_cache/
31
+ .pytest_cache/
32
+ .mypy_cache/
33
+ .coverage
34
+ .coverage.*
35
+ htmlcov/
36
+ .tox/
37
+
38
+ # ---- IDE / OS noise ----
39
+ .idea/
40
+ .vscode/
41
+ *.swp
42
+ .DS_Store
43
+ Thumbs.db
@@ -0,0 +1,57 @@
1
+ # AI Agent Rules (CLAUDE.md / .cursorrules)
2
+
3
+ Welcome, AI Agent (Claude, Agy, Cursor, or otherwise)!
4
+ When assisting the user with the `db-conn-mcp` project, please strictly adhere to the following rules to maintain the project's core philosophy.
5
+
6
+ > **Note:** This file is canonical. `CLAUDE.md` and `.cursorrules` are exact copies — when you change a rule, update all three so they stay byte-identical.
7
+
8
+ ## 1. Simplicity First (No Over-engineering)
9
+ - **DO NOT** introduce complex architectures, custom OAuth servers, or JWT minting.
10
+ - **DO NOT** use complex AST parsers to validate read vs. write queries.
11
+ - **DO** rely on native database features. Read-only enforcement lives inside each dialect (for Postgres v1: set the session read-only with `SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;`).
12
+
13
+ ## 2. Code Quality & Standards (Production Grade)
14
+ - **Ruff:** Use `ruff` as the strict standard for all linting and formatting. Ensure all code passes `ruff check` and `ruff format`.
15
+ - **SOLID & KISS:** Code must be highly modular but radically simple. Follow SOLID principles to keep concerns separated (e.g., config parsing vs. database connection vs. MCP transport), but never at the expense of KISS (Keep It Simple, Stupid).
16
+ - **DevX & AgentX:** The codebase must be highly readable and easy to modify for *both* human developers and AI agents. Keep files reasonably small, avoid deep nesting, and use explicit typing (`typing` module) everywhere.
17
+ - **Documentation:** Well-documented from day one. Every class, module, and non-trivial function must have a clear Python docstring.
18
+
19
+ ## 3. Configuration via JSON
20
+ - The single source of truth for database connections is `connections.json`.
21
+ - The schema is a top-level object `{ "connections": [ ... ] }`; each connection has `name`, `dsn`, `mode` (`"read"` or `"write"`), and an optional `yolo` (boolean, default `false`).
22
+ - Feel free to programmatically read, update, or create `connections.json` for the user if they ask you to add a database.
23
+
24
+ ## 4. Technology Stack
25
+ - Keep the project in Python.
26
+ - Use the official `mcp` SDK (`pip install mcp`).
27
+ - Use standard, modern async libraries where applicable (e.g., `asyncpg`).
28
+ - **`pyproject.toml` is the single source of truth for dependencies** — declare all packages there. Do NOT maintain a separate `requirements.txt`.
29
+ - **v1 ships PostgreSQL only**, but all DB-specific code lives behind a `Dialect` abstraction (`dialects/`) so adding MySQL/SQLite later is a single new file. Never leak a specific database's SQL above the dialect layer.
30
+
31
+ ## 5. MCP Transport Modes
32
+ - The application must support both `stdio` and `http` (SSE) transports.
33
+ - Ensure command-line arguments (using `argparse`) dictate which transport is launched.
34
+
35
+ ## 6. Security & Safety
36
+ - **Never log DSNs** or passwords to the console. Connection diagnostics must be **sanitized** — describe the *category* of failure and how to fix it, never echo the DSN, host, user, or password.
37
+ - Treat the `mode` attribute in `connections.json` as an absolute security boundary. If `mode` is `read`, the AI must not be able to bypass it.
38
+ - Writes are gated server-side in this order: `mode` (hard, native) → `yolo` (per-database persisted trust) → `user_consent` (explicit per-operation approval). `yolo` and `user_consent` only relax the *prompt* on a DB that is already `write`; they can NEVER make a `read` DB writable.
39
+
40
+ ## 7. Living Documentation (Always Keep Docs Updated)
41
+ - **ALWAYS, ALWAYS, ALWAYS** keep the docs in sync with decisions. The moment a design, scope, or architecture decision is made or accepted, update the relevant docs in the SAME change — never defer it:
42
+ - `PRD.md` — product scope, features, tools.
43
+ - `PLAN.md` — implementation roadmap / phases.
44
+ - `ARCHITECTURE.md` — components, flows, diagrams.
45
+ - These rule files (`AGENT_RULES.md` → copied to `CLAUDE.md` and `.cursorrules`) when a rule itself changes.
46
+ - Docs and code must never drift. A change that alters behavior or scope without updating the docs is **incomplete**.
47
+
48
+ ## 8. Commit Hygiene (No Agent Watermarks)
49
+ - **NEVER** add agent/AI watermarks, attributions, or trailers to commits, PRs, or any other output — e.g. no `Co-Authored-By: Claude …`, no "Generated with Claude Code", no 🤖 lines. Commits and messages must read like a normal human developer's work.
50
+
51
+ ## 9. Environment & Sanitation
52
+ - **Always work inside a project-local virtual environment** (`.venv`): `python -m venv .venv`, activate it, and install there. NEVER install dependencies into the global/system Python. `.venv` is git-ignored.
53
+ - **Install from `pyproject.toml`** (the single source of truth — see Rule 4): `pip install -e .`. Do not hand-maintain a `requirements.txt`.
54
+ - **Input sanitation:** validate and safely quote any identifier (database/table/column names) before placing it into catalog/introspection queries — never naive string concatenation. Route user-supplied *values* through driver parameterization. (Raw SQL in `execute_read_query`/`execute_write_query` is intentional and governed by the `mode` → `yolo` → `user_consent` gate.)
55
+ - **Secrets sanitation:** never commit `connections.json` or `.env` (both git-ignored); never print or log DSNs, hosts, or credentials; keep all error/diagnostic output sanitized (see Rule 6).
56
+
57
+ Follow these rules rigidly to ensure `db-conn-mcp` remains the cleanest, most straightforward open-source database MCP server available.
@@ -0,0 +1,312 @@
1
+ # Architecture: `db-conn-mcp`
2
+
3
+ This document shows how `db-conn-mcp` is structured and traces the **end-to-end flow**, from a user setting up a connection on the CLI, through an AI agent exploring and querying the database, to the write-safety gate that protects their data.
4
+
5
+ > **v1 scope:** PostgreSQL only. The code is built around a **dialect seam** so adding MySQL / SQLite later is a *one-file* job. See [Extensibility](#extensibility-adding-a-database).
6
+
7
+ ---
8
+
9
+ ## 1. Component Overview
10
+
11
+ ```
12
+ ┌──────────────────────────────────────────┐
13
+ ┌──────────────┐ │ db-conn-mcp │
14
+ │ AI Agent │ MCP │ server.py ── 8 tools + 1 prompt │
15
+ │ (Claude, │◄───────►│ │ │
16
+ │ Cursor, …) │ stdio/ │ ├─► safety.py ── write-gate │
17
+ └──────────────┘ http │ │ │
18
+ │ ├─► diagnostics.py ── doctor / │
19
+ ┌──────────────┐ │ │ sanitized error → cause + fix │
20
+ │ Human user │ CLI │ ▼ │
21
+ │ (terminal) │────────►│ dialects/ ── Postgres SQL + RO guard │
22
+ └──────────────┘ setup │ (registry) │ config.py ◄─ conns │
23
+ └──────┼───────────────────────│──────────┘
24
+ ▼ ▼
25
+ ┌─────────────┐ ┌────────────────┐
26
+ │ PostgreSQL │ │connections.json│
27
+ └─────────────┘ └────────────────┘
28
+ ```
29
+
30
+ ### Module responsibilities (single-purpose layers)
31
+
32
+ | Module | Responsibility | Knows about Postgres? |
33
+ |---|---|---|
34
+ | `cli.py` | Parse args (`--config`, `--transport`); run server or a management subcommand (`setup`/`status`/`add`/`clients`/`remove`/`yolo`); detect & inject MCP clients | No |
35
+ | `config.py` | Resolve, load, validate, **and save** `connections.json` | No |
36
+ | `models.py` | Pydantic types: `Connection{name, dsn, mode, yolo}`, `Config` | No |
37
+ | `dialects/base.py` | The `Dialect` ABC — the extensibility contract | No |
38
+ | `dialects/postgres.py` | `asyncpg` impl + native read-only enforcement | **Yes (only here)** |
39
+ | `dialects/registry.py` | Map DSN scheme → `Dialect`; clear error on unknown scheme | No |
40
+ | `safety.py` | Pure write-gate decision (`mode` + `yolo` + `consent`) | No |
41
+ | `diagnostics.py` | Classify driver errors → **sanitized** cause + fix; the doctor | No |
42
+ | `handlers.py` | The 8 tool handlers as plain async methods (transport-free, unit-testable) | No |
43
+ | `server.py` | `FastMCP` app: registers the 8 tools + the prompt onto `handlers`, transport wiring | No |
44
+
45
+ The **dialect layer is the only place that knows a database is PostgreSQL.** Everything above it speaks the abstract `Dialect` contract.
46
+
47
+ ---
48
+
49
+ ## 2. The Tools an Agent Sees
50
+
51
+ | # | Tool | Kind | Safety |
52
+ |---|---|---|---|
53
+ | 1 | `list_databases` | Explore | safe — names + mode + yolo |
54
+ | 2 | `list_tables` | Explore | safe |
55
+ | 3 | `get_table_schema` | Explore | safe |
56
+ | 4 | `sample_table_rows` | Explore | safe (first N rows) |
57
+ | 5 | `execute_read_query` | Execute | runs inside a **read-only transaction** |
58
+ | 6 | `execute_write_query` | Execute | **gated** (mode → yolo → consent) |
59
+ | 7 | `set_yolo_mode` | Config | persists `yolo` flag for one named DB |
60
+ | 8 | `check_database` | Doctor | tests one DB (or all) → `OK` or sanitized cause + fix |
61
+
62
+ Plus **one MCP prompt** — `troubleshoot_connection` — a discoverable, full connection-gotchas checklist the agent can pull when a DB won't connect (see [§7](#7-flow-e--self-diagnosing-connections-the-doctor)).
63
+
64
+ ---
65
+
66
+ ## 3. Flow A — Setup (human, on the CLI)
67
+
68
+ A human runs the wizard once to register a database. This is the *only* step that handles a raw DSN.
69
+
70
+ ```mermaid
71
+ sequenceDiagram
72
+ actor User
73
+ participant CLI as setup wizard (cli.py)
74
+ participant Cfg as config.py
75
+ participant FS as connections.json
76
+
77
+ User->>CLI: python -m db_conn_mcp setup
78
+ CLI->>User: Scope? (global ~/.db-conn-mcp | repo ./)
79
+ User-->>CLI: global
80
+ CLI->>User: Name? DSN? Mode (read/write)?
81
+ User-->>CLI: "prod", "postgresql://…", "read"
82
+ CLI->>Cfg: add_connection(Connection)
83
+ Cfg->>Cfg: validate (pydantic + scheme is known)
84
+ Cfg->>FS: write connections.json (yolo defaults to false)
85
+ CLI->>User: ✅ registered. Inject into Claude/Cursor config? (y/n)
86
+ Note over CLI,FS: DSN is never logged — only the name is echoed.
87
+ ```
88
+
89
+ Resulting `connections.json`:
90
+ ```json
91
+ {
92
+ "connections": [
93
+ { "name": "prod", "dsn": "postgresql://…", "mode": "read" },
94
+ { "name": "dev", "dsn": "postgresql://…", "mode": "write", "yolo": false }
95
+ ]
96
+ }
97
+ ```
98
+
99
+ **Config resolution order** (first match wins): `--config <path>` → `./connections.json` → `~/.db-conn-mcp/connections.json`.
100
+
101
+ ---
102
+
103
+ ## 4. Flow B — Agent explores, then runs a READ query
104
+
105
+ The everyday safe path. The agent discovers what exists, learns the schema, then queries.
106
+
107
+ ```mermaid
108
+ sequenceDiagram
109
+ actor Agent
110
+ participant Server as server.py
111
+ participant Reg as dialects/registry
112
+ participant PG as PostgresDialect
113
+ participant DB as PostgreSQL
114
+
115
+ Agent->>Server: list_databases()
116
+ Server-->>Agent: [{prod, read}, {dev, write, yolo:false}]
117
+
118
+ Agent->>Server: list_tables(db="prod")
119
+ Server->>Reg: dialect_for("postgresql://…")
120
+ Reg-->>Server: PostgresDialect
121
+ Server->>PG: connect(dsn, read_only=true)
122
+ PG->>DB: SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY
123
+ PG->>DB: query information_schema
124
+ DB-->>Agent: [users, orders, …]
125
+
126
+ Agent->>Server: get_table_schema(db="prod", table="orders")
127
+ Server->>PG: get_schema(conn, "orders")
128
+ PG-->>Agent: columns, types, PK/FK
129
+
130
+ Agent->>Server: execute_read_query(db="prod", sql="SELECT …")
131
+ Server->>PG: connect(read_only=true) → execute(sql)
132
+ Note over PG,DB: A write here would be rejected by Postgres itself.
133
+ DB-->>Agent: rows
134
+ ```
135
+
136
+ Read-only is enforced **natively by the database** — even a `read`-mode connection physically cannot mutate data, regardless of what SQL is sent.
137
+
138
+ ---
139
+
140
+ ## 5. Flow C — Agent runs a WRITE query (the safety gate)
141
+
142
+ This is the heart of the safety model. The decision lives **in the server**, not in the agent's good intentions.
143
+
144
+ ```mermaid
145
+ flowchart TD
146
+ A[execute_write_query db, sql, user_consent] --> B{db.mode == write?}
147
+ B -- no --> R1[❌ REJECT: db is read-only]
148
+ B -- yes --> C{db.yolo == true?}
149
+ C -- yes --> RUN[✅ run write SQL]
150
+ C -- no --> D{user_consent == true?}
151
+ D -- yes --> RUN
152
+ D -- no --> R2[❌ REJECT:<br/>'Read the table & schema, show the<br/>exact SQL to the user, get a yes,<br/>then call again with user_consent=true']
153
+ ```
154
+
155
+ The intended agent choreography for a non-yolo write:
156
+
157
+ ```mermaid
158
+ sequenceDiagram
159
+ actor User
160
+ actor Agent
161
+ participant Server as server.py
162
+ participant PG as PostgresDialect
163
+ participant DB as PostgreSQL
164
+
165
+ Note over Agent: 1. Understand before changing
166
+ Agent->>Server: get_table_schema(db="dev", table="users")
167
+ Server->>PG: get_schema → columns/types
168
+ PG-->>Agent: schema
169
+ Agent->>Server: sample_table_rows(db="dev", table="users")
170
+ PG-->>Agent: example rows (learn the data shape)
171
+
172
+ Note over Agent,User: 2. First call WITHOUT consent — server forces the ask
173
+ Agent->>Server: execute_write_query(db="dev", sql="UPDATE users SET …", user_consent=false)
174
+ Server-->>Agent: ❌ REJECT — show SQL to user, get explicit yes
175
+
176
+ Agent->>User: "I will run: UPDATE users SET … (affects ~3 rows). Proceed?"
177
+ User-->>Agent: "yes"
178
+
179
+ Note over Agent,DB: 3. Re-call WITH consent
180
+ Agent->>Server: execute_write_query(db="dev", sql="UPDATE users SET …", user_consent=true)
181
+ Server->>PG: connect(read_only=false) → execute(sql)
182
+ DB-->>Agent: rows affected
183
+ ```
184
+
185
+ `mode` is the **hard boundary** (native, unbypassable). `yolo` and `user_consent` only relax the *prompt* on a DB that is *already* `write` — they can never make a `read` DB writable.
186
+
187
+ ---
188
+
189
+ ## 6. Flow D — Enabling YOLO mode (persisted)
190
+
191
+ When the user is tired of confirming every write on a trusted DB, they ask the agent to enable yolo. The server writes it back to disk so it survives restarts.
192
+
193
+ ```mermaid
194
+ sequenceDiagram
195
+ actor User
196
+ actor Agent
197
+ participant Server as server.py
198
+ participant Cfg as config.py
199
+ participant FS as connections.json
200
+
201
+ User->>Agent: "Enable yolo mode for dev — stop asking me."
202
+ Agent->>Server: set_yolo_mode(database="dev", enabled=true)
203
+ Server->>Cfg: set_yolo("dev", true)
204
+ Cfg->>FS: rewrite connections.json (dev.yolo = true)
205
+ Server-->>Agent: ✅ yolo enabled for "dev" (persisted)
206
+
207
+ Note over Agent,FS: Future writes to "dev" skip the consent gate —<br/>across this and all future sessions.
208
+ ```
209
+
210
+ `set_yolo_mode("dev", false)` reverts it. YOLO is **per-database**: enabling it on `dev` never affects `prod`.
211
+
212
+ ---
213
+
214
+ ## 7. Flow E — Self-diagnosing connections (the doctor)
215
+
216
+ Connections are validated **lazily**: the server boots even if a database is down, and *any* tool that hits an unreachable DB gets a sanitized, actionable diagnostic instead of a raw stack trace. The agent (or user) can also probe proactively with `check_database`.
217
+
218
+ ```mermaid
219
+ sequenceDiagram
220
+ actor Agent
221
+ participant Server as server.py
222
+ participant Diag as diagnostics.py
223
+ participant PG as PostgresDialect
224
+ participant DB as PostgreSQL
225
+
226
+ Agent->>Server: check_database(name="prod")
227
+ Server->>PG: connect(dsn, read_only=true)
228
+ PG->>DB: TCP connect / auth
229
+ DB--xPG: error (refused / auth / no-db / SSL / timeout)
230
+ PG-->>Server: raises driver exception
231
+ Server->>Diag: explain(error) ⟵ DSN/password NEVER passed in
232
+ Diag-->>Server: {category, cause, fixes[]}
233
+ Server-->>Agent: "❌ prod unreachable — AUTH FAILED.<br/>Likely: wrong user/password or role missing.<br/>Fix: verify credentials; confirm the role exists.<br/>(full checklist: prompt 'troubleshoot_connection')"
234
+ Note over Agent: For the complete gotchas list:
235
+ Agent->>Server: getPrompt("troubleshoot_connection")
236
+ Server-->>Agent: full host/port/firewall/sslmode/<br/>container-localhost/db-name/pool checklist
237
+ ```
238
+
239
+ **Error classification** (`diagnostics.py` maps driver exception → sanitized advice — credentials never appear in the output):
240
+
241
+ | Category | Triggered by (examples) | Sanitized cause + fix shown to agent |
242
+ |---|---|---|
243
+ | `AUTH_FAILED` | invalid password / role does not exist | "Wrong user/password, or the role doesn't exist. Verify credentials and that the role exists." |
244
+ | `HOST_UNREACHABLE` | connection refused / timeout | "DB not accepting connections. Is it running? Check host/port, firewall. In Docker, `localhost` ≠ the DB container." |
245
+ | `DB_NOT_FOUND` | database "x" does not exist | "Database name is wrong or not created yet. Check spelling/case; create it if needed." |
246
+ | `DNS_FAILURE` | could not translate host name | "Hostname can't be resolved — likely a typo in the host or a missing VPN/network." |
247
+ | `SSL_REQUIRED` | server requires SSL | "Server demands SSL. Add `?sslmode=require` (or appropriate mode) to the DSN." |
248
+ | `POOL_EXHAUSTED` | too many connections | "Connection limit hit. Close idle sessions or raise the server's `max_connections`." |
249
+ | `UNKNOWN` | anything unmatched | "Unrecognized connection error. See the `troubleshoot_connection` prompt for the full checklist." |
250
+
251
+ The **`troubleshoot_connection` MCP prompt** is the standalone, full gotchas checklist — discoverable by the agent at any time, not just on error. Targeted fixes appear inline per error; the prompt is the complete reference.
252
+
253
+ > **Security:** `diagnostics.explain()` receives only the *driver exception type/message-class* — never the `Connection` object or DSN — so no host, user, or password can leak into a tool response or log.
254
+
255
+ ---
256
+
257
+ ## 8. Extensibility — Adding a Database
258
+
259
+ Adding MySQL or SQLite later touches **exactly one new file** plus a one-line registration. Nothing above the dialect layer changes.
260
+
261
+ ```mermaid
262
+ flowchart LR
263
+ subgraph Today [v1 — today]
264
+ REG[registry] --> PGD[postgres.py]
265
+ end
266
+ subgraph Later [adding MySQL]
267
+ REG2[registry<br/>+1 line] --> PGD2[postgres.py]
268
+ REG2 -.new file.-> MYD[mysql.py]
269
+ end
270
+ Today ==> Later
271
+ ```
272
+
273
+ The contract a new dialect must satisfy:
274
+
275
+ ```python
276
+ class Dialect(ABC):
277
+ scheme: str # e.g. "mysql"
278
+ async def connect(self, dsn, *, read_only): ... # native read-only enforcement lives here
279
+ async def list_tables(self, conn): ...
280
+ async def get_schema(self, conn, table): ...
281
+ async def sample_rows(self, conn, table, n=10): ...
282
+ async def execute(self, conn, sql): ...
283
+ ```
284
+
285
+ | DB | Read-only mechanism (inside `connect`) | Introspection source |
286
+ |---|---|---|
287
+ | PostgreSQL (v1) | `SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY` | `information_schema` |
288
+ | MySQL (future) | `START TRANSACTION READ ONLY` | `information_schema` |
289
+ | SQLite (future) | open file with `?mode=ro` | `sqlite_master` / `PRAGMA` |
290
+
291
+ Because each dialect owns its own read-only enforcement and catalog queries, those per-database differences **never leak** into `safety.py` or `server.py`.
292
+
293
+ ---
294
+
295
+ ## 9. End-to-End at a Glance
296
+
297
+ ```
298
+ Human ──setup──► connections.json ──read──► config.py
299
+ │
300
+ AI Agent ──MCP (stdio/http)──► server.py ─────┼─► safety.py (write gate)
301
+ │ ├─► diagnostics.py (doctor, sanitized)
302
+ ▼ ▼
303
+ dialects/registry ──► PostgresDialect ──► PostgreSQL
304
+ (scheme → impl) (RO guard + SQL)
305
+ ```
306
+
307
+ 1. A human registers a DB once via the CLI wizard → `connections.json`.
308
+ 2. An agent connects over MCP and **explores** (`list_*`, `get_table_schema`, `sample_table_rows`) — always read-only.
309
+ 3. **Reads** run inside a native read-only transaction.
310
+ 4. **Writes** pass through the server-side gate: `mode` (hard) → `yolo` (persisted trust) → `user_consent` (per-op ask).
311
+ 5. `set_yolo_mode` lets a trusted DB skip the per-op ask, persisted to disk.
312
+ 6. Any unreachable DB yields a **sanitized diagnostic** (cause + fix), never a raw error or leaked DSN; `check_database` probes proactively and `troubleshoot_connection` is the full gotchas checklist.
@@ -0,0 +1,57 @@
1
+ # AI Agent Rules (CLAUDE.md / .cursorrules)
2
+
3
+ Welcome, AI Agent (Claude, Agy, Cursor, or otherwise)!
4
+ When assisting the user with the `db-conn-mcp` project, please strictly adhere to the following rules to maintain the project's core philosophy.
5
+
6
+ > **Note:** This file is canonical. `CLAUDE.md` and `.cursorrules` are exact copies — when you change a rule, update all three so they stay byte-identical.
7
+
8
+ ## 1. Simplicity First (No Over-engineering)
9
+ - **DO NOT** introduce complex architectures, custom OAuth servers, or JWT minting.
10
+ - **DO NOT** use complex AST parsers to validate read vs. write queries.
11
+ - **DO** rely on native database features. Read-only enforcement lives inside each dialect (for Postgres v1: set the session read-only with `SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;`).
12
+
13
+ ## 2. Code Quality & Standards (Production Grade)
14
+ - **Ruff:** Use `ruff` as the strict standard for all linting and formatting. Ensure all code passes `ruff check` and `ruff format`.
15
+ - **SOLID & KISS:** Code must be highly modular but radically simple. Follow SOLID principles to keep concerns separated (e.g., config parsing vs. database connection vs. MCP transport), but never at the expense of KISS (Keep It Simple, Stupid).
16
+ - **DevX & AgentX:** The codebase must be highly readable and easy to modify for *both* human developers and AI agents. Keep files reasonably small, avoid deep nesting, and use explicit typing (`typing` module) everywhere.
17
+ - **Documentation:** Well-documented from day one. Every class, module, and non-trivial function must have a clear Python docstring.
18
+
19
+ ## 3. Configuration via JSON
20
+ - The single source of truth for database connections is `connections.json`.
21
+ - The schema is a top-level object `{ "connections": [ ... ] }`; each connection has `name`, `dsn`, `mode` (`"read"` or `"write"`), and an optional `yolo` (boolean, default `false`).
22
+ - Feel free to programmatically read, update, or create `connections.json` for the user if they ask you to add a database.
23
+
24
+ ## 4. Technology Stack
25
+ - Keep the project in Python.
26
+ - Use the official `mcp` SDK (`pip install mcp`).
27
+ - Use standard, modern async libraries where applicable (e.g., `asyncpg`).
28
+ - **`pyproject.toml` is the single source of truth for dependencies** — declare all packages there. Do NOT maintain a separate `requirements.txt`.
29
+ - **v1 ships PostgreSQL only**, but all DB-specific code lives behind a `Dialect` abstraction (`dialects/`) so adding MySQL/SQLite later is a single new file. Never leak a specific database's SQL above the dialect layer.
30
+
31
+ ## 5. MCP Transport Modes
32
+ - The application must support both `stdio` and `http` (SSE) transports.
33
+ - Ensure command-line arguments (using `argparse`) dictate which transport is launched.
34
+
35
+ ## 6. Security & Safety
36
+ - **Never log DSNs** or passwords to the console. Connection diagnostics must be **sanitized** — describe the *category* of failure and how to fix it, never echo the DSN, host, user, or password.
37
+ - Treat the `mode` attribute in `connections.json` as an absolute security boundary. If `mode` is `read`, the AI must not be able to bypass it.
38
+ - Writes are gated server-side in this order: `mode` (hard, native) → `yolo` (per-database persisted trust) → `user_consent` (explicit per-operation approval). `yolo` and `user_consent` only relax the *prompt* on a DB that is already `write`; they can NEVER make a `read` DB writable.
39
+
40
+ ## 7. Living Documentation (Always Keep Docs Updated)
41
+ - **ALWAYS, ALWAYS, ALWAYS** keep the docs in sync with decisions. The moment a design, scope, or architecture decision is made or accepted, update the relevant docs in the SAME change — never defer it:
42
+ - `PRD.md` — product scope, features, tools.
43
+ - `PLAN.md` — implementation roadmap / phases.
44
+ - `ARCHITECTURE.md` — components, flows, diagrams.
45
+ - These rule files (`AGENT_RULES.md` → copied to `CLAUDE.md` and `.cursorrules`) when a rule itself changes.
46
+ - Docs and code must never drift. A change that alters behavior or scope without updating the docs is **incomplete**.
47
+
48
+ ## 8. Commit Hygiene (No Agent Watermarks)
49
+ - **NEVER** add agent/AI watermarks, attributions, or trailers to commits, PRs, or any other output — e.g. no `Co-Authored-By: Claude …`, no "Generated with Claude Code", no 🤖 lines. Commits and messages must read like a normal human developer's work.
50
+
51
+ ## 9. Environment & Sanitation
52
+ - **Always work inside a project-local virtual environment** (`.venv`): `python -m venv .venv`, activate it, and install there. NEVER install dependencies into the global/system Python. `.venv` is git-ignored.
53
+ - **Install from `pyproject.toml`** (the single source of truth — see Rule 4): `pip install -e .`. Do not hand-maintain a `requirements.txt`.
54
+ - **Input sanitation:** validate and safely quote any identifier (database/table/column names) before placing it into catalog/introspection queries — never naive string concatenation. Route user-supplied *values* through driver parameterization. (Raw SQL in `execute_read_query`/`execute_write_query` is intentional and governed by the `mode` → `yolo` → `user_consent` gate.)
55
+ - **Secrets sanitation:** never commit `connections.json` or `.env` (both git-ignored); never print or log DSNs, hosts, or credentials; keep all error/diagnostic output sanitized (see Rule 6).
56
+
57
+ Follow these rules rigidly to ensure `db-conn-mcp` remains the cleanest, most straightforward open-source database MCP server available.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 db-conn-mcp contributors
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.