From 81e50cadb57bbae24ddac36b630fb80f2130c106 Mon Sep 17 00:00:00 2001
From: t8y2 <1156263951@qq.com>
Date: Sat, 30 May 2026 09:11:33 +0800
Subject: [PATCH] feat: add XuguDB agent support
---
README.md | 4 +-
README.zh-CN.md | 4 +-
apps/desktop/public/icons/database/xugu.png | Bin 0 -> 9209 bytes
.../connection/ConnectionDialog.vue | 3 +
.../src/components/icons/DatabaseIcon.vue | 1 +
.../desktop/src/lib/connectionPresentation.ts | 3 +
apps/desktop/src/lib/connectionUrl.ts | 1 +
.../desktop/src/lib/databaseCapabilitySets.ts | 4 +
apps/desktop/src/types/database.ts | 1 +
.../assets/database-drivers.manifest.json | 11 +
crates/dbx-core/src/agent_catalog.rs | 7 +
crates/dbx-core/src/connection.rs | 1 +
crates/dbx-core/src/models/connection.rs | 5 +
docs/content/docs/databases.cn.mdx | 9 +-
docs/content/docs/databases.mdx | 9 +-
docs/content/docs/driver-management.cn.mdx | 2 +-
docs/content/docs/driver-management.mdx | 2 +-
packages/app-tests/connectionUrl.test.ts | 14 +
.../connectionUrlPlaceholder.test.ts | 1 +
.../app-tests/databaseCapabilities.test.ts | 7 +
packages/mcp-server/src/index.ts | 369 +++++++++---------
packages/node-core/src/diagnostics.ts | 1 +
22 files changed, 265 insertions(+), 194 deletions(-)
create mode 100644 apps/desktop/public/icons/database/xugu.png
diff --git a/README.md b/README.md
index c628b36018..150f2c5b9a 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,7 @@
### 40+ Databases, One Tool
-MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, KingBase, Vastbase, GoldenDB, Doris, SelectDB, StarRocks, Redshift, DM, TDengine, CockroachDB, Access, HighGo, and more. Agent/JDBC-oriented profiles extend DBX to H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, and custom JDBC connections. All in a single ~15 MB app. No bundled Chromium.
+MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, KingBase, Vastbase, GoldenDB, Doris, SelectDB, StarRocks, Redshift, DM, TDengine, XuguDB, CockroachDB, Access, HighGo, and more. Agent/JDBC-oriented profiles extend DBX to H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, and custom JDBC connections. All in a single ~15 MB app. No bundled Chromium.
### Query Editor
@@ -290,7 +290,7 @@ DBX is 15 MB with no runtime dependencies (no Java, no Python). It includes AI a
What databases are supported?
-MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, KingBase, Vastbase, GoldenDB, Doris, SelectDB, StarRocks, Redshift, DM, TDengine, CockroachDB, Access, HighGo, and more. JDBC-oriented profiles extend support to H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, and custom JDBC connections.
+MySQL, PostgreSQL, SQLite, Redis, MongoDB, DuckDB, ClickHouse, SQL Server, Oracle, Elasticsearch, MariaDB, TiDB, OceanBase, openGauss, GaussDB, KingBase, Vastbase, GoldenDB, Doris, SelectDB, StarRocks, Redshift, DM, TDengine, XuguDB, CockroachDB, Access, HighGo, and more. JDBC-oriented profiles extend support to H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, and custom JDBC connections.
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 55ceb23319..ded6d0d6c4 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -82,7 +82,7 @@
### 40+ 种数据库,一个工具搞定
-MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server、Oracle、Elasticsearch、MariaDB、TiDB、OceanBase、openGauss、GaussDB、KingBase、Vastbase、GoldenDB、Doris、SelectDB、StarRocks、Redshift、DM、TDengine、CockroachDB、Access、HighGo 等数据库都能直接连接。Agent/JDBC 方向的配置还可扩展到 H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB 和自定义 JDBC。全部装进约 15 MB 的应用里,不内嵌 Chromium。
+MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server、Oracle、Elasticsearch、MariaDB、TiDB、OceanBase、openGauss、GaussDB、KingBase、Vastbase、GoldenDB、Doris、SelectDB、StarRocks、Redshift、DM、TDengine、虚谷 XuguDB、CockroachDB、Access、HighGo 等数据库都能直接连接。Agent/JDBC 方向的配置还可扩展到 H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB 和自定义 JDBC。全部装进约 15 MB 的应用里,不内嵌 Chromium。
### 查询编辑器
@@ -290,7 +290,7 @@ DBX 仅 15 MB,无需运行时依赖(无需 Java、无需 Python)。AI 和
支持哪些数据库?
-MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server、Oracle、Elasticsearch、MariaDB、TiDB、OceanBase、openGauss、GaussDB、KingBase、Vastbase、GoldenDB、Doris、SelectDB、StarRocks、Redshift、DM、TDengine、CockroachDB、Access、HighGo 等。JDBC 方向配置可扩展到 H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB 及自定义 JDBC 连接。
+MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server、Oracle、Elasticsearch、MariaDB、TiDB、OceanBase、openGauss、GaussDB、KingBase、Vastbase、GoldenDB、Doris、SelectDB、StarRocks、Redshift、DM、TDengine、虚谷 XuguDB、CockroachDB、Access、HighGo 等。JDBC 方向配置可扩展到 H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB 及自定义 JDBC 连接。
diff --git a/apps/desktop/public/icons/database/xugu.png b/apps/desktop/public/icons/database/xugu.png
new file mode 100644
index 0000000000000000000000000000000000000000..e564ac22949763f8c4f2f3025ecdf3ed553e5415
GIT binary patch
literal 9209
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
literal 0
HcmV?d00001
diff --git a/apps/desktop/src/components/connection/ConnectionDialog.vue b/apps/desktop/src/components/connection/ConnectionDialog.vue
index 19f687c8c0..582030b767 100644
--- a/apps/desktop/src/components/connection/ConnectionDialog.vue
+++ b/apps/desktop/src/components/connection/ConnectionDialog.vue
@@ -257,6 +257,7 @@ const driverProfiles: Record<
sundb: { type: "sundb", port: 22000, user: "root", label: "SunDB", icon: "sundb" },
jdbc: { type: "jdbc", port: 0, user: "", label: "JDBC", icon: "jdbc" },
tdengine: { type: "tdengine", port: 6041, user: "root", label: "TDengine", icon: "tdengine" },
+ xugu: { type: "xugu", port: 5138, user: "", label: "虚谷 XuguDB", icon: "xugu" },
iris: { type: "iris", port: 1972, user: "_SYSTEM", label: "IRIS", icon: "iris" },
custom_mysql: {
type: "mysql",
@@ -486,6 +487,7 @@ const iconTypeMap: Record = {
redshift: "redshift",
cockroachdb: "cockroachdb",
tdengine: "tdengine",
+ xugu: "xugu",
dm: "dm",
h2: "h2",
snowflake: "snowflake",
@@ -554,6 +556,7 @@ const dbOptions = [
{ value: "bigquery", label: "BigQuery" },
{ value: "kylin", label: "Kylin" },
{ value: "sundb", label: "SunDB" },
+ { value: "xugu", label: "虚谷 XuguDB" },
{ value: "iris", label: "IRIS" },
{ value: "jdbc", label: "JDBC" },
{ value: "custom_mysql", label: "Custom (MySQL)" },
diff --git a/apps/desktop/src/components/icons/DatabaseIcon.vue b/apps/desktop/src/components/icons/DatabaseIcon.vue
index c1892dd5d9..76d88fd913 100644
--- a/apps/desktop/src/components/icons/DatabaseIcon.vue
+++ b/apps/desktop/src/components/icons/DatabaseIcon.vue
@@ -62,6 +62,7 @@ const assetIcons: Record = {
tdsql: "tdsql.webp",
polardb: "polardb.webp",
greatsql: "greatsql.webp",
+ xugu: "xugu.png",
iris: "iris.png",
};
diff --git a/apps/desktop/src/lib/connectionPresentation.ts b/apps/desktop/src/lib/connectionPresentation.ts
index 733d470a9b..be4eb0a166 100644
--- a/apps/desktop/src/lib/connectionPresentation.ts
+++ b/apps/desktop/src/lib/connectionPresentation.ts
@@ -70,6 +70,9 @@ export function connectionUrlPlaceholder(dbType: DatabaseType): string {
case "tdengine":
return "tdengine://user:password@host:6041/database";
+ case "xugu":
+ return "xugu://user:password@host:5138/database";
+
case "bigquery":
return "bigquery://https://www.googleapis.com/bigquery/v2:443/project-id";
diff --git a/apps/desktop/src/lib/connectionUrl.ts b/apps/desktop/src/lib/connectionUrl.ts
index 2ad2e44e7c..bdfc140ff0 100644
--- a/apps/desktop/src/lib/connectionUrl.ts
+++ b/apps/desktop/src/lib/connectionUrl.ts
@@ -45,6 +45,7 @@ const SCHEME_PROFILES: Record = {
opengauss: { type: "gaussdb", profile: "opengauss", label: "openGauss", defaultPort: 5432 },
tdengine: { type: "tdengine", profile: "tdengine", label: "TDengine", defaultPort: 6041 },
"taos-ws": { type: "tdengine", profile: "tdengine", label: "TDengine", defaultPort: 6041 },
+ xugu: { type: "xugu", profile: "xugu", label: "XuguDB", defaultPort: 5138 },
iris: { type: "iris", profile: "iris", label: "IRIS", defaultPort: 1972 },
};
diff --git a/apps/desktop/src/lib/databaseCapabilitySets.ts b/apps/desktop/src/lib/databaseCapabilitySets.ts
index e34488a651..cebefe8bd7 100644
--- a/apps/desktop/src/lib/databaseCapabilitySets.ts
+++ b/apps/desktop/src/lib/databaseCapabilitySets.ts
@@ -25,6 +25,7 @@ export const SCHEMA_AWARE_TYPES = new Set([
"trino",
"db2",
"tdengine",
+ "xugu",
"iris",
"duckdb",
]);
@@ -98,6 +99,7 @@ export const DATABASE_SEARCH_SUPPORTED_TYPES = new Set([
"kylin",
"sundb",
"tdengine",
+ "xugu",
"iris",
]);
@@ -206,6 +208,7 @@ export const TREE_SCHEMA_TYPES = new Set([
"trino",
"h2",
"tdengine",
+ "xugu",
"iris",
"duckdb",
]);
@@ -243,6 +246,7 @@ export const AGENT_DRIVER_TYPES = new Set([
"sundb",
"gaussdb",
"tdengine",
+ "xugu",
"iris",
]);
diff --git a/apps/desktop/src/types/database.ts b/apps/desktop/src/types/database.ts
index 111bd49256..5d1f5a2139 100644
--- a/apps/desktop/src/types/database.ts
+++ b/apps/desktop/src/types/database.ts
@@ -41,6 +41,7 @@ export type DatabaseType =
| "kylin"
| "sundb"
| "tdengine"
+ | "xugu"
| "iris"
| "jdbc";
diff --git a/crates/dbx-core/assets/database-drivers.manifest.json b/crates/dbx-core/assets/database-drivers.manifest.json
index 138b1a005c..ea8da538f6 100644
--- a/crates/dbx-core/assets/database-drivers.manifest.json
+++ b/crates/dbx-core/assets/database-drivers.manifest.json
@@ -449,6 +449,17 @@
"skipTcpProbe": true,
"defaultPort": 6041
},
+ {
+ "dbType": "xugu",
+ "label": "虚谷 XuguDB",
+ "runtimeMode": "agent",
+ "mcpMode": "bridge",
+ "agentKey": "xugu",
+ "singleConnectionPool": false,
+ "metadataConnectionScoped": false,
+ "skipTcpProbe": true,
+ "defaultPort": 5138
+ },
{
"dbType": "jdbc",
"label": "JDBC",
diff --git a/crates/dbx-core/src/agent_catalog.rs b/crates/dbx-core/src/agent_catalog.rs
index 56881aac8e..17b589dba5 100644
--- a/crates/dbx-core/src/agent_catalog.rs
+++ b/crates/dbx-core/src/agent_catalog.rs
@@ -205,6 +205,13 @@ const AGENT_CATALOG: &[AgentCatalogEntry] = &[
store_visible: true,
profiles: &[],
},
+ AgentCatalogEntry {
+ db_type: DatabaseType::Xugu,
+ key: "xugu",
+ label: "虚谷 XuguDB",
+ store_visible: true,
+ profiles: &[],
+ },
AgentCatalogEntry {
db_type: DatabaseType::MongoDb,
key: "mongodb",
diff --git a/crates/dbx-core/src/connection.rs b/crates/dbx-core/src/connection.rs
index 5f7437e130..d23c8e9874 100644
--- a/crates/dbx-core/src/connection.rs
+++ b/crates/dbx-core/src/connection.rs
@@ -300,6 +300,7 @@ impl AppState {
| DatabaseType::Kylin
| DatabaseType::Sundb
| DatabaseType::Tdengine
+ | DatabaseType::Xugu
| DatabaseType::Iris
| DatabaseType::Access => {
let connect_params =
diff --git a/crates/dbx-core/src/models/connection.rs b/crates/dbx-core/src/models/connection.rs
index 1afe119524..60c954a21a 100644
--- a/crates/dbx-core/src/models/connection.rs
+++ b/crates/dbx-core/src/models/connection.rs
@@ -190,6 +190,7 @@ pub enum DatabaseType {
Kylin,
Sundb,
Tdengine,
+ Xugu,
#[serde(rename = "iris")]
Iris,
Jdbc,
@@ -377,6 +378,7 @@ impl ConnectionConfig {
DatabaseType::Kylin => format!("kylin://{host}:{port}{db_part}"),
DatabaseType::Sundb => format!("sundb://{host}:{port}{db_part}"),
DatabaseType::Tdengine => format!("tdengine://{host}:{port}{db_part}"),
+ DatabaseType::Xugu => format!("xugu://{host}:{port}{db_part}"),
DatabaseType::Iris => format!("iris://{host}:{port}{db_part}"),
DatabaseType::Jdbc => "jdbc:".to_string(),
}
@@ -538,6 +540,9 @@ impl ConnectionConfig {
DatabaseType::Tdengine => {
format!("tdengine://{}:{}@{host}:{port}{db_part}", username, password)
}
+ DatabaseType::Xugu => {
+ format!("xugu://{}:{}@{host}:{port}{db_part}", username, password)
+ }
DatabaseType::Iris => {
format!("iris://{}:{}@{host}:{port}{db_part}", username, password)
}
diff --git a/docs/content/docs/databases.cn.mdx b/docs/content/docs/databases.cn.mdx
index ec289be9e2..eb5f60d321 100644
--- a/docs/content/docs/databases.cn.mdx
+++ b/docs/content/docs/databases.cn.mdx
@@ -18,7 +18,7 @@ DBX 主应用会尽量使用 Rust 原生驱动。部分数据库通过 MySQL 或
| MySQL 兼容类型 | MariaDB、TiDB、OceanBase、Doris、SelectDB、StarRocks、GoldenDB、自定义 MySQL | 复用 MySQL 风格连接能力 |
| PostgreSQL 兼容类型 | openGauss、KingBase、HighGo、Vastbase、CockroachDB、自定义 PostgreSQL | 复用 PostgreSQL 风格连接能力 |
| 文件型数据库 | SQLite、DuckDB、Microsoft Access | 选择本地数据库文件,不填写主机和端口 |
-| Agent/JDBC 扩展类型 | H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB、TDengine、JDBC | 功能覆盖取决于对应驱动路径 |
+| Agent/JDBC 扩展类型 | H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB、TDengine、虚谷 XuguDB、JDBC | 功能覆盖取决于对应驱动路径 |
## 默认端口
@@ -52,6 +52,7 @@ DBX 主应用会尽量使用 Rust 原生驱动。部分数据库通过 MySQL 或
| Kylin | 7070 |
| SunDB | 22000 |
| TDengine | 6041 |
+| 虚谷 XuguDB | 5138 |
SQLite、DuckDB、Access 和 JDBC 在不需要网络端口时,会在连接模型中保存为端口 `0`。
@@ -63,9 +64,9 @@ DBX 只会在具备足够元数据和 SQL 生成能力的数据库上启用高
| 功能 | 支持类型 |
|---|---|
-| Schema 感知树 | PostgreSQL、SQL Server、Oracle、Redshift、DM、GaussDB、KingBase、HighGo、Vastbase、JDBC、H2、Snowflake、Trino、DB2、TDengine |
+| Schema 感知树 | PostgreSQL、SQL Server、Oracle、Redshift、DM、GaussDB、KingBase、HighGo、Vastbase、JDBC、H2、Snowflake、Trino、DB2、TDengine、虚谷 XuguDB |
| ER 图 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redshift、DM、GaussDB、KingBase、HighGo、Vastbase、GoldenDB、Access、H2、DB2 |
-| 数据库搜索 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redshift、DuckDB、ClickHouse、DM、GaussDB、KingBase、HighGo、Vastbase、GoldenDB、Access、H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB、TDengine |
+| 数据库搜索 | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redshift、DuckDB、ClickHouse、DM、GaussDB、KingBase、HighGo、Vastbase、GoldenDB、Access、H2、Snowflake、Trino、Hive、DB2、Informix、Neo4j、Cassandra、BigQuery、Kylin、SunDB、TDengine、虚谷 XuguDB |
| 表数据导入 | MySQL、PostgreSQL、SQLite、DuckDB、ClickHouse、SQL Server、Oracle、Doris、StarRocks、Redshift、DM、GaussDB、KingBase、HighGo、Vastbase、GoldenDB、Access |
| 表结构编辑器 | MySQL、PostgreSQL、SQLite、SQL Server |
| 创建数据库 | MySQL、PostgreSQL、SQL Server、ClickHouse、Oracle、DM、GaussDB、Doris、StarRocks、Redshift |
@@ -81,7 +82,7 @@ DBX 只会在具备足够元数据和 SQL 生成能力的数据库上启用高
大多数网络数据库支持主机、端口、用户名、密码、默认数据库、URL 参数、SSL、连接颜色、SSH 隧道和代理设置。文件型数据库会改为选择本地文件。
-DBX 也可以解析常见连接 URL,例如 MySQL、PostgreSQL、Redis、MongoDB、ClickHouse、SQL Server、Oracle、Elasticsearch、DM、GaussDB、openGauss、TDengine 和 Access。
+DBX 也可以解析常见连接 URL,例如 MySQL、PostgreSQL、Redis、MongoDB、ClickHouse、SQL Server、Oracle、Elasticsearch、DM、GaussDB、openGauss、TDengine、虚谷 XuguDB 和 Access。
### ClickHouse TLS
diff --git a/docs/content/docs/databases.mdx b/docs/content/docs/databases.mdx
index b396b0299b..ff76fa7bd0 100644
--- a/docs/content/docs/databases.mdx
+++ b/docs/content/docs/databases.mdx
@@ -18,7 +18,7 @@ The connection dialog exposes ready-to-use profiles with default ports and drive
| MySQL-compatible profiles | MariaDB, TiDB, OceanBase, Doris, SelectDB, StarRocks, GoldenDB, custom MySQL | Reuse MySQL-style connection handling where the engine speaks a compatible protocol |
| PostgreSQL-compatible profiles | openGauss, KingBase, HighGo, Vastbase, CockroachDB, custom PostgreSQL | Reuse PostgreSQL-style connection handling where the engine speaks a compatible protocol |
| File-based engines | SQLite, DuckDB, Microsoft Access | Choose a local database file instead of host and port |
-| Agent/JDBC-oriented engines | H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, TDengine, JDBC | Feature coverage depends on the driver path used by that engine |
+| Agent/JDBC-oriented engines | H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, TDengine, XuguDB, JDBC | Feature coverage depends on the driver path used by that engine |
## Default Ports
@@ -52,6 +52,7 @@ The connection dialog exposes ready-to-use profiles with default ports and drive
| Kylin | 7070 |
| SunDB | 22000 |
| TDengine | 6041 |
+| XuguDB | 5138 |
SQLite, DuckDB, Access, and JDBC profiles use port `0` in the saved connection model when a network port is not part of the connection.
@@ -63,9 +64,9 @@ DBX intentionally enables advanced workflows only where the app has enough metad
| Feature | Supported Types |
|---|---|
-| Schema-aware tree | PostgreSQL, SQL Server, Oracle, Redshift, DM, GaussDB, KingBase, HighGo, Vastbase, JDBC, H2, Snowflake, Trino, DB2, TDengine |
+| Schema-aware tree | PostgreSQL, SQL Server, Oracle, Redshift, DM, GaussDB, KingBase, HighGo, Vastbase, JDBC, H2, Snowflake, Trino, DB2, TDengine, XuguDB |
| ER diagram | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redshift, DM, GaussDB, KingBase, HighGo, Vastbase, GoldenDB, Access, H2, DB2 |
-| Database search | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redshift, DuckDB, ClickHouse, DM, GaussDB, KingBase, HighGo, Vastbase, GoldenDB, Access, H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, TDengine |
+| Database search | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redshift, DuckDB, ClickHouse, DM, GaussDB, KingBase, HighGo, Vastbase, GoldenDB, Access, H2, Snowflake, Trino, Hive, DB2, Informix, Neo4j, Cassandra, BigQuery, Kylin, SunDB, TDengine, XuguDB |
| Table import | MySQL, PostgreSQL, SQLite, DuckDB, ClickHouse, SQL Server, Oracle, Doris, StarRocks, Redshift, DM, GaussDB, KingBase, HighGo, Vastbase, GoldenDB, Access |
| Table structure editor | MySQL, PostgreSQL, SQLite, SQL Server |
| Create database | MySQL, PostgreSQL, SQL Server, ClickHouse, Oracle, DM, GaussDB, Doris, StarRocks, Redshift |
@@ -81,7 +82,7 @@ If a database can connect but does not appear in a feature row, that feature may
Most network databases support host, port, username, password, default database, optional URL parameters, SSL, connection color, SSH tunnel, and proxy settings. File-based engines replace host and port with a file picker.
-DBX can also parse common connection URLs for engines such as MySQL, PostgreSQL, Redis, MongoDB, ClickHouse, SQL Server, Oracle, Elasticsearch, DM, GaussDB, openGauss, TDengine, and Access.
+DBX can also parse common connection URLs for engines such as MySQL, PostgreSQL, Redis, MongoDB, ClickHouse, SQL Server, Oracle, Elasticsearch, DM, GaussDB, openGauss, TDengine, XuguDB, and Access.
### ClickHouse TLS
diff --git a/docs/content/docs/driver-management.cn.mdx b/docs/content/docs/driver-management.cn.mdx
index effab3d052..e0b076bcbd 100644
--- a/docs/content/docs/driver-management.cn.mdx
+++ b/docs/content/docs/driver-management.cn.mdx
@@ -10,7 +10,7 @@ DBX 使用混合驱动架构:常用数据库使用内置原生驱动,需要
| 驱动类型 | 工作方式 | 适用场景 |
|---|---|---|
| 原生(内置) | Rust 驱动编译进 DBX | MySQL、PostgreSQL、SQLite、SQL Server、Oracle、Redis、MongoDB、DuckDB、ClickHouse 等 |
-| JDBC Agent | DBX 管理的 Java 子进程 | 提供 JDBC 驱动的数据库:GaussDB、openGauss、DM、KingBase、HighGo、Vastbase、Trino、Hive、DB2、Informix、Neo4j、TDengine、YashanDB、GoldenDB、Kylin、SunDB 等 |
+| JDBC Agent | DBX 管理的 Java 子进程 | 提供 JDBC 驱动的数据库:GaussDB、openGauss、DM、KingBase、HighGo、Vastbase、Trino、Hive、DB2、Informix、Neo4j、TDengine、虚谷 XuguDB、YashanDB、GoldenDB、Kylin、SunDB 等 |
原生驱动安装后立即可用。Agent 驱动需要一次性下载 JDBC 驱动和 JRE 设置,首次创建连接时 DBX 会自动处理。
diff --git a/docs/content/docs/driver-management.mdx b/docs/content/docs/driver-management.mdx
index 4ed15fcc52..a1eda4708e 100644
--- a/docs/content/docs/driver-management.mdx
+++ b/docs/content/docs/driver-management.mdx
@@ -10,7 +10,7 @@ DBX uses a hybrid driver architecture: built-in native drivers for common databa
| Driver Type | How It Works | Best For |
|---|---|---|
| Native (built-in) | Rust drivers compiled into DBX | MySQL, PostgreSQL, SQLite, SQL Server, Oracle, Redis, MongoDB, DuckDB, ClickHouse, and more |
-| JDBC Agent | Java subprocess managed by DBX | Databases that provide JDBC drivers: GaussDB, openGauss, DM, KingBase, HighGo, Vastbase, Trino, Hive, DB2, Informix, Neo4j, TDengine, YashanDB, GoldenDB, Kylin, SunDB, and more |
+| JDBC Agent | Java subprocess managed by DBX | Databases that provide JDBC drivers: GaussDB, openGauss, DM, KingBase, HighGo, Vastbase, Trino, Hive, DB2, Informix, Neo4j, TDengine, XuguDB, YashanDB, GoldenDB, Kylin, SunDB, and more |
Native drivers work immediately after installation. Agent drivers require a one-time JDBC driver download and JRE setup, which DBX handles automatically when you first create a connection.
diff --git a/packages/app-tests/connectionUrl.test.ts b/packages/app-tests/connectionUrl.test.ts
index eba4db9937..8fd9f3892a 100644
--- a/packages/app-tests/connectionUrl.test.ts
+++ b/packages/app-tests/connectionUrl.test.ts
@@ -71,6 +71,20 @@ test("parses TDengine WebSocket JDBC URLs", () => {
assert.equal(parsed.urlParams, "timezone=UTC");
});
+test("parses XuguDB JDBC URLs", () => {
+ const parsed = parseConnectionUrl("jdbc:xugu://alice:secret@xugu.example.com:5138/demo?charset=utf8");
+
+ assert.equal(parsed.dbType, "xugu");
+ assert.equal(parsed.driverProfile, "xugu");
+ assert.equal(parsed.driverLabel, "XuguDB");
+ assert.equal(parsed.host, "xugu.example.com");
+ assert.equal(parsed.port, 5138);
+ assert.equal(parsed.username, "alice");
+ assert.equal(parsed.password, "secret");
+ assert.equal(parsed.database, "demo");
+ assert.equal(parsed.urlParams, "charset=utf8");
+});
+
test("parses UCanAccess JDBC URLs as Access database files", () => {
const parsed = parseConnectionUrl("jdbc:ucanaccess:///Users/me/data/Northwind.accdb;memory=false");
diff --git a/packages/app-tests/connectionUrlPlaceholder.test.ts b/packages/app-tests/connectionUrlPlaceholder.test.ts
index 78c44bb658..970120ebea 100644
--- a/packages/app-tests/connectionUrlPlaceholder.test.ts
+++ b/packages/app-tests/connectionUrlPlaceholder.test.ts
@@ -20,6 +20,7 @@ const expected: Record = {
elasticsearch: "http://user:password@host:port",
dameng: "dm://user:password@host:port",
tdengine: "tdengine://user:password@host:6041/database",
+ xugu: "xugu://user:password@host:5138/database",
bigquery: "bigquery://https://www.googleapis.com/bigquery/v2:443/project-id",
jdbc: "jdbc:mysql://host:3306/database",
};
diff --git a/packages/app-tests/databaseCapabilities.test.ts b/packages/app-tests/databaseCapabilities.test.ts
index e038d7873d..c466f2c366 100644
--- a/packages/app-tests/databaseCapabilities.test.ts
+++ b/packages/app-tests/databaseCapabilities.test.ts
@@ -35,6 +35,13 @@ test("treats TDengine databases as schema tree roots and agent driver databases"
assert.equal(supportsDriverManagement("tdengine"), true);
});
+test("treats XuguDB as a schema-aware agent driver database", () => {
+ assert.equal(TREE_SCHEMA_TYPES.has("xugu"), true);
+ assert.equal(SCHEMA_AWARE_TYPES.has("xugu"), true);
+ assert.equal(supportsDatabaseSearch("xugu"), true);
+ assert.equal(supportsDriverManagement("xugu"), true);
+});
+
test("treats Access as a local single-database agent driver", () => {
assert.equal(SCHEMA_AWARE_TYPES.has("access"), false);
assert.equal(supportsDriverManagement("access"), true);
diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts
index 3685336f1f..7da4ec1a79 100644
--- a/packages/mcp-server/src/index.ts
+++ b/packages/mcp-server/src/index.ts
@@ -34,7 +34,7 @@ function withDatabase(config: ConnectionConfig, database?: string): ConnectionCo
}
export const DBX_CONNECTION_TYPE_DESCRIPTION =
- "Database type: postgres, mysql, sqlite, redis, duckdb, clickhouse, sqlserver, mongodb, oracle, elasticsearch, doris, starrocks, redshift, dameng, kingbase, highgo, vastbase, goldendb, gaussdb, yashandb, databricks, saphana, teradata, vertica, firebird, exasol, opengauss, oceanbase-oracle, gbase, h2, snowflake, trino, hive, db2, informix, neo4j, cassandra, bigquery, kylin, sundb, tdengine, jdbc, access";
+ "Database type: postgres, mysql, sqlite, redis, duckdb, clickhouse, sqlserver, mongodb, oracle, elasticsearch, doris, starrocks, redshift, dameng, kingbase, highgo, vastbase, goldendb, gaussdb, yashandb, databricks, saphana, teradata, vertica, firebird, exasol, opengauss, oceanbase-oracle, gbase, h2, snowflake, trino, hive, db2, informix, neo4j, cassandra, bigquery, kylin, sundb, tdengine, xugu, jdbc, access";
export function createDbxMcpServer(backend: Backend, options: { isWebMode?: boolean } = {}): McpServer {
const isWebMode = options.isWebMode ?? !!process.env.DBX_WEB_URL;
@@ -44,199 +44,208 @@ export function createDbxMcpServer(backend: Backend, options: { isWebMode?: bool
});
server.tool("dbx_list_connections", "List all database connections configured in DBX", {}, async () => {
- const connections = await backend.loadConnections();
- if (connections.length === 0) return text("No connections configured in DBX.");
- const rows = connections.map((c) => [c.name, c.db_type, c.host, String(c.port), c.database || ""]);
- return text(mdTable(["Name", "Type", "Host", "Port", "Database"], rows));
+ const connections = await backend.loadConnections();
+ if (connections.length === 0) return text("No connections configured in DBX.");
+ const rows = connections.map((c) => [c.name, c.db_type, c.host, String(c.port), c.database || ""]);
+ return text(mdTable(["Name", "Type", "Host", "Port", "Database"], rows));
});
server.tool(
- "dbx_list_tables",
- "List tables and views for a database connection",
- {
- connection_name: z.string().describe("Name of the DBX connection"),
- database: z.string().optional().describe("Database name"),
- schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
- },
- async ({ connection_name, database, schema }) => {
- const config = await backend.findConnection(connection_name);
- if (!config) return text(`Connection "${connection_name}" not found`);
- const tables = await backend.listTables(withDatabase(config, database), schema);
- if (tables.length === 0) return text("No tables found.");
- const rows = tables.map((t) => [t.name, t.type]);
- return text(mdTable(["Table", "Type"], rows));
- },
- );
-
- server.tool(
- "dbx_describe_table",
- "Get column definitions for a table",
- {
- connection_name: z.string().describe("Name of the DBX connection"),
- table: z.string().describe("Table name"),
- database: z.string().optional().describe("Database name"),
- schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
- },
- async ({ connection_name, table, database, schema }) => {
- const config = await backend.findConnection(connection_name);
- if (!config) return text(`Connection "${connection_name}" not found`);
- const columns = await backend.describeTable(withDatabase(config, database), table, schema);
- if (columns.length === 0) return text("No columns found.");
- const rows = columns.map((c) => [
- c.is_primary_key ? `${c.name} (PK)` : c.name,
- c.data_type,
- c.is_nullable ? "YES" : "NO",
- c.column_default ?? "",
- c.comment ?? "",
- ]);
- return text(mdTable(["Column", "Type", "Nullable", "Default", "Comment"], rows));
- },
- );
-
- server.tool(
- "dbx_execute_query",
- "Execute a SQL query on a database connection (max 100 rows returned)",
- {
- connection_name: z.string().describe("Name of the DBX connection"),
- database: z.string().optional().describe("Database name"),
- sql: z.string().describe("SQL query to execute"),
- },
- async ({ connection_name, database, sql }) => {
- const config = await backend.findConnection(connection_name);
- if (!config) return text(`Connection "${connection_name}" not found`);
- if (config.db_type !== "mongodb") {
- const safety = evaluateSqlSafety(sql, sqlSafetyFromEnv());
- if (!safety.allowed) return text(`Query blocked: ${safety.reason}`);
- }
- // MongoDB shell commands don't fit the SQL safety evaluator; the backend
- // (node-core executeQuery) applies command-aware read/write gating.
- try {
- const result = await backend.executeQuery(withDatabase(config, database), sql);
- if (result.columns.length === 0) return text(`Query executed. ${result.row_count} row(s) affected.`);
- const rows = result.rows.map((r) => result.columns.map((c) => formatCell(r[c])));
- return text(`${mdTable(result.columns, rows)}\n\n${result.row_count} row(s)`);
- } catch (e: unknown) {
- const msg = e instanceof Error ? e.message : String(e);
- return text(`Query error: ${msg}`);
- }
- },
- );
-
- server.tool(
- "dbx_get_schema_context",
- "Get compact table and column context for writing SQL",
- {
- connection_name: z.string().describe("Name of the DBX connection"),
- database: z.string().optional().describe("Database name"),
- schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
- tables: z.array(z.string()).optional().describe("Specific table names to include"),
- max_tables: z.number().int().min(1).max(20).default(8).describe("Maximum number of tables to include"),
- },
- async ({ connection_name, database, schema, tables, max_tables }) => {
- const config = await backend.findConnection(connection_name);
- if (!config) return text(`Connection "${connection_name}" not found`);
- const context = await buildSchemaContext(backend, withDatabase(config, database), { schema, tables, maxTables: max_tables });
- if (context.tables.length === 0) return text("No matching tables found.");
- return text(formatSchemaContext(context));
- },
- );
-
- server.tool(
- "dbx_add_connection",
- "Add a new database connection to DBX",
- {
- name: z.string().describe("Connection name"),
- db_type: z
- .string()
- .describe(DBX_CONNECTION_TYPE_DESCRIPTION),
- host: z.string().describe("Database host"),
- port: z.number().optional().describe("Database port (TDengine defaults to 6041)"),
- username: z.string().default("").describe("Username"),
- password: z.string().default("").describe("Password"),
- database: z.string().optional().describe("Default database name"),
- ssl: z.boolean().default(false).describe("Enable SSL"),
- },
- async ({ name, db_type, host, port, username, password, database, ssl }) => {
- const existing = await backend.findConnection(name);
- if (existing) return text(`Connection "${name}" already exists.`);
- const FILE_BASED_TYPES = new Set(["sqlite", "duckdb", "access"]);
- const resolvedPort = port ?? (db_type === "tdengine" ? 6041 : FILE_BASED_TYPES.has(db_type) ? 0 : undefined);
- if (resolvedPort === undefined) return text("Port is required for this database type.");
- const config = await backend.addConnection({
- name,
- db_type,
- host,
- port: resolvedPort,
- username,
- password,
- database,
- ssl,
- ssh_enabled: false,
- } as Omit);
- await notifyReload();
- return text(`Connection "${config.name}" added (id: ${config.id}).`);
- },
- );
-
- server.tool(
- "dbx_remove_connection",
- "Remove a database connection from DBX",
- {
- connection_name: z.string().describe("Name of the connection to remove"),
- },
- async ({ connection_name }) => {
- const removed = await backend.removeConnection(connection_name);
- if (!removed) return text(`Connection "${connection_name}" not found.`);
- await notifyReload();
- return text(`Connection "${connection_name}" removed.`);
- },
- );
-
-// Desktop-only tools: open table and execute-and-show require the Tauri bridge
- if (!isWebMode) {
- server.tool(
- "dbx_open_table",
- "Open a table in DBX desktop app UI. Requires DBX to be running.",
+ "dbx_list_tables",
+ "List tables and views for a database connection",
{
connection_name: z.string().describe("Name of the DBX connection"),
- table: z.string().describe("Table name to open"),
database: z.string().optional().describe("Database name"),
- schema: z.string().optional().describe("Schema name"),
+ schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
+ },
+ async ({ connection_name, database, schema }) => {
+ const config = await backend.findConnection(connection_name);
+ if (!config) return text(`Connection "${connection_name}" not found`);
+ const tables = await backend.listTables(withDatabase(config, database), schema);
+ if (tables.length === 0) return text("No tables found.");
+ const rows = tables.map((t) => [t.name, t.type]);
+ return text(mdTable(["Table", "Type"], rows));
+ },
+ );
+
+ server.tool(
+ "dbx_describe_table",
+ "Get column definitions for a table",
+ {
+ connection_name: z.string().describe("Name of the DBX connection"),
+ table: z.string().describe("Table name"),
+ database: z.string().optional().describe("Database name"),
+ schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
},
async ({ connection_name, table, database, schema }) => {
- return bridgeRequest("/open-table", { connection_name, table, database, schema }, `Opened ${table} in DBX`);
+ const config = await backend.findConnection(connection_name);
+ if (!config) return text(`Connection "${connection_name}" not found`);
+ const columns = await backend.describeTable(withDatabase(config, database), table, schema);
+ if (columns.length === 0) return text("No columns found.");
+ const rows = columns.map((c) => [
+ c.is_primary_key ? `${c.name} (PK)` : c.name,
+ c.data_type,
+ c.is_nullable ? "YES" : "NO",
+ c.column_default ?? "",
+ c.comment ?? "",
+ ]);
+ return text(mdTable(["Column", "Type", "Nullable", "Default", "Comment"], rows));
},
+ );
+
+ server.tool(
+ "dbx_execute_query",
+ "Execute a SQL query on a database connection (max 100 rows returned)",
+ {
+ connection_name: z.string().describe("Name of the DBX connection"),
+ database: z.string().optional().describe("Database name"),
+ sql: z.string().describe("SQL query to execute"),
+ },
+ async ({ connection_name, database, sql }) => {
+ const config = await backend.findConnection(connection_name);
+ if (!config) return text(`Connection "${connection_name}" not found`);
+ if (config.db_type !== "mongodb") {
+ const safety = evaluateSqlSafety(sql, sqlSafetyFromEnv());
+ if (!safety.allowed) return text(`Query blocked: ${safety.reason}`);
+ }
+ // MongoDB shell commands don't fit the SQL safety evaluator; the backend
+ // (node-core executeQuery) applies command-aware read/write gating.
+ try {
+ const result = await backend.executeQuery(withDatabase(config, database), sql);
+ if (result.columns.length === 0) return text(`Query executed. ${result.row_count} row(s) affected.`);
+ const rows = result.rows.map((r) => result.columns.map((c) => formatCell(r[c])));
+ return text(`${mdTable(result.columns, rows)}\n\n${result.row_count} row(s)`);
+ } catch (e: unknown) {
+ const msg = e instanceof Error ? e.message : String(e);
+ return text(`Query error: ${msg}`);
+ }
+ },
+ );
+
+ server.tool(
+ "dbx_get_schema_context",
+ "Get compact table and column context for writing SQL",
+ {
+ connection_name: z.string().describe("Name of the DBX connection"),
+ database: z.string().optional().describe("Database name"),
+ schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
+ tables: z.array(z.string()).optional().describe("Specific table names to include"),
+ max_tables: z.number().int().min(1).max(20).default(8).describe("Maximum number of tables to include"),
+ },
+ async ({ connection_name, database, schema, tables, max_tables }) => {
+ const config = await backend.findConnection(connection_name);
+ if (!config) return text(`Connection "${connection_name}" not found`);
+ const context = await buildSchemaContext(backend, withDatabase(config, database), {
+ schema,
+ tables,
+ maxTables: max_tables,
+ });
+ if (context.tables.length === 0) return text("No matching tables found.");
+ return text(formatSchemaContext(context));
+ },
+ );
+
+ server.tool(
+ "dbx_add_connection",
+ "Add a new database connection to DBX",
+ {
+ name: z.string().describe("Connection name"),
+ db_type: z.string().describe(DBX_CONNECTION_TYPE_DESCRIPTION),
+ host: z.string().describe("Database host"),
+ port: z.number().optional().describe("Database port (TDengine defaults to 6041, XuguDB defaults to 5138)"),
+ username: z.string().default("").describe("Username"),
+ password: z.string().default("").describe("Password"),
+ database: z.string().optional().describe("Default database name"),
+ ssl: z.boolean().default(false).describe("Enable SSL"),
+ },
+ async ({ name, db_type, host, port, username, password, database, ssl }) => {
+ const existing = await backend.findConnection(name);
+ if (existing) return text(`Connection "${name}" already exists.`);
+ const FILE_BASED_TYPES = new Set(["sqlite", "duckdb", "access"]);
+ const DEFAULT_PORTS: Record = { tdengine: 6041, xugu: 5138 };
+ const resolvedPort = port ?? DEFAULT_PORTS[db_type] ?? (FILE_BASED_TYPES.has(db_type) ? 0 : undefined);
+ if (resolvedPort === undefined) return text("Port is required for this database type.");
+ const config = await backend.addConnection({
+ name,
+ db_type,
+ host,
+ port: resolvedPort,
+ username,
+ password,
+ database,
+ ssl,
+ ssh_enabled: false,
+ } as Omit);
+ await notifyReload();
+ return text(`Connection "${config.name}" added (id: ${config.id}).`);
+ },
+ );
+
+ server.tool(
+ "dbx_remove_connection",
+ "Remove a database connection from DBX",
+ {
+ connection_name: z.string().describe("Name of the connection to remove"),
+ },
+ async ({ connection_name }) => {
+ const removed = await backend.removeConnection(connection_name);
+ if (!removed) return text(`Connection "${connection_name}" not found.`);
+ await notifyReload();
+ return text(`Connection "${connection_name}" removed.`);
+ },
+ );
+
+ // Desktop-only tools: open table and execute-and-show require the Tauri bridge
+ if (!isWebMode) {
+ server.tool(
+ "dbx_open_table",
+ "Open a table in DBX desktop app UI. Requires DBX to be running.",
+ {
+ connection_name: z.string().describe("Name of the DBX connection"),
+ table: z.string().describe("Table name to open"),
+ database: z.string().optional().describe("Database name"),
+ schema: z.string().optional().describe("Schema name"),
+ },
+ async ({ connection_name, table, database, schema }) => {
+ return bridgeRequest("/open-table", { connection_name, table, database, schema }, `Opened ${table} in DBX`);
+ },
);
server.tool(
- "dbx_execute_and_show",
- "Execute a SQL query in DBX desktop app UI and show results there. Requires DBX to be running.",
- {
- connection_name: z.string().describe("Name of the DBX connection"),
- sql: z.string().describe("SQL query to execute"),
- database: z.string().optional().describe("Database name"),
- },
- async ({ connection_name, sql, database }) => {
- const config = await backend.findConnection(connection_name);
- const safetyOptions = sqlSafetyFromEnv();
- if (config?.db_type === "mongodb") {
- const aggregate = parseMongoAggregateCommand(sql);
- if (aggregate) {
- const safety = evaluateMongoAggregateSafety(aggregate, safetyOptions);
+ "dbx_execute_and_show",
+ "Execute a SQL query in DBX desktop app UI and show results there. Requires DBX to be running.",
+ {
+ connection_name: z.string().describe("Name of the DBX connection"),
+ sql: z.string().describe("SQL query to execute"),
+ database: z.string().optional().describe("Database name"),
+ },
+ async ({ connection_name, sql, database }) => {
+ const config = await backend.findConnection(connection_name);
+ const safetyOptions = sqlSafetyFromEnv();
+ if (config?.db_type === "mongodb") {
+ const aggregate = parseMongoAggregateCommand(sql);
+ if (aggregate) {
+ const safety = evaluateMongoAggregateSafety(aggregate, safetyOptions);
+ if (!safety.allowed) return text(`Query blocked: ${safety.reason}`);
+ }
+ } else {
+ const safety = evaluateSqlSafety(sql, safetyOptions);
if (!safety.allowed) return text(`Query blocked: ${safety.reason}`);
}
- } else {
- const safety = evaluateSqlSafety(sql, safetyOptions);
- if (!safety.allowed) return text(`Query blocked: ${safety.reason}`);
- }
- // MongoDB shell commands bypass the SQL safety evaluator; pass MCP
- // safety flags to the desktop executor for command-aware gating.
- return bridgeRequest(
- "/execute-query",
- { connection_name, sql, database, allow_writes: safetyOptions.allowWrites, allow_dangerous: safetyOptions.allowDangerous },
- "Query sent to DBX",
- );
- },
+ // MongoDB shell commands bypass the SQL safety evaluator; pass MCP
+ // safety flags to the desktop executor for command-aware gating.
+ return bridgeRequest(
+ "/execute-query",
+ {
+ connection_name,
+ sql,
+ database,
+ allow_writes: safetyOptions.allowWrites,
+ allow_dangerous: safetyOptions.allowDangerous,
+ },
+ "Query sent to DBX",
+ );
+ },
);
}
diff --git a/packages/node-core/src/diagnostics.ts b/packages/node-core/src/diagnostics.ts
index 53664aded7..3955cf3f3f 100644
--- a/packages/node-core/src/diagnostics.ts
+++ b/packages/node-core/src/diagnostics.ts
@@ -38,6 +38,7 @@ export const BRIDGE_REQUIRED_TYPES = [
"bigquery",
"kylin",
"sundb",
+ "xugu",
"jdbc",
"access",
] as const;