@dbx-app/mcp-server 0.4.3 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +76 -42
- package/package.json +2 -2
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -127,7 +127,7 @@ PostgreSQL, MySQL, SQLite, Doris, StarRocks, and Redshift queries run directly f
|
|
|
127
127
|
## Requirements
|
|
128
128
|
|
|
129
129
|
- [DBX](https://github.com/t8y2/dbx) installed with at least one connection configured
|
|
130
|
-
- Node.js
|
|
130
|
+
- Node.js 22.13.0 或更高版本
|
|
131
131
|
|
|
132
132
|
## License
|
|
133
133
|
|
|
@@ -242,4 +242,4 @@ PostgreSQL、MySQL、SQLite、Doris、StarRocks、Redshift 查询可由 MCP Serv
|
|
|
242
242
|
### 系统要求
|
|
243
243
|
|
|
244
244
|
- 已安装 [DBX](https://github.com/t8y2/dbx) 并配置了至少一个数据库连接
|
|
245
|
-
- Node.js
|
|
245
|
+
- Node.js 22.13.0 or newer
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { type Backend } from "@dbx-app/node-core";
|
|
4
|
+
export declare const DBX_MCP_PACKAGE_VERSION: string;
|
|
5
|
+
export declare 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, xugu, jdbc, access";
|
|
4
6
|
export declare function createDbxMcpServer(backend: Backend, options?: {
|
|
5
7
|
isWebMode?: boolean;
|
|
6
8
|
}): McpServer;
|
package/dist/index.js
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
4
5
|
import { pathToFileURL } from "node:url";
|
|
5
6
|
import { z } from "zod";
|
|
6
|
-
import { buildSchemaContext, createBackend, evaluateSqlSafety, formatSchemaContext, notifyReload, postBridge, sqlSafetyFromEnv, } from "@dbx-app/node-core";
|
|
7
|
+
import { buildSchemaContext, createBackend, evaluateMongoAggregateSafety, evaluateSqlSafety, formatCell, formatSchemaContext, mdTable, notifyReload, parseMongoAggregateCommand, postBridge, sqlSafetyFromEnv, } from "@dbx-app/node-core";
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const packageJson = require("../package.json");
|
|
10
|
+
export const DBX_MCP_PACKAGE_VERSION = packageJson.version ?? "0.0.0";
|
|
7
11
|
function text(s) {
|
|
8
12
|
return { content: [{ type: "text", text: s }] };
|
|
9
13
|
}
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
const header = `| ${headers.map((h, i) => h.padEnd(widths[i])).join(" | ")} |`;
|
|
13
|
-
const sep = `| ${widths.map((w) => "-".repeat(w)).join(" | ")} |`;
|
|
14
|
-
const body = rows.map((r) => `| ${r.map((c, i) => (c || "").padEnd(widths[i])).join(" | ")} |`).join("\n");
|
|
15
|
-
return `${header}\n${sep}\n${body}`;
|
|
14
|
+
function toolError(code, message) {
|
|
15
|
+
return { ...text(`${code}: ${message}`), isError: true };
|
|
16
16
|
}
|
|
17
|
+
function withDatabase(config, database) {
|
|
18
|
+
return database === undefined ? config : { ...config, database };
|
|
19
|
+
}
|
|
20
|
+
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, xugu, jdbc, access";
|
|
17
21
|
export function createDbxMcpServer(backend, options = {}) {
|
|
18
22
|
const isWebMode = options.isWebMode ?? !!process.env.DBX_WEB_URL;
|
|
19
23
|
const server = new McpServer({
|
|
20
24
|
name: "dbx",
|
|
21
|
-
version:
|
|
25
|
+
version: DBX_MCP_PACKAGE_VERSION,
|
|
22
26
|
});
|
|
23
27
|
server.tool("dbx_list_connections", "List all database connections configured in DBX", {}, async () => {
|
|
24
28
|
const connections = await backend.loadConnections();
|
|
@@ -29,12 +33,13 @@ export function createDbxMcpServer(backend, options = {}) {
|
|
|
29
33
|
});
|
|
30
34
|
server.tool("dbx_list_tables", "List tables and views for a database connection", {
|
|
31
35
|
connection_name: z.string().describe("Name of the DBX connection"),
|
|
36
|
+
database: z.string().optional().describe("Database name"),
|
|
32
37
|
schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
|
|
33
|
-
}, async ({ connection_name, schema }) => {
|
|
38
|
+
}, async ({ connection_name, database, schema }) => {
|
|
34
39
|
const config = await backend.findConnection(connection_name);
|
|
35
40
|
if (!config)
|
|
36
|
-
return
|
|
37
|
-
const tables = await backend.listTables(config, schema);
|
|
41
|
+
return toolError("CONNECTION_NOT_FOUND", `Connection "${connection_name}" not found.`);
|
|
42
|
+
const tables = await backend.listTables(withDatabase(config, database), schema);
|
|
38
43
|
if (tables.length === 0)
|
|
39
44
|
return text("No tables found.");
|
|
40
45
|
const rows = tables.map((t) => [t.name, t.type]);
|
|
@@ -43,12 +48,13 @@ export function createDbxMcpServer(backend, options = {}) {
|
|
|
43
48
|
server.tool("dbx_describe_table", "Get column definitions for a table", {
|
|
44
49
|
connection_name: z.string().describe("Name of the DBX connection"),
|
|
45
50
|
table: z.string().describe("Table name"),
|
|
51
|
+
database: z.string().optional().describe("Database name"),
|
|
46
52
|
schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
|
|
47
|
-
}, async ({ connection_name, table, schema }) => {
|
|
53
|
+
}, async ({ connection_name, table, database, schema }) => {
|
|
48
54
|
const config = await backend.findConnection(connection_name);
|
|
49
55
|
if (!config)
|
|
50
|
-
return
|
|
51
|
-
const columns = await backend.describeTable(config, table, schema);
|
|
56
|
+
return toolError("CONNECTION_NOT_FOUND", `Connection "${connection_name}" not found.`);
|
|
57
|
+
const columns = await backend.describeTable(withDatabase(config, database), table, schema);
|
|
52
58
|
if (columns.length === 0)
|
|
53
59
|
return text("No columns found.");
|
|
54
60
|
const rows = columns.map((c) => [
|
|
@@ -62,16 +68,21 @@ export function createDbxMcpServer(backend, options = {}) {
|
|
|
62
68
|
});
|
|
63
69
|
server.tool("dbx_execute_query", "Execute a SQL query on a database connection (max 100 rows returned)", {
|
|
64
70
|
connection_name: z.string().describe("Name of the DBX connection"),
|
|
71
|
+
database: z.string().optional().describe("Database name"),
|
|
65
72
|
sql: z.string().describe("SQL query to execute"),
|
|
66
|
-
}, async ({ connection_name, sql }) => {
|
|
73
|
+
}, async ({ connection_name, database, sql }) => {
|
|
67
74
|
const config = await backend.findConnection(connection_name);
|
|
68
75
|
if (!config)
|
|
69
|
-
return
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
return toolError("CONNECTION_NOT_FOUND", `Connection "${connection_name}" not found.`);
|
|
77
|
+
if (config.db_type !== "mongodb") {
|
|
78
|
+
const safety = evaluateSqlSafety(sql, sqlSafetyFromEnv());
|
|
79
|
+
if (!safety.allowed)
|
|
80
|
+
return toolError("SQL_BLOCKED", safety.reason ?? "SQL blocked.");
|
|
81
|
+
}
|
|
82
|
+
// MongoDB shell commands don't fit the SQL safety evaluator; the backend
|
|
83
|
+
// (node-core executeQuery) applies command-aware read/write gating.
|
|
73
84
|
try {
|
|
74
|
-
const result = await backend.executeQuery(config, sql);
|
|
85
|
+
const result = await backend.executeQuery(withDatabase(config, database), sql);
|
|
75
86
|
if (result.columns.length === 0)
|
|
76
87
|
return text(`Query executed. ${result.row_count} row(s) affected.`);
|
|
77
88
|
const rows = result.rows.map((r) => result.columns.map((c) => formatCell(r[c])));
|
|
@@ -79,30 +90,33 @@ export function createDbxMcpServer(backend, options = {}) {
|
|
|
79
90
|
}
|
|
80
91
|
catch (e) {
|
|
81
92
|
const msg = e instanceof Error ? e.message : String(e);
|
|
82
|
-
return
|
|
93
|
+
return toolError("QUERY_ERROR", msg);
|
|
83
94
|
}
|
|
84
95
|
});
|
|
85
96
|
server.tool("dbx_get_schema_context", "Get compact table and column context for writing SQL", {
|
|
86
97
|
connection_name: z.string().describe("Name of the DBX connection"),
|
|
98
|
+
database: z.string().optional().describe("Database name"),
|
|
87
99
|
schema: z.string().optional().describe("Schema name (default: public for PostgreSQL)"),
|
|
88
100
|
tables: z.array(z.string()).optional().describe("Specific table names to include"),
|
|
89
101
|
max_tables: z.number().int().min(1).max(20).default(8).describe("Maximum number of tables to include"),
|
|
90
|
-
}, async ({ connection_name, schema, tables, max_tables }) => {
|
|
102
|
+
}, async ({ connection_name, database, schema, tables, max_tables }) => {
|
|
91
103
|
const config = await backend.findConnection(connection_name);
|
|
92
104
|
if (!config)
|
|
93
|
-
return
|
|
94
|
-
const context = await buildSchemaContext(backend, config,
|
|
105
|
+
return toolError("CONNECTION_NOT_FOUND", `Connection "${connection_name}" not found.`);
|
|
106
|
+
const context = await buildSchemaContext(backend, withDatabase(config, database), {
|
|
107
|
+
schema,
|
|
108
|
+
tables,
|
|
109
|
+
maxTables: max_tables,
|
|
110
|
+
});
|
|
95
111
|
if (context.tables.length === 0)
|
|
96
112
|
return text("No matching tables found.");
|
|
97
113
|
return text(formatSchemaContext(context));
|
|
98
114
|
});
|
|
99
115
|
server.tool("dbx_add_connection", "Add a new database connection to DBX", {
|
|
100
116
|
name: z.string().describe("Connection name"),
|
|
101
|
-
db_type: z
|
|
102
|
-
.string()
|
|
103
|
-
.describe("Database type: postgres, mysql, sqlite, redis, duckdb, clickhouse, sqlserver, mongodb, oracle, elasticsearch, doris, starrocks, redshift, dameng, kingbase, highgo, vastbase, goldendb, gaussdb, h2, snowflake, trino, hive, db2, informix, neo4j, cassandra, bigquery, kylin, sundb, tdengine, jdbc, access"),
|
|
117
|
+
db_type: z.string().describe(DBX_CONNECTION_TYPE_DESCRIPTION),
|
|
104
118
|
host: z.string().describe("Database host"),
|
|
105
|
-
port: z.number().optional().describe("Database port (TDengine defaults to 6041)"),
|
|
119
|
+
port: z.number().optional().describe("Database port (TDengine defaults to 6041, XuguDB defaults to 5138)"),
|
|
106
120
|
username: z.string().default("").describe("Username"),
|
|
107
121
|
password: z.string().default("").describe("Password"),
|
|
108
122
|
database: z.string().optional().describe("Default database name"),
|
|
@@ -112,7 +126,8 @@ export function createDbxMcpServer(backend, options = {}) {
|
|
|
112
126
|
if (existing)
|
|
113
127
|
return text(`Connection "${name}" already exists.`);
|
|
114
128
|
const FILE_BASED_TYPES = new Set(["sqlite", "duckdb", "access"]);
|
|
115
|
-
const
|
|
129
|
+
const DEFAULT_PORTS = { tdengine: 6041, xugu: 5138 };
|
|
130
|
+
const resolvedPort = port ?? DEFAULT_PORTS[db_type] ?? (FILE_BASED_TYPES.has(db_type) ? 0 : undefined);
|
|
116
131
|
if (resolvedPort === undefined)
|
|
117
132
|
return text("Port is required for this database type.");
|
|
118
133
|
const config = await backend.addConnection({
|
|
@@ -134,7 +149,7 @@ export function createDbxMcpServer(backend, options = {}) {
|
|
|
134
149
|
}, async ({ connection_name }) => {
|
|
135
150
|
const removed = await backend.removeConnection(connection_name);
|
|
136
151
|
if (!removed)
|
|
137
|
-
return
|
|
152
|
+
return toolError("CONNECTION_NOT_FOUND", `Connection "${connection_name}" not found.`);
|
|
138
153
|
await notifyReload();
|
|
139
154
|
return text(`Connection "${connection_name}" removed.`);
|
|
140
155
|
});
|
|
@@ -146,6 +161,9 @@ export function createDbxMcpServer(backend, options = {}) {
|
|
|
146
161
|
database: z.string().optional().describe("Database name"),
|
|
147
162
|
schema: z.string().optional().describe("Schema name"),
|
|
148
163
|
}, async ({ connection_name, table, database, schema }) => {
|
|
164
|
+
const config = await backend.findConnection(connection_name);
|
|
165
|
+
if (!config)
|
|
166
|
+
return toolError("CONNECTION_NOT_FOUND", `Connection "${connection_name}" not found.`);
|
|
149
167
|
return bridgeRequest("/open-table", { connection_name, table, database, schema }, `Opened ${table} in DBX`);
|
|
150
168
|
});
|
|
151
169
|
server.tool("dbx_execute_and_show", "Execute a SQL query in DBX desktop app UI and show results there. Requires DBX to be running.", {
|
|
@@ -153,26 +171,42 @@ export function createDbxMcpServer(backend, options = {}) {
|
|
|
153
171
|
sql: z.string().describe("SQL query to execute"),
|
|
154
172
|
database: z.string().optional().describe("Database name"),
|
|
155
173
|
}, async ({ connection_name, sql, database }) => {
|
|
156
|
-
const
|
|
157
|
-
if (!
|
|
158
|
-
return
|
|
159
|
-
|
|
174
|
+
const config = await backend.findConnection(connection_name);
|
|
175
|
+
if (!config)
|
|
176
|
+
return toolError("CONNECTION_NOT_FOUND", `Connection "${connection_name}" not found.`);
|
|
177
|
+
const safetyOptions = sqlSafetyFromEnv();
|
|
178
|
+
if (config?.db_type === "mongodb") {
|
|
179
|
+
const aggregate = parseMongoAggregateCommand(sql);
|
|
180
|
+
if (aggregate) {
|
|
181
|
+
const safety = evaluateMongoAggregateSafety(aggregate, safetyOptions);
|
|
182
|
+
if (!safety.allowed)
|
|
183
|
+
return toolError("SQL_BLOCKED", safety.reason ?? "Query blocked.");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
const safety = evaluateSqlSafety(sql, safetyOptions);
|
|
188
|
+
if (!safety.allowed)
|
|
189
|
+
return toolError("SQL_BLOCKED", safety.reason ?? "SQL blocked.");
|
|
190
|
+
}
|
|
191
|
+
// MongoDB shell commands bypass the SQL safety evaluator; pass MCP
|
|
192
|
+
// safety flags to the desktop executor for command-aware gating.
|
|
193
|
+
return bridgeRequest("/execute-query", {
|
|
194
|
+
connection_name,
|
|
195
|
+
sql,
|
|
196
|
+
database,
|
|
197
|
+
allow_writes: safetyOptions.allowWrites,
|
|
198
|
+
allow_dangerous: safetyOptions.allowDangerous,
|
|
199
|
+
}, "Query sent to DBX");
|
|
160
200
|
});
|
|
161
201
|
}
|
|
162
202
|
return server;
|
|
163
203
|
}
|
|
164
|
-
function formatCell(value) {
|
|
165
|
-
if (value === null || value === undefined)
|
|
166
|
-
return "NULL";
|
|
167
|
-
if (typeof value === "object")
|
|
168
|
-
return JSON.stringify(value);
|
|
169
|
-
return String(value);
|
|
170
|
-
}
|
|
171
204
|
async function bridgeRequest(path, body, successMsg) {
|
|
172
205
|
const res = await postBridge(path, body);
|
|
173
206
|
if (res.ok)
|
|
174
207
|
return text(successMsg);
|
|
175
|
-
|
|
208
|
+
const message = res.text.startsWith("DBX is not running") ? res.text : `Failed: ${res.text}`;
|
|
209
|
+
return toolError("DBX_NOT_RUNNING", message);
|
|
176
210
|
}
|
|
177
211
|
async function main() {
|
|
178
212
|
const backend = await createBackend();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dbx-app/mcp-server",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"mcpName": "io.github.t8y2/dbx",
|
|
5
5
|
"description": "MCP server for DBX — query databases from Claude Code, Cursor, and other AI agents",
|
|
6
6
|
"type": "module",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
36
36
|
"zod": "^3.25.20",
|
|
37
|
-
"@dbx-app/node-core": "^0.4.
|
|
37
|
+
"@dbx-app/node-core": "^0.4.4"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.15.21",
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/t8y2/dbx",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.4.
|
|
9
|
+
"version": "0.4.4",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@dbx-app/mcp-server",
|
|
14
|
-
"version": "0.4.
|
|
14
|
+
"version": "0.4.4",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
}
|