Universal DB (read-only)

io.github.alghanimv0.1.0更新於 Oct 9, 2026

Read-only, guarded SQL access for AI agents: PostgreSQL, MySQL, ClickHouse, Oracle, SQL Server, Db2

已驗證STDIO僅桌面DatabasesData & Analytics

概覽

AI 產生的概覽

讓 AI 助理以受保護的唯讀方式存取 PostgreSQL、MySQL、ClickHouse、Oracle、SQL Server、Db2 與 SQLite 資料庫。

功能
提供 29 個 db_* 工具,用來列出連線、目錄、結構描述、資料表、欄位、檢視表、索引與關聯,並驗證、執行與解讀唯讀查詢。也能對資料表做剖析、搜尋值與中繼資料、推斷關聯、檢閱結構描述並產生 Markdown 資料字典。聯邦工具可跨連線執行單一語句,或在兩個連線間做有界雜湊聯結。每個語句都會先被解析,且必須是單一讀取;寫入會被拒絕。
適用情境
當助理需要探索、記錄或查詢既有關聯式資料庫,且完全不能有寫入風險時使用。適合跨多種引擎的結構描述探索、資料剖析與唯讀報表,包括隔離網路環境。
執行需求
本機 Python 3.12 執行環境(虛擬環境;不接受 pip install --user 或僅靠 PYTHONPATH 的方式)或容器映像。需要宣告連線的 YAML 設定,憑證透過 username_file、password_file 等檔案提供,並以絕對路徑 UDBMCP_CONFIG 指定設定位置。需要資料庫驅動程式與到各資料庫的網路存取;容器映像不含 SQL Server 驅動程式。
安裝前請注意
資料庫登入帳號是首要控制手段:應使用僅限 SELECT 且限定 allowed_schemas 的帳號,因為欄位層級授權或檢視表才是敏感欄位真正的保護。stdio 不是安全邊界,能執行 shell 命令或讀取檔案的代理可直接讀取 ~/.universal-db-mcp/secrets/ 中的憑證並繞過防護連線;可考慮在獨立服務帳號下使用 HTTP 模式。對已遮罩欄位的述詞不會被遮罩,WHERE 子句仍可縮小結果範圍。Db2 的 db_explain 需要對 explain 資料表有 INSERT、SELECT 與 DELETE 權限。每次呼叫都會寫入本機稽核檔。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Universal DB (read-only),將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

universal-db-mcp

[CI] [OpenSSF Scorecard] [license: Apache-2.0] [python: 3.12] [MCP: 29 tools] [databases: 8] [access: read-only] [works with: Claude · Cursor · VS Code]

Quick start · Tools · Security model · Offline install · Docs · Landing page · Report a vulnerability

Give AI agents read-only access to your databases, safely. universal-db-mcp is an MCP server that lets Claude Code, Cursor, VS Code or any MCP client discover, document, profile, search and query the databases you declare, and nothing else. Every statement is parsed before it runs and must be a single read; it runs on a read-only session, columns that look sensitive are masked, and every call is audited. Writes are refused, not discouraged. The server never calls an LLM, sends no telemetry and installs nothing at runtime, so it also runs on networks with no internet access at all.

Status: hardened through a production security review and three follow-up code reviews (2026-09/10). IMPLEMENTATION_STATUS.md is the honest ledger of what is implemented, what is verified and how, and what is still open; claims here are limited to what its gates and test-evidence/ demonstrate.

What it does

Engines (each version below passes the version matrix, scripts/version_matrix/, results in test-evidence/version-matrix/):

EngineVerified versions
PostgreSQL12, 13, 14, 15, 16, 17
MySQL / MariaDBMySQL 5.7, 8.0, 8.4; MariaDB 10.6, 11.4
ClickHouse23.8, 24.3, 24.8, 25.3
Oracle18c, 21c, 23 (thin mode); 11g, 18c, 23 (thick mode, for legacy password verifiers)
SQL Server2017, 2019, 2022
Db211.5.8, 11.5.9
SQLitethe CPython build's SQLite

29 tools (docs/tools.md is the contract):

  • Connections: db_list_connections, db_test_connection, db_get_capabilities.
  • Catalog: db_list_catalogs, db_list_databases, db_list_schemas, db_list_tables, db_get_table, db_list_columns, db_list_views, db_list_synonyms, db_list_routines, db_list_indexes, db_get_relationships, db_get_statistics, db_search_metadata, db_get_catalog.
  • Reading data: db_validate_query, db_query, db_sample_table, db_explain (plans, never executions), db_get_query_history.
  • Discovery, review and documentation: db_profile_table, db_search_values (find a value across tables and connections without writing SQL), db_infer_relationships, db_review_schema (prioritized optimization findings), db_document_schema (Markdown data dictionary).
  • Federated reads: db_federated_query (one statement, or one per connection, merged) and db_federated_join (a bounded hash join across two connections).

