dbeaver-mcp
by bnuprst
README.md
# dbeaver-mcp
An MCP server that lets AI assistants (Claude Code, Claude Desktop, GitHub Copilot in VS Code, Cursor...) explore your databases **through the connections you already have in DBeaver**. No second place to keep hosts and passwords.
Supported engines: **PostgreSQL**, **MySQL**, **MariaDB**, **SQLite**, **SQL Server** and **Oracle**. Other DBeaver connections are listed but marked as unavailable.
**Read-only by default, enforced by the database itself wherever the engine can do it:**
| Engine | How writes are refused |
|---|---|
| PostgreSQL | Every query runs in `BEGIN TRANSACTION READ ONLY` and is always rolled back |
| MySQL, MariaDB | Read-only session and `START TRANSACTION READ ONLY`, always rolled back. The session setting also refuses DDL, which would otherwise commit implicitly. |
| SQLite | The file is opened read-only, and an authorizer inside SQLite denies `ATTACH`, `VACUUM INTO`, writable pragmas and file functions |
| Oracle | `SET TRANSACTION READ ONLY`, always rolled back. Oracle refuses DML there; DDL would commit implicitly, so the SQL check keeps it out (and blocks autonomous transactions). |
| SQL Server | **No read-only transaction exists.** Queries run in a transaction that is always rolled back, and the SQL check refuses everything but one `SELECT`. `list_connections` and `--check` warn about it; use a login that can only read, or set `DBEAVER_MCP_REQUIRE_DB_READONLY=true` to refuse these connections. |
Works with DBeaver Community Edition. DBeaver does not need to be running.
> Formerly published as `dbeaver-postgres-mcp`. The `dbeaver-postgres-mcp` command still works as an alias of `dbeaver-mcp`.
## What the assistant can do
| Tool | What it does |
|---|---|
| `list_connections` | Your DBeaver connections: name, engine, placeholder style, type (dev/test/prod), host, database, user, and how read-only is enforced. Never passwords. |
| `list_schemas` | Schemas with table counts and comments (MySQL: databases; SQLite: `main`; Oracle: users that own tables) |
| `list_tables` | Tables and views with estimated row counts and comments. PostgreSQL partitions are folded into their parent. Filter by name or comment. |
| `describe_table` | Columns (type, nullable, default, comment), primary/unique/foreign/check constraints, tables that reference it, indexes, size, view definition |
| `find_columns` | Which tables have a column like `policy_id` |
| `run_query` | One read statement (`SELECT`, `WITH`, `VALUES`, plus `SHOW`/`TABLE` on PostgreSQL and `SHOW`/`DESCRIBE`/`TABLE` on MySQL), with parameters, row limit and shortened long values. Placeholders follow the engine: `$1` on PostgreSQL, `?` on MySQL, MariaDB and SQLite, `@p1` on SQL Server, `:1` on Oracle. |
| `explain_query` | Query plan; `analyze: true` runs it (still read-only) for real timings. SQLite has no timings and shows the plan only. SQL Server uses `SHOWPLAN_TEXT` / `STATISTICS PROFILE`; Oracle uses `EXPLAIN PLAN` (rolled back) and, for `analyze`, `DBMS_XPLAN.DISPLAY_CURSOR`, which needs `SELECT_CATALOG_ROLE`. |
| `execute_sql` | **Only when `DBEAVER_MCP_ALLOW_WRITE=true`**, and never on prod or read-only connections |
## Install and connect
Requires Node.js 20+. SQLite connections need Node.js 22.13+ (they use Node's built-in `node:sqlite`, so nothing native is installed). SQL Server uses `tedious` and Oracle uses `oracledb` in Thin mode, both pure JavaScript; no Oracle Instant Client is needed. `oracledb` is an optional dependency: if it is missing, Oracle connections are listed as unavailable.
Check first what it sees and whether it can log in:
```bash
npx -y dbeaver-mcp --check
```
```
workspace: C:\Users\you\AppData\Roaming\DBeaverData\workspace6
- Local Insurance [dev] localhost:5432/insurance user=qa, password saved
OK: connected as qa, PostgreSQL 16.4
- Shop [dev] localhost:3306/shop user=app, password saved
OK: connected as app@%, MySQL 8.4.6
- Local cache [dev] C:\data\cache.db
OK: SQLite 3.51.2
- Analytics [prod] warehouse:1521/dwh user=bi, password saved
OK: connected as BI, Oracle 19.24.0.0.0; read-only enforced by: a read-only transaction (DDL is kept out by the SQL guard)
- Billing [test] sql01:1433/billing user=report, password saved
OK: connected as report, SQL Server 16.0.4305.5 (Standard Edition (64-bit)); read-only enforced by: the SQL guard and a rollback only
- Legacy ERP [prod] erp:5000/erp user=sa, password saved
NOT USABLE: mssql is not supported yet (supported: postgresql, mysql, sqlite, sqlserver, oracle)
mode: read-only
```
### Claude Code
```bash
claude mcp add dbeaver -- npx -y dbeaver-mcp
```
On Windows: `claude mcp add dbeaver -- cmd /c npx -y dbeaver-mcp`
Limit it to some connections:
```bash
claude mcp add dbeaver -e DBEAVER_MCP_CONNECTION_TYPES=dev,test -- npx -y dbeaver-mcp
```
### GitHub Copilot in VS Code
`.vscode/mcp.json` in your project (or the user-level MCP config):
```json
{
"servers": {
"dbeaver": {
"type": "stdio",
"command": "npx",
"args": ["-y", "dbeaver-mcp"],
"env": { "DBEAVER_MCP_CONNECTION_TYPES": "dev,test" }
}
}
}
```
Use Copilot Chat in **Agent** mode to call the tools.
### Claude Desktop / Cursor
```json
{
"mcpServers": {
"dbeaver": {
"command": "npx",
"args": ["-y", "dbeaver-mcp"]
}
}
}
```
Claude Desktop: `claude_desktop_config.json`. Cursor: Settings → MCP.
## Configuration
All settings are environment variables, so they work in any MCP client.
| Variable | Default | |
|---|---|---|
| `DBEAVER_WORKSPACE` | auto | Folder that contains `General`. Auto-detected for Windows, macOS, Linux, Flatpak and Snap installs. |
| `DBEAVER_PROJECTS` | all | Only these DBeaver projects, comma separated |
| `DBEAVER_MCP_CONNECTIONS` | all | Only these connections (name, id, or `project/name`) |
| `DBEAVER_MCP_EXCLUDE` | none | Hide these connections |
| `DBEAVER_MCP_CONNECTION_TYPES` | all | Only these DBeaver connection types, e.g. `dev,test` |
| `DBEAVER_MCP_ALLOW_WRITE` | `false` | `true` adds `execute_sql` |
| `DBEAVER_MCP_REQUIRE_DB_READONLY` | `false` | `true` refuses connections whose engine cannot enforce read-only itself (SQL Server) |
| `DBEAVER_MCP_DEFAULT_ROWS` | `100` | Rows returned when the model does not ask for a number |
| `DBEAVER_MCP_MAX_ROWS` | `1000` | Hard limit per query |
| `DBEAVER_MCP_TIMEOUT_MS` | `30000` | Time limit for every query (`statement_timeout`, `max_execution_time` / `max_statement_time`; SQLite, SQL Server and Oracle queries are cancelled by the client). Schema exploration gets at least 15 s, because some dictionaries (Oracle's) are slow. |
| `DBEAVER_MCP_MAX_CELL_CHARS` | `500` | Longer text/JSON values are cut |
The workspace is re-read every few seconds, so a connection you add in DBeaver is visible without restarting.
### What is taken from DBeaver
- Host, port, database or SQLite file (also from the JDBC URL when the connection is saved in URL mode), SQL Server named instances, Oracle service name / SID / TNS alias (`tnsnames.ora` from DBeaver's TNS path or `TNS_ADMIN`)
- Saved user and password (`credentials-config*.json`)
- Connection type (dev / test / prod, and custom types with "confirm execute")
- The "Read-only connection" checkbox
- SSL settings with root/client certificates (PEM): PostgreSQL `sslmode`; MySQL `sslMode` / `useSSL` / `verifyServerCertificate` (like Connector/J, TLS is tried first by default); SQL Server `encrypt` / `trustServerCertificate` (encrypted by default, like the Microsoft JDBC driver)
- Default schema (bootstrap setting): the search path on PostgreSQL, the default database on MySQL, `CURRENT_SCHEMA` on Oracle
If a PostgreSQL connection has no saved password, or the project uses DBeaver's "secure storage" (OS keychain), the server falls back to the normal PostgreSQL ways: `PGPASSWORD` and `~/.pgpass` (`%APPDATA%\postgresql\pgpass.conf` on Windows). MySQL connections without a saved password log in without one.
SQL Server: SQL Server logins only (Windows, Kerberos and Entra ID authentication are listed as unavailable). Oracle: normal database logins only; connections set to log in as SYSDBA/SYSOPER are refused.
**Not supported yet:** SSH tunnels and proxies (those connections are listed as unavailable), and engines other than the six above. See [docs/multi-db-plan.md](docs/multi-db-plan.md) for what comes next; a new engine is one class implementing `Driver` in `src/drivers/types.ts`.
## Safety
Layers, from strongest to weakest:
1. **Read-only in the database, always rolled back** (see the table at the top). On PostgreSQL, MySQL, MariaDB and SQLite, writes, DDL, data-modifying CTEs and `SELECT INTO` all fail inside the engine; on Oracle, DML does. The test suite checks this per engine with the SQL check switched off, and checks that SQL Server's rollback undoes writes and DDL.
2. **Static SQL check**, per dialect. It understands each engine's quoting and comments, including MySQL's `/*! ... */` comments, which the server executes. It allows one statement per call and no transaction control, and it blocks the few things a read-only transaction does not stop:
- PostgreSQL: `dblink*` (opens a second connection), server files (`pg_read_file`, `lo_import`...), admin functions (`pg_terminate_backend`, `set_config`...), password tables (`pg_authid`, `pg_shadow`)
- MySQL / MariaDB: `INTO OUTFILE` / `DUMPFILE`, `LOAD DATA`, `LOAD_FILE()`, `SLEEP()` / `BENCHMARK()` / locks, `SET` / `USE` (session state of pooled connections), `KILL`, `mysql.user`
- SQLite: `ATTACH`, `VACUUM`, `load_extension()` and file functions (SQLite's authorizer also denies them)
- SQL Server: anything but one `SELECT` / `WITH`. SQL Server runs statements sent without `;` as one batch (`SELECT 1 DELETE FROM t`), so in read-only mode the check refuses every other statement's first word anywhere (`INSERT`, `EXEC`, `INTO`, `COMMIT`, `SET`...; quote a column named like a keyword). Also `xp_*`, `sp_OA*`, `OPENROWSET` / `OPENQUERY` / `OPENDATASOURCE`, dynamic SQL, `WAITFOR`, `NEXT VALUE FOR`, login tables.
- Oracle: DDL, PL/SQL blocks, `WITH FUNCTION` and `PRAGMA AUTONOMOUS_TRANSACTION`, `ALTER SESSION`, packages that reach files, the network, other sessions or jobs (`UTL_FILE`, `UTL_HTTP`, `DBMS_LOCK`, `DBMS_SQL`, `DBMS_SCHEDULER`...), `SYS.USER$` / `LINK$`. A stored function that itself uses an autonomous transaction cannot be detected; a read-only login prevents that.
3. **Timeouts and limits.** Every query has a time limit and lock timeouts. Row caps stop on the server side: a cursor on PostgreSQL, `sql_select_limit` on MySQL, stepping the statement on SQLite, `SET ROWCOUNT` on SQL Server, and fetching only the rows needed on Oracle. Big tables are never pulled into memory.
4. **Write mode is opt-in** and still refuses prod-like and DBeaver read-only connections.
What you should still do:
- **Use a database user with SELECT-only grants** for the connections you expose. That is the only protection that does not depend on this code, and on SQL Server it is the only one in the database at all.
- Remember that query results are sent to your AI provider. Check this is allowed for the data (personal data, client data).
- DBeaver's password file is encrypted with a key that is the same in every installation. This tool reads it the same way DBeaver does, and so can any program running as you. The server never returns passwords to the model.
## Development
```bash
npm install
npm run build
npm test # unit tests
npm run inspect # MCP Inspector UI
```
`npm test` always runs the unit tests and the SQLite conformance tests, which build their own database file. Tests for the other engines run when that database is available and loaded with the matching fixture. `test/compose.yml` starts all of them:
```bash
docker compose -p dbeaver-mcp-test -f test/compose.yml up -d
# Oracle takes a minute or two on first start.
docker exec -i dbeaver-mcp-test-postgres-1 psql -U postgres < test/fixture.sql
docker exec -i dbeaver-mcp-test-mysql-1 mysql -uroot -proot < test/fixture.mysql.sql
docker exec -i dbeaver-mcp-test-mariadb-1 mariadb -uroot -proot < test/fixture.mysql.sql
docker cp test/fixture.mssql.sql dbeaver-mcp-test-mssql-1:/tmp/f.sql
docker exec dbeaver-mcp-test-mssql-1 /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P 'Sa_passw0rd!' -i /tmp/f.sql
docker cp test/fixture.oracle.sql dbeaver-mcp-test-oracle-1:/tmp/f.sql
docker exec dbeaver-mcp-test-oracle-1 sqlplus -s system/oracle@//localhost/FREEPDB1 @/tmp/f.sql
TEST_PG_HOST=localhost TEST_PG_PORT=15432 PGPASSWORD=postgres TEST_MYSQL=localhost:13306 TEST_MARIADB=localhost:13307 \
TEST_MSSQL=localhost:11433 TEST_ORACLE=localhost:11521 npm test
```
(`podman` works the same as `docker`. In Git Bash on Windows, set `MSYS_NO_PATHCONV=1` for the `docker cp` / `exec` lines.) `src/conformance.test.ts` runs the same checks against every engine; `src/integration.test.ts` has the PostgreSQL-specific ones.
Publishing:
```bash
npm login
npm publish # runs the tests first
```
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues