@johnroshan/universal-db-mcp 1.0.1 → 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.
Files changed (3) hide show
  1. package/index.js +32 -126
  2. package/lib.js +214 -0
  3. package/package.json +7 -2
package/index.js CHANGED
@@ -6,10 +6,15 @@ import {
6
6
  CallToolRequestSchema,
7
7
  ListToolsRequestSchema,
8
8
  } from "@modelcontextprotocol/sdk/types.js";
9
- import pg from "pg";
10
- import mysql from "mysql2/promise";
11
-
12
- const { Client: PgClient } = pg;
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.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 getConnection();
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 conn.query(args.sql);
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
- let results;
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
- let results;
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
- let results;
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 results = await conn.query(
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(results[0], null, 2),
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 limit = args.limit || 100;
310
- let results;
311
-
312
- if (DB_TYPE === "postgres") {
313
- results = await conn.query(
314
- `SELECT * FROM ${args.table} WHERE ${args.column}::text ILIKE $1 LIMIT $2`,
315
- [`%${args.value}%`, limit]
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,12 +1,16 @@
1
1
  {
2
2
  "name": "@johnroshan/universal-db-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "MCP server for PostgreSQL and MySQL databases - enables Claude to query your databases",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "universal-db-mcp": "./index.js"
9
9
  },
10
+ "exports": {
11
+ ".": "./index.js",
12
+ "./lib": "./lib.js"
13
+ },
10
14
  "scripts": {
11
15
  "start": "node index.js"
12
16
  },
@@ -33,8 +37,9 @@
33
37
  },
34
38
  "files": [
35
39
  "index.js",
40
+ "lib.js",
36
41
  "README.md",
37
- "LICENCE"
42
+ "LICENSE"
38
43
  ],
39
44
  "dependencies": {
40
45
  "@modelcontextprotocol/sdk": "^1.25.1",