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.
- udbmcp-0.2.0.dist-info/METADATA +403 -0
- udbmcp-0.2.0.dist-info/RECORD +55 -0
- udbmcp-0.2.0.dist-info/WHEEL +4 -0
- udbmcp-0.2.0.dist-info/entry_points.txt +2 -0
- udbmcp-0.2.0.dist-info/licenses/LICENSE +202 -0
- udbmcp-0.2.0.dist-info/licenses/NOTICE +14 -0
- universal_db_mcp/__init__.py +3 -0
- universal_db_mcp/__main__.py +1136 -0
- universal_db_mcp/agents/__init__.py +1 -0
- universal_db_mcp/agents/claude_code.py +536 -0
- universal_db_mcp/agents/claude_desktop.py +465 -0
- universal_db_mcp/agents/cline.py +378 -0
- universal_db_mcp/agents/core.py +1710 -0
- universal_db_mcp/agents/cursor.py +394 -0
- universal_db_mcp/agents/dsh.py +769 -0
- universal_db_mcp/agents/registry.py +128 -0
- universal_db_mcp/agents/vscode.py +578 -0
- universal_db_mcp/config.py +1646 -0
- universal_db_mcp/connectors/__init__.py +0 -0
- universal_db_mcp/connectors/base.py +503 -0
- universal_db_mcp/connectors/clickhouse.py +1540 -0
- universal_db_mcp/connectors/db2.py +1166 -0
- universal_db_mcp/connectors/driver_helpers.py +691 -0
- universal_db_mcp/connectors/mssql.py +1148 -0
- universal_db_mcp/connectors/mysql.py +1207 -0
- universal_db_mcp/connectors/oracle.py +1297 -0
- universal_db_mcp/connectors/postgres.py +1116 -0
- universal_db_mcp/connectors/registry.py +44 -0
- universal_db_mcp/connectors/sqlite.py +1259 -0
- universal_db_mcp/diagnostics/__init__.py +0 -0
- universal_db_mcp/diagnostics/doctor.py +1233 -0
- universal_db_mcp/diagnostics/site_check.py +272 -0
- universal_db_mcp/discovery/__init__.py +1 -0
- universal_db_mcp/discovery/document.py +157 -0
- universal_db_mcp/discovery/inference.py +156 -0
- universal_db_mcp/discovery/profile.py +263 -0
- universal_db_mcp/discovery/system_schemas.py +602 -0
- universal_db_mcp/discovery/types.py +201 -0
- universal_db_mcp/errors.py +24 -0
- universal_db_mcp/http_protocol.py +599 -0
- universal_db_mcp/models/__init__.py +12 -0
- universal_db_mcp/models/capabilities.py +79 -0
- universal_db_mcp/models/responses.py +62 -0
- universal_db_mcp/security/__init__.py +0 -0
- universal_db_mcp/security/cursors.py +137 -0
- universal_db_mcp/security/policy.py +299 -0
- universal_db_mcp/security/redact.py +202 -0
- universal_db_mcp/security/session.py +87 -0
- universal_db_mcp/security/sql_guard.py +2627 -0
- universal_db_mcp/server.py +6201 -0
- universal_db_mcp/services/__init__.py +0 -0
- universal_db_mcp/services/audit.py +911 -0
- universal_db_mcp/services/executor.py +979 -0
- universal_db_mcp/services/metadata.py +465 -0
- 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,,
|