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;