udbmcp 0.2.0__py3-none-any.whl

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 (55) hide show
  1. udbmcp-0.2.0.dist-info/METADATA +403 -0
  2. udbmcp-0.2.0.dist-info/RECORD +55 -0
  3. udbmcp-0.2.0.dist-info/WHEEL +4 -0
  4. udbmcp-0.2.0.dist-info/entry_points.txt +2 -0
  5. udbmcp-0.2.0.dist-info/licenses/LICENSE +202 -0
  6. udbmcp-0.2.0.dist-info/licenses/NOTICE +14 -0
  7. universal_db_mcp/__init__.py +3 -0
  8. universal_db_mcp/__main__.py +1136 -0
  9. universal_db_mcp/agents/__init__.py +1 -0
  10. universal_db_mcp/agents/claude_code.py +536 -0
  11. universal_db_mcp/agents/claude_desktop.py +465 -0
  12. universal_db_mcp/agents/cline.py +378 -0
  13. universal_db_mcp/agents/core.py +1710 -0
  14. universal_db_mcp/agents/cursor.py +394 -0
  15. universal_db_mcp/agents/dsh.py +769 -0
  16. universal_db_mcp/agents/registry.py +128 -0
  17. universal_db_mcp/agents/vscode.py +578 -0
  18. universal_db_mcp/config.py +1646 -0
  19. universal_db_mcp/connectors/__init__.py +0 -0
  20. universal_db_mcp/connectors/base.py +503 -0
  21. universal_db_mcp/connectors/clickhouse.py +1540 -0
  22. universal_db_mcp/connectors/db2.py +1166 -0
  23. universal_db_mcp/connectors/driver_helpers.py +691 -0
  24. universal_db_mcp/connectors/mssql.py +1148 -0
  25. universal_db_mcp/connectors/mysql.py +1207 -0
  26. universal_db_mcp/connectors/oracle.py +1297 -0
  27. universal_db_mcp/connectors/postgres.py +1116 -0
  28. universal_db_mcp/connectors/registry.py +44 -0
  29. universal_db_mcp/connectors/sqlite.py +1259 -0
  30. universal_db_mcp/diagnostics/__init__.py +0 -0
  31. universal_db_mcp/diagnostics/doctor.py +1233 -0
  32. universal_db_mcp/diagnostics/site_check.py +272 -0
  33. universal_db_mcp/discovery/__init__.py +1 -0
  34. universal_db_mcp/discovery/document.py +157 -0
  35. universal_db_mcp/discovery/inference.py +156 -0
  36. universal_db_mcp/discovery/profile.py +263 -0
  37. universal_db_mcp/discovery/system_schemas.py +602 -0
  38. universal_db_mcp/discovery/types.py +201 -0
  39. universal_db_mcp/errors.py +24 -0
  40. universal_db_mcp/http_protocol.py +599 -0
  41. universal_db_mcp/models/__init__.py +12 -0
  42. universal_db_mcp/models/capabilities.py +79 -0
  43. universal_db_mcp/models/responses.py +62 -0
  44. universal_db_mcp/security/__init__.py +0 -0
  45. universal_db_mcp/security/cursors.py +137 -0
  46. universal_db_mcp/security/policy.py +299 -0
  47. universal_db_mcp/security/redact.py +202 -0
  48. universal_db_mcp/security/session.py +87 -0
  49. universal_db_mcp/security/sql_guard.py +2627 -0
  50. universal_db_mcp/server.py +6201 -0
  51. universal_db_mcp/services/__init__.py +0 -0
  52. universal_db_mcp/services/audit.py +911 -0
  53. universal_db_mcp/services/executor.py +979 -0
  54. universal_db_mcp/services/metadata.py +465 -0
  55. universal_db_mcp/wizard.py +1604 -0
