@johnroshan/universal-db-mcp 1.0.0 → 1.0.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/index.js +33 -127
- package/lib.js +214 -0
- package/package.json +51 -41
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -6,10 +6,15 @@ import {
|
|
|
6
6
|
CallToolRequestSchema,
|
|
7
7
|
ListToolsRequestSchema,
|
|
8
8
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
import {
|
|
10
|
+
createConnection,
|
|
11
|
+
query,
|
|
12
|
+
listTables,
|
|
13
|
+
describeTable,
|
|
14
|
+
listDatabases,
|
|
15
|
+
getTableRowCount,
|
|
16
|
+
searchTable,
|
|
17
|
+
} from "./lib.js";
|
|
13
18
|
|
|
14
19
|
const DB_TYPE = process.env.DB_TYPE;
|
|
15
20
|
const DB_HOST = process.env.DB_HOST || "localhost";
|
|
@@ -32,7 +37,7 @@ if (!DATABASE_URL && (!DB_NAME || !DB_USER)) {
|
|
|
32
37
|
const server = new Server(
|
|
33
38
|
{
|
|
34
39
|
name: "universal-db-mcp",
|
|
35
|
-
version: "1.0.
|
|
40
|
+
version: "1.0.1",
|
|
36
41
|
},
|
|
37
42
|
{
|
|
38
43
|
capabilities: {
|
|
@@ -41,53 +46,6 @@ const server = new Server(
|
|
|
41
46
|
}
|
|
42
47
|
);
|
|
43
48
|
|
|
44
|
-
async function getConnection() {
|
|
45
|
-
if (DB_TYPE === "postgres") {
|
|
46
|
-
const client = new PgClient(
|
|
47
|
-
DATABASE_URL || {
|
|
48
|
-
host: DB_HOST,
|
|
49
|
-
port: DB_PORT || 5432,
|
|
50
|
-
database: DB_NAME,
|
|
51
|
-
user: DB_USER,
|
|
52
|
-
password: DB_PASSWORD,
|
|
53
|
-
}
|
|
54
|
-
);
|
|
55
|
-
await client.connect();
|
|
56
|
-
return {
|
|
57
|
-
client,
|
|
58
|
-
async query(sql, params = []) {
|
|
59
|
-
const result = await client.query(sql, params);
|
|
60
|
-
return result.rows;
|
|
61
|
-
},
|
|
62
|
-
async close() {
|
|
63
|
-
await client.end();
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
} else if (DB_TYPE === "mysql") {
|
|
67
|
-
const connection = await mysql.createConnection(
|
|
68
|
-
DATABASE_URL || {
|
|
69
|
-
host: DB_HOST,
|
|
70
|
-
port: DB_PORT || 3306,
|
|
71
|
-
database: DB_NAME,
|
|
72
|
-
user: DB_USER,
|
|
73
|
-
password: DB_PASSWORD,
|
|
74
|
-
}
|
|
75
|
-
);
|
|
76
|
-
return {
|
|
77
|
-
client: connection,
|
|
78
|
-
async query(sql, params = []) {
|
|
79
|
-
const [rows] = await connection.execute(sql, params);
|
|
80
|
-
return rows;
|
|
81
|
-
},
|
|
82
|
-
async close() {
|
|
83
|
-
await connection.end();
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
} else {
|
|
87
|
-
throw new Error(`Unsupported database type: ${DB_TYPE}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
49
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
92
50
|
return {
|
|
93
51
|
tools: [
|
|
@@ -184,10 +142,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
184
142
|
let conn;
|
|
185
143
|
|
|
186
144
|
try {
|
|
187
|
-
conn = await
|
|
145
|
+
conn = await createConnection({
|
|
146
|
+
DB_TYPE,
|
|
147
|
+
DATABASE_URL,
|
|
148
|
+
DB_HOST,
|
|
149
|
+
DB_PORT,
|
|
150
|
+
DB_NAME,
|
|
151
|
+
DB_USER,
|
|
152
|
+
DB_PASSWORD,
|
|
153
|
+
});
|
|
188
154
|
|
|
189
155
|
if (name === "query") {
|
|
190
|
-
const results = await
|
|
156
|
+
const results = await query(conn, args.sql);
|
|
191
157
|
return {
|
|
192
158
|
content: [
|
|
193
159
|
{
|
|
@@ -206,22 +172,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
206
172
|
}
|
|
207
173
|
|
|
208
174
|
if (name === "list_tables") {
|
|
209
|
-
|
|
210
|
-
if (DB_TYPE === "postgres") {
|
|
211
|
-
results = await conn.query(`
|
|
212
|
-
SELECT table_name, table_type
|
|
213
|
-
FROM information_schema.tables
|
|
214
|
-
WHERE table_schema = 'public'
|
|
215
|
-
ORDER BY table_name
|
|
216
|
-
`);
|
|
217
|
-
} else {
|
|
218
|
-
results = await conn.query(`
|
|
219
|
-
SELECT table_name, table_type
|
|
220
|
-
FROM information_schema.tables
|
|
221
|
-
WHERE table_schema = ?
|
|
222
|
-
ORDER BY table_name
|
|
223
|
-
`, [DB_NAME]);
|
|
224
|
-
}
|
|
175
|
+
const results = await listTables(conn);
|
|
225
176
|
return {
|
|
226
177
|
content: [
|
|
227
178
|
{
|
|
@@ -233,32 +184,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
233
184
|
}
|
|
234
185
|
|
|
235
186
|
if (name === "describe_table") {
|
|
236
|
-
|
|
237
|
-
if (DB_TYPE === "postgres") {
|
|
238
|
-
results = await conn.query(`
|
|
239
|
-
SELECT
|
|
240
|
-
column_name,
|
|
241
|
-
data_type,
|
|
242
|
-
character_maximum_length,
|
|
243
|
-
is_nullable,
|
|
244
|
-
column_default
|
|
245
|
-
FROM information_schema.columns
|
|
246
|
-
WHERE table_name = $1
|
|
247
|
-
ORDER BY ordinal_position
|
|
248
|
-
`, [args.table]);
|
|
249
|
-
} else {
|
|
250
|
-
results = await conn.query(`
|
|
251
|
-
SELECT
|
|
252
|
-
column_name,
|
|
253
|
-
data_type,
|
|
254
|
-
character_maximum_length,
|
|
255
|
-
is_nullable,
|
|
256
|
-
column_default
|
|
257
|
-
FROM information_schema.columns
|
|
258
|
-
WHERE table_schema = ? AND table_name = ?
|
|
259
|
-
ORDER BY ordinal_position
|
|
260
|
-
`, [DB_NAME, args.table]);
|
|
261
|
-
}
|
|
187
|
+
const results = await describeTable(conn, args.table);
|
|
262
188
|
return {
|
|
263
189
|
content: [
|
|
264
190
|
{
|
|
@@ -270,17 +196,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
270
196
|
}
|
|
271
197
|
|
|
272
198
|
if (name === "list_databases") {
|
|
273
|
-
|
|
274
|
-
if (DB_TYPE === "postgres") {
|
|
275
|
-
results = await conn.query(`
|
|
276
|
-
SELECT datname as database_name
|
|
277
|
-
FROM pg_database
|
|
278
|
-
WHERE datistemplate = false
|
|
279
|
-
ORDER BY datname
|
|
280
|
-
`);
|
|
281
|
-
} else {
|
|
282
|
-
results = await conn.query(`SHOW DATABASES`);
|
|
283
|
-
}
|
|
199
|
+
const results = await listDatabases(conn);
|
|
284
200
|
return {
|
|
285
201
|
content: [
|
|
286
202
|
{
|
|
@@ -292,35 +208,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
292
208
|
}
|
|
293
209
|
|
|
294
210
|
if (name === "table_row_count") {
|
|
295
|
-
const
|
|
296
|
-
`SELECT COUNT(*) as count FROM ${args.table}`
|
|
297
|
-
);
|
|
211
|
+
const result = await getTableRowCount(conn, args.table);
|
|
298
212
|
return {
|
|
299
213
|
content: [
|
|
300
214
|
{
|
|
301
215
|
type: "text",
|
|
302
|
-
text: JSON.stringify(
|
|
216
|
+
text: JSON.stringify(result, null, 2),
|
|
303
217
|
},
|
|
304
218
|
],
|
|
305
219
|
};
|
|
306
220
|
}
|
|
307
221
|
|
|
308
222
|
if (name === "search_table") {
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
);
|
|
317
|
-
} else {
|
|
318
|
-
results = await conn.query(
|
|
319
|
-
`SELECT * FROM ${args.table} WHERE ${args.column} LIKE ? LIMIT ?`,
|
|
320
|
-
[`%${args.value}%`, limit]
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
|
|
223
|
+
const results = await searchTable(
|
|
224
|
+
conn,
|
|
225
|
+
args.table,
|
|
226
|
+
args.column,
|
|
227
|
+
args.value,
|
|
228
|
+
args.limit
|
|
229
|
+
);
|
|
324
230
|
return {
|
|
325
231
|
content: [
|
|
326
232
|
{
|
package/lib.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
import mysql from "mysql2/promise";
|
|
3
|
+
|
|
4
|
+
const { Client: PgClient } = pg;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create a database connection
|
|
8
|
+
* @param {Object} config - Database configuration
|
|
9
|
+
* @param {string} config.DB_TYPE - Database type ('postgres' or 'mysql')
|
|
10
|
+
* @param {string} config.DATABASE_URL - Connection URL (optional)
|
|
11
|
+
* @param {string} config.DB_HOST - Database host
|
|
12
|
+
* @param {number} config.DB_PORT - Database port
|
|
13
|
+
* @param {string} config.DB_NAME - Database name
|
|
14
|
+
* @param {string} config.DB_USER - Database user
|
|
15
|
+
* @param {string} config.DB_PASSWORD - Database password
|
|
16
|
+
* @returns {Promise<Object>} Connection object with query and close methods
|
|
17
|
+
*/
|
|
18
|
+
export async function createConnection(config) {
|
|
19
|
+
const {
|
|
20
|
+
DB_TYPE,
|
|
21
|
+
DATABASE_URL,
|
|
22
|
+
DB_HOST = "localhost",
|
|
23
|
+
DB_PORT,
|
|
24
|
+
DB_NAME,
|
|
25
|
+
DB_USER,
|
|
26
|
+
DB_PASSWORD,
|
|
27
|
+
} = config;
|
|
28
|
+
|
|
29
|
+
if (!DB_TYPE) {
|
|
30
|
+
throw new Error("DB_TYPE is required (postgres or mysql)");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!DATABASE_URL && (!DB_NAME || !DB_USER)) {
|
|
34
|
+
throw new Error("Either DATABASE_URL or DB_NAME and DB_USER are required");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (DB_TYPE === "postgres") {
|
|
38
|
+
const client = new PgClient(
|
|
39
|
+
DATABASE_URL || {
|
|
40
|
+
host: DB_HOST,
|
|
41
|
+
port: DB_PORT || 5432,
|
|
42
|
+
database: DB_NAME,
|
|
43
|
+
user: DB_USER,
|
|
44
|
+
password: DB_PASSWORD,
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
await client.connect();
|
|
48
|
+
return {
|
|
49
|
+
client,
|
|
50
|
+
dbType: "postgres",
|
|
51
|
+
dbName: DB_NAME,
|
|
52
|
+
async query(sql, params = []) {
|
|
53
|
+
const result = await client.query(sql, params);
|
|
54
|
+
return result.rows;
|
|
55
|
+
},
|
|
56
|
+
async close() {
|
|
57
|
+
await client.end();
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
} else if (DB_TYPE === "mysql") {
|
|
61
|
+
const connection = await mysql.createConnection(
|
|
62
|
+
DATABASE_URL || {
|
|
63
|
+
host: DB_HOST,
|
|
64
|
+
port: DB_PORT || 3306,
|
|
65
|
+
database: DB_NAME,
|
|
66
|
+
user: DB_USER,
|
|
67
|
+
password: DB_PASSWORD,
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
return {
|
|
71
|
+
client: connection,
|
|
72
|
+
dbType: "mysql",
|
|
73
|
+
dbName: DB_NAME,
|
|
74
|
+
async query(sql, params = []) {
|
|
75
|
+
const [rows] = await connection.execute(sql, params);
|
|
76
|
+
return rows;
|
|
77
|
+
},
|
|
78
|
+
async close() {
|
|
79
|
+
await connection.end();
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error(`Unsupported database type: ${DB_TYPE}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Execute a SQL query
|
|
89
|
+
* @param {Object} conn - Database connection
|
|
90
|
+
* @param {string} sql - SQL query
|
|
91
|
+
* @param {Array} params - Query parameters
|
|
92
|
+
* @returns {Promise<Array>} Query results
|
|
93
|
+
*/
|
|
94
|
+
export async function query(conn, sql, params = []) {
|
|
95
|
+
return await conn.query(sql, params);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* List all tables in the database
|
|
100
|
+
* @param {Object} conn - Database connection
|
|
101
|
+
* @returns {Promise<Array>} List of tables
|
|
102
|
+
*/
|
|
103
|
+
export async function listTables(conn) {
|
|
104
|
+
if (conn.dbType === "postgres") {
|
|
105
|
+
return await conn.query(`
|
|
106
|
+
SELECT table_name, table_type
|
|
107
|
+
FROM information_schema.tables
|
|
108
|
+
WHERE table_schema = 'public'
|
|
109
|
+
ORDER BY table_name
|
|
110
|
+
`);
|
|
111
|
+
} else {
|
|
112
|
+
return await conn.query(
|
|
113
|
+
`
|
|
114
|
+
SELECT table_name, table_type
|
|
115
|
+
FROM information_schema.tables
|
|
116
|
+
WHERE table_schema = ?
|
|
117
|
+
ORDER BY table_name
|
|
118
|
+
`,
|
|
119
|
+
[conn.dbName]
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Describe table schema
|
|
126
|
+
* @param {Object} conn - Database connection
|
|
127
|
+
* @param {string} tableName - Table name
|
|
128
|
+
* @returns {Promise<Array>} Table schema information
|
|
129
|
+
*/
|
|
130
|
+
export async function describeTable(conn, tableName) {
|
|
131
|
+
if (conn.dbType === "postgres") {
|
|
132
|
+
return await conn.query(
|
|
133
|
+
`
|
|
134
|
+
SELECT
|
|
135
|
+
column_name,
|
|
136
|
+
data_type,
|
|
137
|
+
character_maximum_length,
|
|
138
|
+
is_nullable,
|
|
139
|
+
column_default
|
|
140
|
+
FROM information_schema.columns
|
|
141
|
+
WHERE table_name = $1
|
|
142
|
+
ORDER BY ordinal_position
|
|
143
|
+
`,
|
|
144
|
+
[tableName]
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
return await conn.query(
|
|
148
|
+
`
|
|
149
|
+
SELECT
|
|
150
|
+
column_name,
|
|
151
|
+
data_type,
|
|
152
|
+
character_maximum_length,
|
|
153
|
+
is_nullable,
|
|
154
|
+
column_default
|
|
155
|
+
FROM information_schema.columns
|
|
156
|
+
WHERE table_schema = ? AND table_name = ?
|
|
157
|
+
ORDER BY ordinal_position
|
|
158
|
+
`,
|
|
159
|
+
[conn.dbName, tableName]
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* List all databases
|
|
166
|
+
* @param {Object} conn - Database connection
|
|
167
|
+
* @returns {Promise<Array>} List of databases
|
|
168
|
+
*/
|
|
169
|
+
export async function listDatabases(conn) {
|
|
170
|
+
if (conn.dbType === "postgres") {
|
|
171
|
+
return await conn.query(`
|
|
172
|
+
SELECT datname as database_name
|
|
173
|
+
FROM pg_database
|
|
174
|
+
WHERE datistemplate = false
|
|
175
|
+
ORDER BY datname
|
|
176
|
+
`);
|
|
177
|
+
} else {
|
|
178
|
+
return await conn.query(`SHOW DATABASES`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get row count for a table
|
|
184
|
+
* @param {Object} conn - Database connection
|
|
185
|
+
* @param {string} tableName - Table name
|
|
186
|
+
* @returns {Promise<Object>} Row count
|
|
187
|
+
*/
|
|
188
|
+
export async function getTableRowCount(conn, tableName) {
|
|
189
|
+
const results = await conn.query(`SELECT COUNT(*) as count FROM ${tableName}`);
|
|
190
|
+
return results[0];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Search table for matching records
|
|
195
|
+
* @param {Object} conn - Database connection
|
|
196
|
+
* @param {string} tableName - Table name
|
|
197
|
+
* @param {string} columnName - Column to search
|
|
198
|
+
* @param {string} value - Search value
|
|
199
|
+
* @param {number} limit - Maximum results (default: 100)
|
|
200
|
+
* @returns {Promise<Array>} Matching records
|
|
201
|
+
*/
|
|
202
|
+
export async function searchTable(conn, tableName, columnName, value, limit = 100) {
|
|
203
|
+
if (conn.dbType === "postgres") {
|
|
204
|
+
return await conn.query(
|
|
205
|
+
`SELECT * FROM ${tableName} WHERE ${columnName}::text ILIKE $1 LIMIT $2`,
|
|
206
|
+
[`%${value}%`, limit]
|
|
207
|
+
);
|
|
208
|
+
} else {
|
|
209
|
+
return await conn.query(
|
|
210
|
+
`SELECT * FROM ${tableName} WHERE ${columnName} LIKE ? LIMIT ?`,
|
|
211
|
+
[`%${value}%`, limit]
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
}
|
package/package.json
CHANGED
|
@@ -1,42 +1,52 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
2
|
+
"name": "@johnroshan/universal-db-mcp",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "MCP server for PostgreSQL and MySQL databases - enables Claude to query your databases",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"universal-db-mcp": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./index.js",
|
|
12
|
+
"./lib": "./lib.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "node index.js"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"model-context-protocol",
|
|
20
|
+
"claude",
|
|
21
|
+
"database",
|
|
22
|
+
"postgresql",
|
|
23
|
+
"mysql",
|
|
24
|
+
"sql",
|
|
25
|
+
"ai",
|
|
26
|
+
"llm"
|
|
27
|
+
],
|
|
28
|
+
"author": "John Roshan <johnroshan2255@gmail.com>",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/johnroshan2255/universal-db-mcp.git"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/johnroshan2255/universal-db-mcp#readme",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"index.js",
|
|
40
|
+
"lib.js",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
46
|
+
"pg": "^8.13.1",
|
|
47
|
+
"mysql2": "^3.11.5"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"eslint": "^9.17.0"
|
|
51
|
+
}
|
|
52
|
+
}
|