@dbx-app/mcp-server 0.1.1 → 0.1.3
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/dist/index.js +64 -28
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -2,22 +2,31 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { homedir, platform } from "node:os";
|
|
5
8
|
import { loadConnections, findConnection } from "./connections.js";
|
|
6
9
|
import { listTables, describeTable, executeQuery } from "./database.js";
|
|
10
|
+
function text(s) {
|
|
11
|
+
return { content: [{ type: "text", text: s }] };
|
|
12
|
+
}
|
|
13
|
+
function mdTable(headers, rows) {
|
|
14
|
+
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || "").length), 3));
|
|
15
|
+
const header = `| ${headers.map((h, i) => h.padEnd(widths[i])).join(" | ")} |`;
|
|
16
|
+
const sep = `| ${widths.map((w) => "-".repeat(w)).join(" | ")} |`;
|
|
17
|
+
const body = rows.map((r) => `| ${r.map((c, i) => (c || "").padEnd(widths[i])).join(" | ")} |`).join("\n");
|
|
18
|
+
return `${header}\n${sep}\n${body}`;
|
|
19
|
+
}
|
|
7
20
|
const server = new McpServer({
|
|
8
21
|
name: "dbx",
|
|
9
|
-
version: "0.1.
|
|
22
|
+
version: "0.1.1",
|
|
10
23
|
});
|
|
11
24
|
server.tool("dbx_list_connections", "List all database connections configured in DBX", {}, async () => {
|
|
12
25
|
const connections = await loadConnections();
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
port: c.port,
|
|
18
|
-
database: c.database || "",
|
|
19
|
-
}));
|
|
20
|
-
return { content: [{ type: "text", text: JSON.stringify(list, null, 2) }] };
|
|
26
|
+
if (connections.length === 0)
|
|
27
|
+
return text("No connections configured in DBX.");
|
|
28
|
+
const rows = connections.map((c) => [c.name, c.db_type, c.host, String(c.port), c.database || ""]);
|
|
29
|
+
return text(mdTable(["Name", "Type", "Host", "Port", "Database"], rows));
|
|
21
30
|
});
|
|
22
31
|
server.tool("dbx_list_tables", "List tables and views for a database connection", {
|
|
23
32
|
connection_name: z.string().describe("Name of the DBX connection"),
|
|
@@ -25,9 +34,12 @@ server.tool("dbx_list_tables", "List tables and views for a database connection"
|
|
|
25
34
|
}, async ({ connection_name, schema }) => {
|
|
26
35
|
const config = await findConnection(connection_name);
|
|
27
36
|
if (!config)
|
|
28
|
-
return
|
|
37
|
+
return text(`Connection "${connection_name}" not found`);
|
|
29
38
|
const tables = await listTables(config, schema);
|
|
30
|
-
|
|
39
|
+
if (tables.length === 0)
|
|
40
|
+
return text("No tables found.");
|
|
41
|
+
const rows = tables.map((t) => [t.name, t.type]);
|
|
42
|
+
return text(mdTable(["Table", "Type"], rows));
|
|
31
43
|
});
|
|
32
44
|
server.tool("dbx_describe_table", "Get column definitions for a table", {
|
|
33
45
|
connection_name: z.string().describe("Name of the DBX connection"),
|
|
@@ -36,9 +48,18 @@ server.tool("dbx_describe_table", "Get column definitions for a table", {
|
|
|
36
48
|
}, async ({ connection_name, table, schema }) => {
|
|
37
49
|
const config = await findConnection(connection_name);
|
|
38
50
|
if (!config)
|
|
39
|
-
return
|
|
51
|
+
return text(`Connection "${connection_name}" not found`);
|
|
40
52
|
const columns = await describeTable(config, table, schema);
|
|
41
|
-
|
|
53
|
+
if (columns.length === 0)
|
|
54
|
+
return text("No columns found.");
|
|
55
|
+
const rows = columns.map((c) => [
|
|
56
|
+
c.is_primary_key ? `${c.name} (PK)` : c.name,
|
|
57
|
+
c.data_type,
|
|
58
|
+
c.is_nullable ? "YES" : "NO",
|
|
59
|
+
c.column_default ?? "",
|
|
60
|
+
c.comment ?? "",
|
|
61
|
+
]);
|
|
62
|
+
return text(mdTable(["Column", "Type", "Nullable", "Default", "Comment"], rows));
|
|
42
63
|
});
|
|
43
64
|
server.tool("dbx_execute_query", "Execute a SQL query on a database connection (max 100 rows returned)", {
|
|
44
65
|
connection_name: z.string().describe("Name of the DBX connection"),
|
|
@@ -46,19 +67,26 @@ server.tool("dbx_execute_query", "Execute a SQL query on a database connection (
|
|
|
46
67
|
}, async ({ connection_name, sql }) => {
|
|
47
68
|
const config = await findConnection(connection_name);
|
|
48
69
|
if (!config)
|
|
49
|
-
return
|
|
70
|
+
return text(`Connection "${connection_name}" not found`);
|
|
50
71
|
try {
|
|
51
72
|
const result = await executeQuery(config, sql);
|
|
52
|
-
|
|
73
|
+
if (result.columns.length === 0)
|
|
74
|
+
return text(`Query executed. ${result.row_count} row(s) affected.`);
|
|
75
|
+
const rows = result.rows.map((r) => result.columns.map((c) => formatCell(r[c])));
|
|
76
|
+
return text(`${mdTable(result.columns, rows)}\n\n${result.row_count} row(s)`);
|
|
53
77
|
}
|
|
54
78
|
catch (e) {
|
|
55
79
|
const msg = e instanceof Error ? e.message : String(e);
|
|
56
|
-
return
|
|
80
|
+
return text(`Query error: ${msg}`);
|
|
57
81
|
}
|
|
58
82
|
});
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
83
|
+
function formatCell(value) {
|
|
84
|
+
if (value === null || value === undefined)
|
|
85
|
+
return "NULL";
|
|
86
|
+
if (typeof value === "object")
|
|
87
|
+
return JSON.stringify(value);
|
|
88
|
+
return String(value);
|
|
89
|
+
}
|
|
62
90
|
function appDataDir() {
|
|
63
91
|
const home = homedir();
|
|
64
92
|
switch (platform()) {
|
|
@@ -81,23 +109,31 @@ server.tool("dbx_open_table", "Open a table in DBX desktop app UI. Requires DBX
|
|
|
81
109
|
database: z.string().optional().describe("Database name"),
|
|
82
110
|
schema: z.string().optional().describe("Schema name"),
|
|
83
111
|
}, async ({ connection_name, table, database, schema }) => {
|
|
112
|
+
return bridgeRequest("/open-table", { connection_name, table, database, schema }, `Opened ${table} in DBX`);
|
|
113
|
+
});
|
|
114
|
+
server.tool("dbx_execute_and_show", "Execute a SQL query in DBX desktop app UI and show results there. Requires DBX to be running.", {
|
|
115
|
+
connection_name: z.string().describe("Name of the DBX connection"),
|
|
116
|
+
sql: z.string().describe("SQL query to execute"),
|
|
117
|
+
database: z.string().optional().describe("Database name"),
|
|
118
|
+
}, async ({ connection_name, sql, database }) => {
|
|
119
|
+
return bridgeRequest("/execute-query", { connection_name, sql, database }, "Query sent to DBX");
|
|
120
|
+
});
|
|
121
|
+
async function bridgeRequest(path, body, successMsg) {
|
|
84
122
|
try {
|
|
85
123
|
const bridgeUrl = await getBridgeUrl();
|
|
86
|
-
const res = await fetch(`${bridgeUrl}
|
|
124
|
+
const res = await fetch(`${bridgeUrl}${path}`, {
|
|
87
125
|
method: "POST",
|
|
88
126
|
headers: { "Content-Type": "application/json" },
|
|
89
|
-
body: JSON.stringify(
|
|
127
|
+
body: JSON.stringify(body),
|
|
90
128
|
});
|
|
91
|
-
if (res.ok)
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
const text = await res.text();
|
|
95
|
-
return { content: [{ type: "text", text: `Failed: ${text}` }] };
|
|
129
|
+
if (res.ok)
|
|
130
|
+
return text(successMsg);
|
|
131
|
+
return text(`Failed: ${await res.text()}`);
|
|
96
132
|
}
|
|
97
133
|
catch {
|
|
98
|
-
return
|
|
134
|
+
return text("DBX is not running. Please start DBX first.");
|
|
99
135
|
}
|
|
100
|
-
}
|
|
136
|
+
}
|
|
101
137
|
async function main() {
|
|
102
138
|
const transport = new StdioServerTransport();
|
|
103
139
|
await server.connect(transport);
|
package/package.json
CHANGED