@@ -0,0 +1,403 @@
1
+ Metadata-Version: 2.5
2
+ Name: udbmcp
3
+ Version: 0.2.0
4
+ Summary: Read-only MCP server that gives AI agents safe access to your databases
5
+ Project-URL: Homepage, https://github.com/alghanim/universal-db-mcp
6
+ Project-URL: Repository, https://github.com/alghanim/universal-db-mcp
7
+ Project-URL: Issues, https://github.com/alghanim/universal-db-mcp/issues
8
+ Project-URL: Security, https://github.com/alghanim/universal-db-mcp/security/policy
9
+ Author: universal-db-mcp maintainers
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ License-File: NOTICE
13
+ Keywords: ai-agents,clickhouse,database,db2,mariadb,mcp,model-context-protocol,mysql,oracle,postgresql,read-only,sql,sql-server,sqlite
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: System Administrators
18
+ Classifier: Operating System :: MacOS
19
+ Classifier: Operating System :: POSIX :: Linux
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Database
24
+ Classifier: Topic :: Database :: Front-Ends
25
+ Classifier: Topic :: Security
26
+ Requires-Python: >=3.12
27
+ Requires-Dist: cryptography>=50
28
+ Requires-Dist: h11<0.17,>=0.16
29
+ Requires-Dist: mcp<3,>=2.2.0
30
+ Requires-Dist: pyyaml<7,>=6.0.2
31
+ Requires-Dist: sqlglot~=30.18.0
32
+ Requires-Dist: uvicorn<0.54,>=0.52.4
33
+ Provides-Extra: all
34
+ Requires-Dist: clickhouse-connect<1.9,>=1.8; extra == 'all'
35
+ Requires-Dist: oracledb<5,>=4; extra == 'all'
36
+ Requires-Dist: psycopg[binary]<4,>=3.2; extra == 'all'
37
+ Requires-Dist: pymysql[ed25519,rsa]<2,>=1.2.0; extra == 'all'
38
+ Requires-Dist: pynacl>=1.6.2; extra == 'all'
39
+ Provides-Extra: clickhouse
40
+ Requires-Dist: clickhouse-connect<1.9,>=1.8; extra == 'clickhouse'
41
+ Provides-Extra: db2
42
+ Requires-Dist: ibm-db<4,>=3.2; extra == 'db2'
43
+ Provides-Extra: dev
44
+ Requires-Dist: build>=1.2; extra == 'dev'
45
+ Requires-Dist: mypy>=2.4.0; extra == 'dev'
46
+ Requires-Dist: pytest>=9; extra == 'dev'
47
+ Requires-Dist: ruff>=0.16; extra == 'dev'
48
+ Requires-Dist: types-pyyaml; extra == 'dev'
49
+ Provides-Extra: mssql
50
+ Requires-Dist: pyodbc<6,>=5.1; extra == 'mssql'
51
+ Provides-Extra: mysql
52
+ Requires-Dist: pymysql[ed25519,rsa]<2,>=1.2.0; extra == 'mysql'
53
+ Requires-Dist: pynacl>=1.6.2; extra == 'mysql'
54
+ Provides-Extra: oracle
55
+ Requires-Dist: oracledb<5,>=4; extra == 'oracle'
56
+ Provides-Extra: pg
57
+ Requires-Dist: psycopg[binary]<4,>=3.2; extra == 'pg'
58
+ Description-Content-Type: text/markdown
59
+
60
+ <h1 align="center">universal-db-mcp</h1>
61
+
62
+ <!-- mcp-name: io.github.alghanim/universal-db-mcp -->
63
+
64
+ <p align="center">
65
+ <a href="https://github.com/alghanim/universal-db-mcp/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/alghanim/universal-db-mcp/ci.yml?branch=main&label=CI"></a>
66
+ <a href="https://scorecard.dev/viewer/?uri=github.com/alghanim/universal-db-mcp"><img alt="OpenSSF Scorecard" src="https://api.scorecard.dev/projects/github.com/alghanim/universal-db-mcp/badge"></a>
67
+ <a href="https://m8ven.ai/mcp/alghanim-universal-db-mcp-s7tavw?s=readme"><img alt="M8ven Score" src="https://m8ven.ai/badge/mcp/alghanim-universal-db-mcp-s7tavw?v=5eb12f3910935759c86d7f0e5e0874ec"></a>
68
+ <a href="LICENSE"><img alt="license: Apache-2.0" src="https://img.shields.io/badge/license-Apache--2.0-4c1"></a>
69
+ <a href="pyproject.toml"><img alt="python: 3.12" src="https://img.shields.io/badge/python-3.12-3776ab"></a>
70
+ <a href="docs/tools.md"><img alt="MCP: 29 tools" src="https://img.shields.io/badge/MCP-29%20tools-6f42c1"></a>
71
+ <a href="#what-it-does"><img alt="databases: 8" src="https://img.shields.io/badge/databases-8-e67e22"></a>
72
+ <a href="#how-it-keeps-the-databases-safe"><img alt="access: read-only" src="https://img.shields.io/badge/access-read--only-0e8a7d"></a>
73
+ <a href="docs/claude-code-integration.md"><img alt="works with: Claude · Cursor · VS Code" src="https://img.shields.io/badge/works%20with-Claude%20%C2%B7%20Cursor%20%C2%B7%20VS%20Code-5b5bd6"></a>
74
+ </p>
75
+
76
+ <p align="center">
77
+ <a href="#quick-start">Quick start</a> ·
78
+ <a href="docs/tools.md">Tools</a> ·
79
+ <a href="docs/security.md">Security model</a> ·
80
+ <a href="#install-offline-air-gapped-sites">Offline install</a> ·
81
+ <a href="#documentation-map">Docs</a> ·
82
+ <a href="https://alghanim.github.io/universal-db-mcp/">Landing page</a> ·
83
+ <a href="SECURITY.md">Report a vulnerability</a>
84
+ </p>
85
+
86
+ **Give AI agents read-only access to your databases, safely.**
87
+ universal-db-mcp is an [MCP](https://modelcontextprotocol.io) server that lets
88
+ Claude Code, Cursor, VS Code or any MCP client discover, document, profile,
89
+ search and query the databases you declare, and nothing else. Every statement
90
+ is parsed before it runs and must be a single read; it runs on a read-only
91
+ session, columns that look sensitive are masked, and every call is audited.
92
+ Writes are refused, not discouraged. The server never calls an LLM, sends no
93
+ telemetry and installs nothing at runtime, so it also runs on networks with
94
+ no internet access at all.
95
+
96
+ **Status:** hardened through a production security review and three follow-up
97
+ code reviews (2026-09/10). `IMPLEMENTATION_STATUS.md` is the honest ledger of
98
+ what is implemented, what is verified and how, and what is still open; claims
99
+ here are limited to what its gates and `test-evidence/` demonstrate.
100
+
101
+ ## What it does
102
+
103
+ **Engines** (each version below passes the version matrix,
104
+ `scripts/version_matrix/`, results in `test-evidence/version-matrix/`):
105
+
106
+ | Engine | Verified versions |
107
+ | --- | --- |
108
+ | PostgreSQL | 12, 13, 14, 15, 16, 17 |
109
+ | MySQL / MariaDB | MySQL 5.7, 8.0, 8.4; MariaDB 10.6, 11.4 |
110
+ | ClickHouse | 23.8, 24.3, 24.8, 25.3 |
111
+ | Oracle | 18c, 21c, 23 (thin mode); 11g, 18c, 23 (thick mode, for legacy password verifiers) |
112
+ | SQL Server | 2017, 2019, 2022 |
113
+ | Db2 | 11.5.8, 11.5.9 |
114
+ | SQLite | the CPython build's SQLite |
115
+
116
+ **29 tools** (`docs/tools.md` is the contract):
117
+
118
+ - **Connections:** `db_list_connections`, `db_test_connection`, `db_get_capabilities`.
119
+ - **Catalog:** `db_list_catalogs`, `db_list_databases`, `db_list_schemas`,
120
+ `db_list_tables`, `db_get_table`, `db_list_columns`, `db_list_views`,
121
+ `db_list_synonyms`, `db_list_routines`, `db_list_indexes`,
122
+ `db_get_relationships`, `db_get_statistics`, `db_search_metadata`,
123
+ `db_get_catalog`.
124
+ - **Reading data:** `db_validate_query`, `db_query`, `db_sample_table`,
125
+ `db_explain` (plans, never executions), `db_get_query_history`.
126
+ - **Discovery, review and documentation:** `db_profile_table`,
127
+ `db_search_values` (find a value across tables and connections without
128
+ writing SQL), `db_infer_relationships`, `db_review_schema` (prioritized
129
+ optimization findings), `db_document_schema` (Markdown data dictionary).
130
+ - **Federated reads:** `db_federated_query` (one statement, or one per
131
+ connection, merged) and `db_federated_join` (a bounded hash join across two
132
+ connections).
133
+
134
+ ## How it keeps the databases safe
135
+
136
+ Defence in depth; details and limitations in `docs/security.md` and
137
+ `docs/session-safety.md`.
138
+
139
+ 1. **The database login is the primary control.** Give every connection a
140
+ SELECT-only login limited to the schemas you list in `allowed_schemas`.
141
+ The one exception is Db2's `db_explain`, which writes its plan rows into
142
+ the explain tables a DBA provisions, reads them back and deletes them:
143
+ that login also needs INSERT, SELECT and DELETE on those tables, and only
144
+ there (Oracle's plans go to the session-private `PLAN_TABLE`, which needs
145
+ no grant). Column-level grants or views are the real protection for
146
+ sensitive columns.
147
+ 2. **Read-only sessions.** On by default and enforced by the server itself on
148
+ PostgreSQL, MySQL/MariaDB, ClickHouse and SQLite; Db2 runs at `UR` and SQL
149
+ Server at `READ UNCOMMITTED`, so reads take no share locks. There is no
150
+ write mode: `security.read_only: false`, `allow_write_operations: true` and
151
+ a connection's `read_only: false` are rejected when the config loads. Every
152
+ session is also pinned to the settings that decide how its server lexes a
153
+ statement (MySQL `sql_mode`, PostgreSQL `standard_conforming_strings`, SQL
154
+ Server `QUOTED_IDENTIFIER`, ...), so the server reads exactly what the
155
+ guard parsed.
156
+ 3. **The SQL guard** parses every statement in the connection's dialect and
157
+ accepts one read statement only; anything it cannot parse is refused.
158
+ Under a non-empty `allowed_schemas`, every table must be schema-qualified
159
+ (`SELECT * FROM ocean.buoys`, not `SELECT * FROM buoys`), because the
160
+ engine would otherwise choose the schema. On Oracle, Db2 and PostgreSQL,
161
+ CTE names and unquoted table names must be ASCII. Views that show other
162
+ sessions' SQL, column statistics or stored credentials are refused whatever
163
+ `allowed_system_schemas` opens; Oracle's `SYS.DUAL` and Db2's
164
+ `SYSIBM.SYSDUMMY1` to `SYSDUMMY4` are always readable. Bound parameters
165
+ reach the driver exactly as validated.
166
+ 4. **Masking that refuses what it cannot prove.** Columns matching the
167
+ sensitive patterns (built in, plus `security.mask_columns`) are masked by
168
+ where each output value comes from. A statement over a table with masked
169
+ columns is accepted only in shapes the analysis fully proves (columns,
170
+ expressions, aggregates, window functions, joins, CTEs, subqueries,
171
+ unions, `*` over listed tables); anything else is refused before it runs,
172
+ with the construct named. Predicates are not masked: a `WHERE` on a masked
173
+ column can still narrow results.
174
+ 5. **Bounded, sanitized, audited.** Row, byte, cell and time ceilings; a
175
+ per-query memory cap on ClickHouse (2 GiB by default,
176
+ `options.max_memory_usage`); size caps checked before any parsing; driver
177
+ error text sanitized. Every call is audited, by default to the platform's
178
+ state directory (`/var/log/universal-db-mcp/audit.jsonl` for the service's
179
+ config, `%ProgramData%\UniversalDB MCP\logs\audit.jsonl` on Windows,
180
+ `~/.universal-db-mcp/audit.jsonl` for a per-user config).
181
+ 6. **stdio is not a security boundary.** A stdio registration runs the server
182
+ as your user inside the agent, so an agent that can run shell commands or
183
+ read files can read the credentials in `~/.universal-db-mcp/secrets/` and
184
+ connect directly, outside the guard. Use SELECT-only logins, or run the
185
+ server in HTTP mode (bearer token, behind your reverse proxy) under a
186
+ separate service account.
187
+
188
+ ## Quick start
189
+
190
+ ### From PyPI
191
+
192
+ ```bash
193
+ uv tool install 'udbmcp[all]' # or: pipx install 'udbmcp[all]'
194
+ udbmcp add-connection # writes ~/.universal-db-mcp/config.yaml, secrets in private files beside it
195
+ udbmcp configure-agents # registers it with Claude Code, Claude Desktop, Cursor, VS Code, Cline
196
+ ```
197
+
198
+ `[all]` brings the PostgreSQL, MySQL/MariaDB, ClickHouse and Oracle (thin
199
+ mode) drivers; SQLite needs none. SQL Server (`udbmcp[mssql]`) and Db2
200
+ (`udbmcp[db2]`) also need their vendors' client libraries. The wheel on PyPI
201
+ is the one the release's signed bundle carries, uploaded with PEP 740
202
+ attestations. Run `configure-agents` from an install like this one, not
203
+ through `uvx`: `uvx` runs from a cache that uv prunes, and `configure-agents`
204
+ refuses to register an interpreter there.
205
+
206
+ ### From source
207
+
208
+ ```bash
209
+ uv venv --python 3.12 .venv
210
+ uv pip install --python .venv/bin/python -e '.[dev]'
211
+ .venv/bin/python examples/sqlite-demo/create_demo.py # synthetic fixture
212
+ UDBMCP_CONFIG=examples/sqlite-demo/config.yaml .venv/bin/python -m universal_db_mcp serve --transport stdio
213
+ ```
214
+
215
+ Register the server with your agents with `.venv/bin/udbmcp configure-agents`
216
+ (Claude Code, Claude Desktop, Cursor, VS Code, Cline and others;
217
+ `docs/claude-code-integration.md`). It writes the launch command with
218
+ `python -I` (isolated mode), so the package and its dependencies must be
219
+ installed in a virtual environment, as above; `pip install --user` or
220
+ `PYTHONPATH`-only setups are refused. It registers the system config
221
+ (`/etc/universal-db-mcp/config.yaml`, on Windows
222
+ `%ProgramData%\UniversalDB MCP\config.yaml`) when your user can read it,
223
+ otherwise the per-user `~/.universal-db-mcp/config.yaml`; export an absolute
224
+ `UDBMCP_CONFIG` first to choose another. The `.deb` ships the system config
225
+ readable by every user, so on a `.deb` host make it `root:udbmcp` 0640 first
226
+ (`docs/claude-code-integration.md`).
227
+
228
+ `udbmcp add-connection` adds a connection interactively and stores its
229
+ secrets with private permissions; `udbmcp doctor` checks a config, its
230
+ secrets and (with `--connectivity`) every database.
231
+
232
+ ### With the container image
233
+
234
+ ```bash
235
+ docker pull ghcr.io/alghanim/universal-db-mcp:0.2.0
236
+ gh attestation verify oci://ghcr.io/alghanim/universal-db-mcp:0.2.0 --repo alghanim/universal-db-mcp
237
+ ```
238
+
239
+ It is listed in the official [MCP Registry](https://registry.modelcontextprotocol.io)
240
+ as `io.github.alghanim/universal-db-mcp`, so clients that browse the registry
241
+ can install it. Mount your config and its secrets read-only and run it over
242
+ stdio or HTTP as `docs/offline-deployment.md` (Container mode) describes. The image has no SQL
243
+ Server driver; the same section shows how to add Microsoft's.
244
+
245
+ ## A minimal connection
246
+
247
+ ```yaml
248
+ connections:
249
+ sales:
250
+ type: postgres
251
+ host: pg.example.internal
252
+ port: 5432
253
+ database: sales
254
+ username_file: /etc/universal-db-mcp/secrets/sales.user # 0600, owned by the service account
255
+ password_file: /etc/universal-db-mcp/secrets/sales.pw
256
+ allowed_schemas: [reporting]
257
+ tls:
258
+ enabled: true
259
+ ca_file: /etc/universal-db-mcp/ca/internal-ca.pem
260
+ ```
261
+
262
+ `config.example.yaml` documents every setting, per engine.
263
+
264
+ ## Install offline (air-gapped sites)
265
+
266
+ | Artifact | Platform | Status |
267
+ | --- | --- | --- |
268
+ | `.deb` | Ubuntu 24.04 x86-64 | release gate: install, upgrade, rollback and refusal cases with no network |
269
+ | `.pkg` | macOS arm64 | release gate passes; unsigned (no Developer ID yet) |
270
+ | `.msi` | Windows x64 | compiles with WiX and its custom actions are tested under PowerShell; never run on a Windows host yet |
271
+ | offline bundle | any of the above | signed with the release key; `scripts/install_offline.sh` |
272
+ | container image | linux/amd64, connected hosts | `ghcr.io/alghanim/universal-db-mcp`, built by CI from each release's signed bundle, with a provenance attestation (`docs/offline-deployment.md`) |
273
+
274
+ See `docs/offline-deployment.md`, and `docs/site-upgrade-runbook.md` for a
275
+ site that installs from a release stick (`scripts/package/release_usb.sh`
276
+ builds one). Short version:
277
+
278
+ ```bash
279
+ # staging machine (authorized for network): the bundle is signed with the release
280
+ # key (docs/offline-build.md; scripts/package/release_usb.sh builds a whole stick)
281
+ python scripts/prepare_offline_bundle.py --profile linux-x86_64-ubuntu24.04-cp312 --out out/bundle \
282
+ --signing-key udbmcp-release.pem
283
+
284
+ # target machine (offline), after the trust bootstrap in docs/offline-deployment.md,
285
+ # which installs the public key once its fingerprint matches one received out of band:
286
+ sudo UDBMCP_RELEASE_PUBKEY=/etc/universal-db-mcp/keys/release.pub.pem \
287
+ bash /usr/local/lib/udbmcp-trust/install_offline.sh out/bundle/universal-db-mcp-*/ /opt/universal-db-mcp
288
+ # the installer does not create the config: start from the bundle's template, readable by the service only
289
+ sudo install -m 640 -o root -g udbmcp out/bundle/universal-db-mcp-*/config-templates/config.yaml /etc/universal-db-mcp/config.yaml
290
+ sudo -u udbmcp /opt/universal-db-mcp/venv/bin/python -m universal_db_mcp doctor --config /etc/universal-db-mcp/config.yaml
291
+ ```
292
+
293
+ Nothing from a bundle or stick runs before its Ed25519 signature is checked
294
+ against the installed release key. The installers refuse a bundle whose
295
+ signed `release_seq` is older than the installed release unless you ask for
296
+ the downgrade: `--allow-downgrade` for the scripts, `UDBMCP_ALLOW_DOWNGRADE=1`
297
+ for the `.deb`, a one-shot flag file the `.pkg` names, and
298
+ `UDBMCP_ALLOW_DOWNGRADE=1` for one of this release's MSIs after uninstalling
299
+ the newer one (Windows Installer refuses any older MSI over a newer install).
300
+ In container mode, `scripts/load_images_offline.sh` keeps a root-owned release
301
+ record (`/var/lib/universal-db-mcp/release.json`) and refuses an older bundle
302
+ the same way.
303
+
304
+ ## Verification
305
+
306
+ - **Tests:** over 12,000 unit tests, run by GitHub Actions CI on
307
+ ubuntu-24.04 and locally on macOS; ruff and `mypy --strict` clean.
308
+ `IMPLEMENTATION_STATUS.md` (section 3f) records the latest counts.
309
+ - **Real databases:** the version matrix above, and
310
+ `scripts/live_evidence.py` against the mock databases of
311
+ `scripts/fixtures/start_mock_dbs.sh` (`docs/mock-environment.md`).
312
+ - **Packages:** the `.deb` and `.pkg` release gates and the offline upgrade
313
+ gate (`docs/acceptance-tests.md`).
314
+ - **Supply chain:** pinned, hash-checked locks for every platform, checked
315
+ with pip-audit.
316
+ - **Fuzzing:** the SQL guard is fuzzed with Atheris (`tests/fuzz/`) after each
317
+ push to `main` and weekly: any input it neither accepts nor refuses cleanly fails the run.
318
+
319
+ Not yet verified anywhere: the MSI on a real Windows host and a real macOS
320
+ Installer run of the `.pkg`.
321
+
322
+ ## Development
323
+
324
+ ```bash
325
+ .venv/bin/python -m pytest -o addopts='' -q tests # the whole suite
326
+ UDBMCP_LIVE_FIXTURES=1 UDBMCP_DOCKER_TESTS=1 \
327
+ .venv/bin/python -m pytest -o addopts='' -q tests # plus the live-database and container tests
328
+ .venv/bin/ruff check src tests scripts && .venv/bin/mypy --strict src
329
+ ```
330
+
331
+ Tests that use the local mock databases run only with
332
+ `UDBMCP_LIVE_FIXTURES=1`, so a package gate never waits on a paused or absent
333
+ database; the real-`dpkg` container sequences run only with
334
+ `UDBMCP_DOCKER_TESTS=1`. The bundle-builder tests need pip in the venv: after
335
+ `uv sync`, run
336
+ `uv pip install --python .venv/bin/python --require-hashes -r .github/ci-pip-requirements.txt`.
337
+
338
+ **CI.** `.github/workflows/ci.yml` is a GitHub Actions workflow (ubuntu-24.04,
339
+ CPython 3.12): the unit suite (including the MSI custom-action tests under
340
+ `pwsh`), ruff, `mypy --strict`, `prepare_offline_bundle.py --check-locks`, and
341
+ pip-audit over every lock. Actions are pinned to commit SHAs. It runs on
342
+ every pull request and every push to `main`, in two jobs at once: the slowest
343
+ installer and packaging test files run in `installer-tests`, and `checks`
344
+ runs the rest and every other check. `.github/workflows/audit.yml` runs the
345
+ same pip-audit every week, so a new advisory fails a run between commits too.
346
+ Dependabot proposes grouped updates monthly.
347
+ `tests/unit/test_hardening_2026_09_27_ci_hygiene.py` guards the CI setup
348
+ itself (pinned actions, pytest options, the two jobs together running every
349
+ test once, no skips or hooks hidden in conftests or helpers, no compiled or
350
+ symlinked files in the tree); it is a tripwire, not a sandbox, so changes to
351
+ conftests, test helpers and CI files still need a reviewer. After a
352
+ Dependabot bump of `requirements/runtime.in`, refresh and review the locks
353
+ (`docs/offline-build.md`).
354
+
355
+ ## Documentation map
356
+
357
+ | Document | Purpose |
358
+ | --- | --- |
359
+ | `docs/architecture.md` | components, data flow, process boundaries, execution limits |
360
+ | `docs/security.md` | threat model, policy, masking, audit, secrets, TLS |
361
+ | `docs/tools.md` | the MCP tool contract (29 `db_*` tools) |
362
+ | `docs/session-safety.md` | what every connection does to the server session so agent reads cannot hurt production |
363
+ | `docs/driver-matrix.md` | per-engine drivers, native dependencies, provisioning SQL, test status |
364
+ | `docs/oracle-connect-modes.md` | Oracle thick mode (legacy password verifiers), SID and TNS alias connections, TLS |
365
+ | `docs/db2-tls-setup.md` | Db2 TLS enablement runbook |
366
+ | `docs/claude-code-integration.md` | registering the server with Claude Code and other agents, internal gateway setup |
367
+ | `docs/offline-build.md` | Stage A: bundle preparation on the staging machine |
368
+ | `docs/offline-deployment.md` | Stage B: install inside the air gap (`.deb`, `.pkg`, `.msi`, bundle, containers) |
369
+ | `docs/offline-upgrade-rollback.md` | upgrade and rollback |
370
+ | `docs/site-upgrade-runbook.md` | step-by-step site upgrade, with this release's behaviour changes (shipped on the stick as `UPGRADE-README.md`) |
371
+ | `docs/acceptance-tests.md` | release gates and how to run them |
372
+ | `docs/mock-environment.md` | the local mock databases used for live evidence |
373
+ | `docs/adding-connectors.md` | how to add an engine adapter |
374
+ | `docs/troubleshooting.md` | common failures and `doctor` output |
375
+ | `IMPLEMENTATION_STATUS.md` | the implemented / verified / open ledger |
376
+ | `SECURITY.md` | how to report a vulnerability, supported versions, how fixes reach an air-gapped site |
377
+ | `site/index.html` | the product landing page, published by `.github/workflows/pages.yml` (`tests/unit/test_landing_page.py` re-runs every guard verdict it shows) |
378
+
379
+ ## Reporting a vulnerability
380
+
381
+ Please report security issues privately through GitHub private vulnerability
382
+ reporting, never in a public issue. `SECURITY.md` says what to include and
383
+ how fixes reach air-gapped sites. The project is maintained by the
384
+ universal-db-mcp maintainers and publishes no e-mail address: the one in the
385
+ `.deb`'s Maintainer field, which Debian requires, is a placeholder under the
386
+ reserved `.invalid` domain and reaches no one.
387
+
388
+ ## License
389
+
390
+ Apache-2.0. See `LICENSE` and `NOTICE`. Both ship in the wheel and in the
391
+ offline bundle's `licenses/` directory; the `.deb` states the license in its
392
+ DEP-5 `/usr/share/doc/universal-db-mcp/copyright` file, with `NOTICE` beside
393
+ it. Third-party components keep their own licenses: the bundle lists them in
394
+ `sbom/cyclonedx.json`, and each wheel in its `wheelhouse/` carries its license
395
+ files.
396
+
397
+ The Db2 connector's `ibm_db` wheel contains IBM Data Server Driver for ODBC
398
+ and CLI redistributables, which IBM licenses for distribution as part of an
399
+ application. They ship unmodified, with IBM's license and notice files,
400
+ which govern them (see `NOTICE`). Db2 and IBM are trademarks of International
401
+ Business Machines Corporation, used here only to name the database and its
402
+ driver. Microsoft's ODBC Driver for SQL Server and Oracle's Instant Client are
403
+ not redistributed: sites install them from the vendor.
@@ -0,0 +1,55 @@
1
+ universal_db_mcp/__init__.py,sha256=CE2m_fw2AzPSBTq0-db_NdIEPhllt2DppIRTInCLaPk,107
2
+ universal_db_mcp/__main__.py,sha256=1UJe9XFQcfWMdRTE12kinD7cd4a8k5POAOLcfDfQhvI,49908
3
+ universal_db_mcp/config.py,sha256=lM2G1otjrRd1duw1SZpkzvca3DanlYFR7PBFPSNOrNs,79037
4
+ universal_db_mcp/errors.py,sha256=cG5nyeVqAL-TYJpzT1dDF21PIKvUr_qIbza4Zx9GCG0,884
5
+ universal_db_mcp/http_protocol.py,sha256=QGoX6QL7XYQlw-FbjT7KCnjtmXRg3Qmawo9U1k6bMGc,25574
6
+ universal_db_mcp/server.py,sha256=kuOCvdW_ahEbSisTekBaCKNo4E3LTLoq-cKPhZBxGRQ,302812
7
+ universal_db_mcp/wizard.py,sha256=RZCdniH3oW0foR2rGSvPk0X5bqlSY-_FJ722W3cWa5E,71685
8
+ universal_db_mcp/agents/__init__.py,sha256=sxLXTWg3dbSeu57TRXxSyaPee4G5Ge_3u-jJUgjPsp4,85
9
+ universal_db_mcp/agents/claude_code.py,sha256=LNqXC_Wynj9wmWL4XkGUhMh3ocuMbP2IrjNoez8mB_0,22924
10
+ universal_db_mcp/agents/claude_desktop.py,sha256=s0uDLnrYiWZbd1b5NSDHwdd0Yc4FquBkrx1aDamoCBA,19594
11
+ universal_db_mcp/agents/cline.py,sha256=HnCAmu4DsI3ylc_Pv2fJT42YBz_Js3eicxfbiahbjHw,15942
12
+ universal_db_mcp/agents/core.py,sha256=Dr-1oNcoipEenFth0l4DunjAi3CIafgNaUXt7N8_HS4,77995
13
+ universal_db_mcp/agents/cursor.py,sha256=7GoS0gPduD9quFl5jSpJn7O6yswElbDxbH-05D1x-9s,16247
14
+ universal_db_mcp/agents/dsh.py,sha256=lsnGD4k_FKoR53O20fXnm0Kt3wt1a7sh7zGzNPM9A38,32917
15
+ universal_db_mcp/agents/registry.py,sha256=4v_wYEl3Sf2I3Dvxm0EG7a75hGITElreSYhRuk35Rhk,4812
16
+ universal_db_mcp/agents/vscode.py,sha256=KkcMG5NrScWGas7bLwiiY9EodMmvwe-lE1-zcNUtOnI,24593
17
+ universal_db_mcp/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ universal_db_mcp/connectors/base.py,sha256=atw8kckpF1pOu8LuRmfFhV6T_-kCsb72nz2ltfZy984,21273
19
+ universal_db_mcp/connectors/clickhouse.py,sha256=R6LD9bkPQMzfqE-1EPqq4wF46RJiCqHnHQdEf13rLDc,75655
20
+ universal_db_mcp/connectors/db2.py,sha256=jHF6wToCnYp_ylKIpVdKB8keBodNx3LeRN4KhMQbBBU,56559
21
+ universal_db_mcp/connectors/driver_helpers.py,sha256=W2xUErzPs39ZQBMUqVAMi2ZllZ-ZBjPqa8eiTQ6nQYo,31208
22
+ universal_db_mcp/connectors/mssql.py,sha256=dT8QI0dz6WDsChkT6D04BpCJ7-V8ybv0AMjNFYF0LFk,55416
23
+ universal_db_mcp/connectors/mysql.py,sha256=iyvFHgvK3uYJckBEx9oFaggZwDmahLMgokSn-fbbVg0,58282
24
+ universal_db_mcp/connectors/oracle.py,sha256=xOQg3x_QAi_-_jqaHZmzLUh4PGnFxaSa8D9ZydE-fc4,63808
25
+ universal_db_mcp/connectors/postgres.py,sha256=DGuSO866HMFaaQtlMZpn2gBQsc4De5ko4LSKCt2wLm0,53086
26
+ universal_db_mcp/connectors/registry.py,sha256=fjzxZSU40zCrxz2WIJlFv6QUDG1Rw-ljTalHFDkAldE,1760
27
+ universal_db_mcp/connectors/sqlite.py,sha256=BA1zlZbdZ_nCHnkDOve0FnaBLzK_UHZNGBvYBA8PrlU,60489
28
+ universal_db_mcp/diagnostics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ universal_db_mcp/diagnostics/doctor.py,sha256=5je0YH2V_K7PKpkdBwGgzyu6Na77kBTtLxNSuJzpZpg,61873
30
+ universal_db_mcp/diagnostics/site_check.py,sha256=5xDj_Twp1zzbmWP4iLRCAXeYz0-lketjxr1a0Njr1Ls,11486
31
+ universal_db_mcp/discovery/__init__.py,sha256=A9mKPuuRd7YQ_ZTq1wnOSb73ut7AYprrMvPrihZrI90,85
32
+ universal_db_mcp/discovery/document.py,sha256=ZYvhH3gM_0F4RSP3pNqGxpclUXtE-EQM6TiHsZO-I8k,6368
33
+ universal_db_mcp/discovery/inference.py,sha256=PYgbUDLFPwH2ue-jxJTKoozDDmfSCoWdfyXyXBjyC2g,6787
34
+ universal_db_mcp/discovery/profile.py,sha256=kUEwMMtk7uxmq7yaQB0RMh77A_OGSUCu0VFYqnIBig8,11492
35
+ universal_db_mcp/discovery/system_schemas.py,sha256=z2ArU2ikq299-f4t76kO-1Ap8gvpAJ8VeWphiYGttSY,35121
36
+ universal_db_mcp/discovery/types.py,sha256=L664QpGEGooq-ESOAyodE2KxkNKOMfv0Igz-o7x6kcg,9371
37
+ universal_db_mcp/models/__init__.py,sha256=VRjlzQ12zmWSwSNvdugR07yYhaX0kPU4ENFDQnpK090,320
38
+ universal_db_mcp/models/capabilities.py,sha256=3jQ7Wctg4kiKKblLZssREhLW2D_cPVhXm7XgdWaI7-4,2693
39
+ universal_db_mcp/models/responses.py,sha256=6wSVi4VkKBzPwhMMeu0uIXS0Fx3x88ZS76UA-l3gfzk,2018
40
+ universal_db_mcp/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
+ universal_db_mcp/security/cursors.py,sha256=QeX8D1BrVv-UtwlAMsxDaXUNQMOeTjG451DyELmTdog,5602
42
+ universal_db_mcp/security/policy.py,sha256=4C8yKw-5rNHmhHjcePxIwkRlXYNBn-kGMVhrV5JooNQ,14834
43
+ universal_db_mcp/security/redact.py,sha256=nXZ5eHVkUkPweiVmPMfak4j1waBgWIIe9SLhTLs1K90,8571
44
+ universal_db_mcp/security/session.py,sha256=c2v3LtqOAI9getaBdJ71VGcCRZIhxK8VzYkPzKOMbgs,3281
45
+ universal_db_mcp/security/sql_guard.py,sha256=Q2g7240AqZD_5-pTutsIuUsZoIfdNEtOd4haNNpxW7g,140114
46
+ universal_db_mcp/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ universal_db_mcp/services/audit.py,sha256=9UoD5xE2D31rU9cr3afj9g3OK12UsXiVsW39GIC_P5Q,40425
48
+ universal_db_mcp/services/executor.py,sha256=7nWLfMDDvoF8w4G47S2WcGWXXj6CDCtZ-tUm53nnsDQ,51824
49
+ universal_db_mcp/services/metadata.py,sha256=KnnU1VkhOn7dn1FhVxqocwmrDrWTIFGnZVgB44oBVBI,20317
50
+ udbmcp-0.2.0.dist-info/METADATA,sha256=ioLIDpvaV1iXHUr41INNDXpe5fbk8XtX7--cpLlobMI,22107
51
+ udbmcp-0.2.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
52
+ udbmcp-0.2.0.dist-info/entry_points.txt,sha256=roBlxBOXmk2bEDKWA-NF1o2tq8o8jjhz-ng-za_hKDo,58
53
+ udbmcp-0.2.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
54
+ udbmcp-0.2.0.dist-info/licenses/NOTICE,sha256=ebx4RqRunFmBr0-MF6uAniAgp6GZeipMgI9HKvRlDIg,725
55
+ udbmcp-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ udbmcp = universal_db_mcp.__main__:main