How it keeps the databases safe

Defence in depth; details and limitations in docs/security.md and docs/session-safety.md.

  1. The database login is the primary control. Give every connection a SELECT-only login limited to the schemas you list in allowed_schemas. The one exception is Db2's db_explain, which writes its plan rows into the explain tables a DBA provisions, reads them back and deletes them: that login also needs INSERT, SELECT and DELETE on those tables, and only there (Oracle's plans go to the session-private PLAN_TABLE, which needs no grant). Column-level grants or views are the real protection for sensitive columns.
  2. Read-only sessions. On by default and enforced by the server itself on PostgreSQL, MySQL/MariaDB, ClickHouse and SQLite; Db2 runs at UR and SQL Server at READ UNCOMMITTED, so reads take no share locks. There is no write mode: security.read_only: false, allow_write_operations: true and a connection's read_only: false are rejected when the config loads. Every session is also pinned to the settings that decide how its server lexes a statement (MySQL sql_mode, PostgreSQL standard_conforming_strings, SQL Server QUOTED_IDENTIFIER, ...), so the server reads exactly what the guard parsed.
  3. The SQL guard parses every statement in the connection's dialect and accepts one read statement only; anything it cannot parse is refused. Under a non-empty allowed_schemas, every table must be schema-qualified (SELECT * FROM ocean.buoys, not SELECT * FROM buoys), because the engine would otherwise choose the schema. On Oracle, Db2 and PostgreSQL, CTE names and unquoted table names must be ASCII. Views that show other sessions' SQL, column statistics or stored credentials are refused whatever allowed_system_schemas opens; Oracle's SYS.DUAL and Db2's SYSIBM.SYSDUMMY1 to SYSDUMMY4 are always readable. Bound parameters reach the driver exactly as validated.
  4. Masking that refuses what it cannot prove. Columns matching the sensitive patterns (built in, plus security.mask_columns) are masked by where each output value comes from. A statement over a table with masked columns is accepted only in shapes the analysis fully proves (columns, expressions, aggregates, window functions, joins, CTEs, subqueries, unions, * over listed tables); anything else is refused before it runs, with the construct named. Predicates are not masked: a WHERE on a masked column can still narrow results.
  5. Bounded, sanitized, audited. Row, byte, cell and time ceilings; a per-query memory cap on ClickHouse (2 GiB by default, options.max_memory_usage); size caps checked before any parsing; driver error text sanitized. Every call is audited, by default to the platform's state directory (/var/log/universal-db-mcp/audit.jsonl for the service's config, %ProgramData%\UniversalDB MCP\logs\audit.jsonl on Windows, ~/.universal-db-mcp/audit.jsonl for a per-user config).
  6. stdio is not a security boundary. A stdio registration runs the server as your user inside the agent, so an agent that can run shell commands or read files can read the credentials in ~/.universal-db-mcp/secrets/ and connect directly, outside the guard. Use SELECT-only logins, or run the server in HTTP mode (bearer token, behind your reverse proxy) under a separate service account.

Quick start

From source

bash
uv venv --python 3.12 .venvuv pip install --python .venv/bin/python -e '.[dev]'.venv/bin/python examples/sqlite-demo/create_demo.py   # synthetic fixtureUDBMCP_CONFIG=examples/sqlite-demo/config.yaml .venv/bin/python -m universal_db_mcp serve --transport stdio

Register the server with your agents with .venv/bin/udbmcp configure-agents (Claude Code, Claude Desktop, Cursor, VS Code, Cline and others; docs/claude-code-integration.md). It writes the launch command with python -I (isolated mode), so the package and its dependencies must be installed in a virtual environment, as above; pip install --user or PYTHONPATH-only setups are refused. It registers the system config (/etc/universal-db-mcp/config.yaml, on Windows %ProgramData%\UniversalDB MCP\config.yaml) when your user can read it, otherwise the per-user ~/.universal-db-mcp/config.yaml; export an absolute UDBMCP_CONFIG first to choose another. The .deb ships the system config readable by every user, so on a .deb host make it root:udbmcp 0640 first (docs/claude-code-integration.md).

udbmcp add-connection adds a connection interactively and stores its secrets with private permissions; udbmcp doctor checks a config, its secrets and (with --connectivity) every database.

With the container image

