@johnroshan/universal-db-mcp 1.0.0

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 (4) hide show
  1. package/LICENCE +21 -0
  2. package/README.md +146 -0
  3. package/index.js +368 -0
  4. package/package.json +42 -0
package/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 [John Roshan]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # Universal Database MCP Server
2
+
3
+ Model Context Protocol server for PostgreSQL and MySQL databases. Enables Claude to query and manage your databases.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @johnroshan/universal-db-mcp
9
+ ```
10
+
11
+ ## Configuration
12
+
13
+ Edit Claude Desktop config file:
14
+
15
+ macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
16
+ Windows: `%APPDATA%\Claude\claude_desktop_config.json`
17
+
18
+ ### PostgreSQL
19
+
20
+ ```json
21
+ {
22
+ "mcpServers": {
23
+ "postgres": {
24
+ "command": "npx",
25
+ "args": ["-y", "@johnroshan/universal-db-mcp"],
26
+ "env": {
27
+ "DB_TYPE": "postgres",
28
+ "DATABASE_URL": "postgresql://user:password@localhost:5432/mydb"
29
+ }
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ Or use individual parameters:
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "postgres": {
41
+ "command": "npx",
42
+ "args": ["-y", "@johnroshan/universal-db-mcp"],
43
+ "env": {
44
+ "DB_TYPE": "postgres",
45
+ "DB_HOST": "localhost",
46
+ "DB_PORT": "5432",
47
+ "DB_NAME": "mydb",
48
+ "DB_USER": "postgres",
49
+ "DB_PASSWORD": "your_password"
50
+ }
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ ### MySQL
57
+
58
+ ```json
59
+ {
60
+ "mcpServers": {
61
+ "mysql": {
62
+ "command": "npx",
63
+ "args": ["-y", "@johnroshan/universal-db-mcp"],
64
+ "env": {
65
+ "DB_TYPE": "mysql",
66
+ "DATABASE_URL": "mysql://user:password@localhost:3306/mydb"
67
+ }
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ ## Available Tools
74
+
75
+ - `query` - Execute SQL queries
76
+ - `list_tables` - List all tables
77
+ - `describe_table` - Get table schema
78
+ - `list_databases` - List databases
79
+ - `table_row_count` - Count rows in table
80
+ - `search_table` - Search records with pattern matching
81
+
82
+ ## Usage Examples
83
+
84
+ After configuration, ask Claude:
85
+
86
+ - "Show me all tables in my database"
87
+ - "What is the schema of the users table"
88
+ - "How many rows are in the orders table"
89
+ - "Find all users with gmail addresses"
90
+ - "Show me the last 10 orders"
91
+
92
+ ## Environment Variables
93
+
94
+ | Variable | Required | Description |
95
+ |----------|----------|-------------|
96
+ | DB_TYPE | Yes | postgres or mysql |
97
+ | DATABASE_URL | No | Full connection string |
98
+ | DB_HOST | No | Database host |
99
+ | DB_PORT | No | Database port |
100
+ | DB_NAME | No | Database name |
101
+ | DB_USER | No | Database user |
102
+ | DB_PASSWORD | No | Database password |
103
+
104
+ Either DATABASE_URL or DB_NAME + DB_USER is required.
105
+
106
+ ## Security
107
+
108
+ This server runs locally on your machine. Database credentials remain on your device.
109
+
110
+ Consider creating a read-only database user:
111
+
112
+ PostgreSQL:
113
+ ```sql
114
+ CREATE USER claude_readonly WITH PASSWORD 'password';
115
+ GRANT CONNECT ON DATABASE mydb TO claude_readonly;
116
+ GRANT USAGE ON SCHEMA public TO claude_readonly;
117
+ GRANT SELECT ON ALL TABLES IN SCHEMA public TO claude_readonly;
118
+ ```
119
+
120
+ MySQL:
121
+ ```sql
122
+ CREATE USER 'claude_readonly'@'localhost' IDENTIFIED BY 'password';
123
+ GRANT SELECT ON mydb.* TO 'claude_readonly'@'localhost';
124
+ FLUSH PRIVILEGES;
125
+ ```
126
+
127
+ ## Troubleshooting
128
+
129
+ Connection refused:
130
+ - Verify database is running
131
+ - Check host and port
132
+ - Check firewall settings
133
+
134
+ Authentication failed:
135
+ - Verify credentials
136
+ - Check user permissions
137
+ - For PostgreSQL check pg_hba.conf
138
+
139
+ Claude does not see server:
140
+ - Restart Claude Desktop
141
+ - Check config file syntax
142
+ - Check logs at ~/Library/Logs/Claude/mcp*.log
143
+
144
+ ## License
145
+
146
+ MIT
package/index.js ADDED
@@ -0,0 +1,368 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import {
6
+ CallToolRequestSchema,
7
+ ListToolsRequestSchema,
8
+ } from "@modelcontextprotocol/sdk/types.js";
9
+ import pg from "pg";
10
+ import mysql from "mysql2/promise";
11
+
12
+ const { Client: PgClient } = pg;
13
+
14
+ const DB_TYPE = process.env.DB_TYPE;
15
+ const DB_HOST = process.env.DB_HOST || "localhost";
16
+ const DB_PORT = process.env.DB_PORT;
17
+ const DB_NAME = process.env.DB_NAME;
18
+ const DB_USER = process.env.DB_USER;
19
+ const DB_PASSWORD = process.env.DB_PASSWORD;
20
+ const DATABASE_URL = process.env.DATABASE_URL;
21
+
22
+ if (!DB_TYPE) {
23
+ console.error("Error: DB_TYPE environment variable required (postgres or mysql)");
24
+ process.exit(1);
25
+ }
26
+
27
+ if (!DATABASE_URL && (!DB_NAME || !DB_USER)) {
28
+ console.error("Error: Either DATABASE_URL or DB_NAME, DB_USER required");
29
+ process.exit(1);
30
+ }
31
+
32
+ const server = new Server(
33
+ {
34
+ name: "universal-db-mcp",
35
+ version: "1.0.0",
36
+ },
37
+ {
38
+ capabilities: {
39
+ tools: {},
40
+ },
41
+ }
42
+ );
43
+
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
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
92
+ return {
93
+ tools: [
94
+ {
95
+ name: "query",
96
+ description: "Execute a SQL query on the database. Returns results as JSON.",
97
+ inputSchema: {
98
+ type: "object",
99
+ properties: {
100
+ sql: {
101
+ type: "string",
102
+ description: "SQL query to execute",
103
+ },
104
+ },
105
+ required: ["sql"],
106
+ },
107
+ },
108
+ {
109
+ name: "list_tables",
110
+ description: "List all tables in the current database",
111
+ inputSchema: {
112
+ type: "object",
113
+ properties: {},
114
+ },
115
+ },
116
+ {
117
+ name: "describe_table",
118
+ description: "Get detailed schema information for a specific table",
119
+ inputSchema: {
120
+ type: "object",
121
+ properties: {
122
+ table: {
123
+ type: "string",
124
+ description: "Name of the table to describe",
125
+ },
126
+ },
127
+ required: ["table"],
128
+ },
129
+ },
130
+ {
131
+ name: "list_databases",
132
+ description: "List all available databases on the server",
133
+ inputSchema: {
134
+ type: "object",
135
+ properties: {},
136
+ },
137
+ },
138
+ {
139
+ name: "table_row_count",
140
+ description: "Get the number of rows in a table",
141
+ inputSchema: {
142
+ type: "object",
143
+ properties: {
144
+ table: {
145
+ type: "string",
146
+ description: "Table name",
147
+ },
148
+ },
149
+ required: ["table"],
150
+ },
151
+ },
152
+ {
153
+ name: "search_table",
154
+ description: "Search for records in a table matching a condition",
155
+ inputSchema: {
156
+ type: "object",
157
+ properties: {
158
+ table: {
159
+ type: "string",
160
+ description: "Table name",
161
+ },
162
+ column: {
163
+ type: "string",
164
+ description: "Column to search in",
165
+ },
166
+ value: {
167
+ type: "string",
168
+ description: "Value to search for",
169
+ },
170
+ limit: {
171
+ type: "number",
172
+ description: "Maximum number of results",
173
+ },
174
+ },
175
+ required: ["table", "column", "value"],
176
+ },
177
+ },
178
+ ],
179
+ };
180
+ });
181
+
182
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
183
+ const { name, arguments: args } = request.params;
184
+ let conn;
185
+
186
+ try {
187
+ conn = await getConnection();
188
+
189
+ if (name === "query") {
190
+ const results = await conn.query(args.sql);
191
+ return {
192
+ content: [
193
+ {
194
+ type: "text",
195
+ text: JSON.stringify(
196
+ {
197
+ rowCount: results.length,
198
+ rows: results,
199
+ },
200
+ null,
201
+ 2
202
+ ),
203
+ },
204
+ ],
205
+ };
206
+ }
207
+
208
+ 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
+ }
225
+ return {
226
+ content: [
227
+ {
228
+ type: "text",
229
+ text: JSON.stringify(results, null, 2),
230
+ },
231
+ ],
232
+ };
233
+ }
234
+
235
+ 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
+ }
262
+ return {
263
+ content: [
264
+ {
265
+ type: "text",
266
+ text: JSON.stringify(results, null, 2),
267
+ },
268
+ ],
269
+ };
270
+ }
271
+
272
+ 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
+ }
284
+ return {
285
+ content: [
286
+ {
287
+ type: "text",
288
+ text: JSON.stringify(results, null, 2),
289
+ },
290
+ ],
291
+ };
292
+ }
293
+
294
+ if (name === "table_row_count") {
295
+ const results = await conn.query(
296
+ `SELECT COUNT(*) as count FROM ${args.table}`
297
+ );
298
+ return {
299
+ content: [
300
+ {
301
+ type: "text",
302
+ text: JSON.stringify(results[0], null, 2),
303
+ },
304
+ ],
305
+ };
306
+ }
307
+
308
+ 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
+
324
+ return {
325
+ content: [
326
+ {
327
+ type: "text",
328
+ text: JSON.stringify(
329
+ {
330
+ matchCount: results.length,
331
+ results: results,
332
+ },
333
+ null,
334
+ 2
335
+ ),
336
+ },
337
+ ],
338
+ };
339
+ }
340
+
341
+ throw new Error(`Unknown tool: ${name}`);
342
+ } catch (error) {
343
+ return {
344
+ content: [
345
+ {
346
+ type: "text",
347
+ text: `Error: ${error.message}`,
348
+ },
349
+ ],
350
+ isError: true,
351
+ };
352
+ } finally {
353
+ if (conn) {
354
+ await conn.close();
355
+ }
356
+ }
357
+ });
358
+
359
+ async function main() {
360
+ const transport = new StdioServerTransport();
361
+ await server.connect(transport);
362
+ console.error(`Universal Database MCP Server running (${DB_TYPE})`);
363
+ }
364
+
365
+ main().catch((error) => {
366
+ console.error("Fatal error:", error);
367
+ process.exit(1);
368
+ });
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@johnroshan/universal-db-mcp",
3
+ "version": "1.0.0",
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
+ "scripts": {
11
+ "start": "node index.js"
12
+ },
13
+ "keywords": [
14
+ "mcp",
15
+ "model-context-protocol",
16
+ "claude",
17
+ "database",
18
+ "postgresql",
19
+ "mysql",
20
+ "sql",
21
+ "ai",
22
+ "llm"
23
+ ],
24
+ "author": "John Roshan <johnroshan2255@gmail.com>",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/johnroshan2255/universal-db-mcp.git"
29
+ },
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "files": ["index.js", "README.md", "LICENCE"],
34
+ "dependencies": {
35
+ "@modelcontextprotocol/sdk": "^1.25.1",
36
+ "pg": "^8.13.1",
37
+ "mysql2": "^3.11.5"
38
+ },
39
+ "devDependencies": {
40
+ "eslint": "^9.17.0"
41
+ }
42
+ }