bash
docker pull ghcr.io/alghanim/universal-db-mcp:0.1.0gh attestation verify oci://ghcr.io/alghanim/universal-db-mcp:0.1.0 --repo alghanim/universal-db-mcp

It is listed in the official MCP Registry as io.github.alghanim/universal-db-mcp, so clients that browse the registry can install it. Mount your config and its secrets read-only and run it over stdio or HTTP as docs/offline-deployment.md (Container mode) describes. The image has no SQL Server driver; the same section shows how to add Microsoft's.

A minimal connection

yaml
connections:  sales:    type: postgres    host: pg.example.internal    port: 5432    database: sales    username_file: /etc/universal-db-mcp/secrets/sales.user   # 0600, owned by the service account    password_file: /etc/universal-db-mcp/secrets/sales.pw    allowed_schemas: [reporting]    tls:      enabled: true      ca_file: /etc/universal-db-mcp/ca/internal-ca.pem

config.example.yaml documents every setting, per engine.

Install offline (air-gapped sites)

ArtifactPlatformStatus
.debUbuntu 24.04 x86-64release gate: install, upgrade, rollback and refusal cases with no network
.pkgmacOS arm64release gate passes; unsigned (no Developer ID yet)
.msiWindows x64compiles with WiX and its custom actions are tested under PowerShell; never run on a Windows host yet
offline bundleany of the abovesigned with the release key; scripts/install_offline.sh
container imagelinux/amd64, connected hostsghcr.io/alghanim/universal-db-mcp, built by CI from each release's signed bundle, with a provenance attestation (docs/offline-deployment.md)

See docs/offline-deployment.md, and docs/site-upgrade-runbook.md for a site that installs from a release stick (scripts/package/release_usb.sh builds one). Short version:

bash
# staging machine (authorized for network): the bundle is signed with the release# key (docs/offline-build.md; scripts/package/release_usb.sh builds a whole stick)python scripts/prepare_offline_bundle.py --profile linux-x86_64-ubuntu24.04-cp312 --out out/bundle \  --signing-key udbmcp-release.pem
# target machine (offline), after the trust bootstrap in docs/offline-deployment.md,# which installs the public key once its fingerprint matches one received out of band:sudo UDBMCP_RELEASE_PUBKEY=/etc/universal-db-mcp/keys/release.pub.pem \  bash /usr/local/lib/udbmcp-trust/install_offline.sh out/bundle/universal-db-mcp-*/ /opt/universal-db-mcp# the installer does not create the config: start from the bundle's template, readable by the service onlysudo install -m 640 -o root -g udbmcp out/bundle/universal-db-mcp-*/config-templates/config.yaml /etc/universal-db-mcp/config.yamlsudo -u udbmcp /opt/universal-db-mcp/venv/bin/python -m universal_db_mcp doctor --config /etc/universal-db-mcp/config.yaml

Nothing from a bundle or stick runs before its Ed25519 signature is checked against the installed release key. The installers refuse a bundle whose signed release_seq is older than the installed release unless you ask for the downgrade: --allow-downgrade for the scripts, UDBMCP_ALLOW_DOWNGRADE=1 for the .deb, a one-shot flag file the .pkg names, and UDBMCP_ALLOW_DOWNGRADE=1 for one of this release's MSIs after uninstalling the newer one (Windows Installer refuses any older MSI over a newer install). In container mode, scripts/load_images_offline.sh keeps a root-owned release record (/var/lib/universal-db-mcp/release.json) and refuses an older bundle the same way.

Verification

  • Tests: over 12,000 unit tests, run by GitHub Actions CI on ubuntu-24.04 and locally on macOS; ruff and mypy --strict clean. IMPLEMENTATION_STATUS.md (section 3f) records the latest counts.
  • Real databases: the version matrix above, and scripts/live_evidence.py against the mock databases of scripts/fixtures/start_mock_dbs.sh (docs/mock-environment.md).
  • Packages: the .deb and .pkg release gates and the offline upgrade gate (docs/acceptance-tests.md).
  • Supply chain: pinned, hash-checked locks for every platform, checked with pip-audit.
  • Fuzzing: the SQL guard is fuzzed with Atheris (tests/fuzz/) after each push to main and weekly: any input it neither accepts nor refuses cleanly fails the run.

Not yet verified anywhere: the MSI on a real Windows host and a real macOS Installer run of the .pkg.

Development

bash
.venv/bin/python -m pytest -o addopts='' -q tests                 # the whole suiteUDBMCP_LIVE_FIXTURES=1 UDBMCP_DOCKER_TESTS=1 \  .venv/bin/python -m pytest -o addopts='' -q tests               # plus the live-database and container tests.venv/bin/ruff check src tests scripts && .venv/bin/mypy --strict src

Tests that use the local mock databases run only with UDBMCP_LIVE_FIXTURES=1, so a package gate never waits on a paused or absent database; the real-dpkg container sequences run only with UDBMCP_DOCKER_TESTS=1. The bundle-builder tests need pip in the venv: after uv sync, run uv pip install --python .venv/bin/python --require-hashes -r .github/ci-pip-requirements.txt.

CI. .github/workflows/ci.yml is a GitHub Actions workflow (ubuntu-24.04, CPython 3.12): the unit suite (including the MSI custom-action tests under pwsh), ruff, mypy --strict, prepare_offline_bundle.py --check-locks, and pip-audit over every lock. Actions are pinned to commit SHAs. It runs on every pull request and every push to main, in two jobs at once: the slowest installer and packaging test files run in installer-tests, and checks runs the rest and every other check. .github/workflows/audit.yml runs the same pip-audit every week, so a new advisory fails a run between commits too. Dependabot proposes grouped updates monthly. tests/unit/test_hardening_2026_09_27_ci_hygiene.py guards the CI setup itself (pinned actions, pytest options, the two jobs together running every test once, no skips or hooks hidden in conftests or helpers, no compiled or symlinked files in the tree); it is a tripwire, not a sandbox, so changes to conftests, test helpers and CI files still need a reviewer. After a Dependabot bump of requirements/runtime.in, refresh and review the locks (docs/offline-build.md).

Documentation map

DocumentPurpose
docs/architecture.mdcomponents, data flow, process boundaries, execution limits
docs/security.mdthreat model, policy, masking, audit, secrets, TLS
docs/tools.mdthe MCP tool contract (29 db_* tools)
docs/session-safety.mdwhat every connection does to the server session so agent reads cannot hurt production
docs/driver-matrix.mdper-engine drivers, native dependencies, provisioning SQL, test status
docs/oracle-connect-modes.mdOracle thick mode (legacy password verifiers), SID and TNS alias connections, TLS
docs/db2-tls-setup.mdDb2 TLS enablement runbook
docs/claude-code-integration.mdregistering the server with Claude Code and other agents, internal gateway setup
docs/offline-build.mdStage A: bundle preparation on the staging machine
docs/offline-deployment.mdStage B: install inside the air gap (.deb, .pkg, .msi, bundle, containers)
docs/offline-upgrade-rollback.mdupgrade and rollback
docs/site-upgrade-runbook.mdstep-by-step site upgrade, with this release's behaviour changes (shipped on the stick as UPGRADE-README.md)
docs/acceptance-tests.mdrelease gates and how to run them
docs/mock-environment.mdthe local mock databases used for live evidence
docs/adding-connectors.mdhow to add an engine adapter
docs/troubleshooting.mdcommon failures and doctor output
IMPLEMENTATION_STATUS.mdthe implemented / verified / open ledger
SECURITY.mdhow to report a vulnerability, supported versions, how fixes reach an air-gapped site
site/index.htmlthe product landing page, published by .github/workflows/pages.yml (tests/unit/test_landing_page.py re-runs every guard verdict it shows)

Reporting a vulnerability

Please report security issues privately through GitHub private vulnerability reporting, never in a public issue. SECURITY.md says what to include and how fixes reach air-gapped sites. The project is maintained by the universal-db-mcp maintainers and publishes no e-mail address: the one in the .deb's Maintainer field, which Debian requires, is a placeholder under the reserved .invalid domain and reaches no one.

License

Apache-2.0. See LICENSE and NOTICE. Both ship in the wheel and in the offline bundle's licenses/ directory; the .deb states the license in its DEP-5 /usr/share/doc/universal-db-mcp/copyright file, with NOTICE beside it. Third-party components keep their own licenses: the bundle lists them in sbom/cyclonedx.json, and each wheel in its wheelhouse/ carries its license files.

The Db2 connector's ibm_db wheel contains IBM Data Server Driver for ODBC and CLI redistributables, which IBM licenses for distribution as part of an application. They ship unmodified, with IBM's license and notice files, which govern them (see NOTICE). Db2 and IBM are trademarks of International Business Machines Corporation, used here only to name the database and its driver. Microsoft's ODBC Driver for SQL Server and Oracle's Instant Client are not redistributed: sites install them from the vendor.

來源:README.md,提交 ae0989b

工具

0
工具後設資料尚未被收錄。

版本歷史

1
  1. v0.1.0最新Oct 9, 2026