@hyqf98/easy_db_mcp_server 1.0.0 → 2.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.
- package/README.md +202 -8
- package/dist/database/base.d.ts +79 -0
- package/dist/database/mysql.d.ts +11 -1
- package/dist/database/mysql.js +336 -18
- package/dist/database/postgresql.d.ts +11 -1
- package/dist/database/postgresql.js +460 -2
- package/dist/database/sqlite.d.ts +11 -1
- package/dist/database/sqlite.js +223 -0
- package/dist/index.js +241 -172
- package/dist/utils/file.d.ts +4 -0
- package/dist/utils/file.js +37 -0
- package/package.json +1 -1
- package/src/database/base.ts +100 -0
- package/src/database/mysql.ts +428 -19
- package/src/database/postgresql.ts +554 -2
- package/src/database/sqlite.ts +282 -0
- package/src/index.ts +310 -195
- package/src/utils/file.ts +46 -0
package/src/index.ts
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import {
|
|
5
|
-
CallToolRequestSchema,
|
|
6
|
-
ListToolsRequestSchema,
|
|
7
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import { z } from 'zod';
|
|
8
5
|
import { loadConfig } from './config.js';
|
|
9
6
|
import { createAdapter } from './database/factory.js';
|
|
10
7
|
import type { DatabaseAdapter } from './database/base.js';
|
|
11
8
|
|
|
12
|
-
// Create server instance
|
|
13
|
-
const server = new
|
|
9
|
+
// Create server instance with new API
|
|
10
|
+
const server = new McpServer(
|
|
14
11
|
{
|
|
15
12
|
name: 'hyqf98@easy_db_mcp_server',
|
|
16
|
-
version: '
|
|
13
|
+
version: '2.0.0',
|
|
17
14
|
},
|
|
18
15
|
{
|
|
19
16
|
capabilities: {
|
|
@@ -24,223 +21,341 @@ const server = new Server(
|
|
|
24
21
|
|
|
25
22
|
// Load configuration and create adapter
|
|
26
23
|
let adapter: DatabaseAdapter;
|
|
24
|
+
let cachedConfig: ReturnType<typeof loadConfig>;
|
|
27
25
|
|
|
28
26
|
try {
|
|
29
|
-
|
|
30
|
-
adapter = createAdapter(
|
|
27
|
+
cachedConfig = loadConfig();
|
|
28
|
+
adapter = createAdapter(cachedConfig);
|
|
31
29
|
await adapter.connect();
|
|
32
|
-
console.error(`Connected to ${
|
|
30
|
+
console.error(`Connected to ${cachedConfig.type} database`);
|
|
33
31
|
} catch (error) {
|
|
34
32
|
console.error('Failed to connect to database:', error);
|
|
35
33
|
process.exit(1);
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
description: 'Database name (optional if EASYDB_DATABASE is set, otherwise required)',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: 'describe_table',
|
|
57
|
-
description: 'Get the structure of a table including columns, types, and constraints. If EASYDB_DATABASE is not set, the database parameter must be provided.',
|
|
58
|
-
inputSchema: {
|
|
59
|
-
type: 'object',
|
|
60
|
-
properties: {
|
|
61
|
-
table: {
|
|
62
|
-
type: 'string',
|
|
63
|
-
description: 'Table name',
|
|
64
|
-
},
|
|
65
|
-
database: {
|
|
66
|
-
type: 'string',
|
|
67
|
-
description: 'Database name (optional if EASYDB_DATABASE is set, otherwise required)',
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
required: ['table'],
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
name: 'execute_query',
|
|
75
|
-
description: 'Execute a SELECT query (read-only). If EASYDB_DATABASE is not set, the database parameter must be provided.',
|
|
76
|
-
inputSchema: {
|
|
77
|
-
type: 'object',
|
|
78
|
-
properties: {
|
|
79
|
-
sql: {
|
|
80
|
-
type: 'string',
|
|
81
|
-
description: 'SQL SELECT query to execute',
|
|
82
|
-
},
|
|
83
|
-
database: {
|
|
84
|
-
type: 'string',
|
|
85
|
-
description: 'Database name (optional if EASYDB_DATABASE is set, otherwise required)',
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
required: ['sql'],
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
name: 'execute_sql',
|
|
93
|
-
description: 'Execute any SQL statement (requires EASYDB_ALLOW_WRITE=true). If EASYDB_DATABASE is not set, the database parameter must be provided.',
|
|
94
|
-
inputSchema: {
|
|
95
|
-
type: 'object',
|
|
96
|
-
properties: {
|
|
97
|
-
sql: {
|
|
98
|
-
type: 'string',
|
|
99
|
-
description: 'SQL statement to execute (INSERT, UPDATE, DELETE, DDL, etc.)',
|
|
100
|
-
},
|
|
101
|
-
database: {
|
|
102
|
-
type: 'string',
|
|
103
|
-
description: 'Database name (optional if EASYDB_DATABASE is set, otherwise required)',
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
required: ['sql'],
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
],
|
|
110
|
-
};
|
|
111
|
-
});
|
|
36
|
+
// Helper function to validate database parameter
|
|
37
|
+
const getDatabase = (paramDb?: string): string => {
|
|
38
|
+
if (paramDb) {
|
|
39
|
+
return paramDb;
|
|
40
|
+
}
|
|
41
|
+
if (cachedConfig.database) {
|
|
42
|
+
return cachedConfig.database;
|
|
43
|
+
}
|
|
44
|
+
throw new Error(
|
|
45
|
+
'Database name is required. Either set EASYDB_DATABASE environment variable or pass the database parameter in the tool call.'
|
|
46
|
+
);
|
|
47
|
+
};
|
|
112
48
|
|
|
113
|
-
//
|
|
114
|
-
server.
|
|
115
|
-
|
|
116
|
-
|
|
49
|
+
// Register existing tools with new API
|
|
50
|
+
server.registerTool(
|
|
51
|
+
'list_tables',
|
|
52
|
+
{
|
|
53
|
+
title: 'List Tables',
|
|
54
|
+
description: 'List all tables in the database. If EASYDB_DATABASE is not set, the database parameter must be provided.',
|
|
55
|
+
inputSchema: {
|
|
56
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set, otherwise required)'),
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
async ({ database }) => {
|
|
60
|
+
const dbName = getDatabase(database);
|
|
61
|
+
const tables = await adapter.listTables(dbName);
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: 'text', text: JSON.stringify(tables, null, 2) }],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
);
|
|
117
67
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
68
|
+
server.registerTool(
|
|
69
|
+
'describe_table',
|
|
70
|
+
{
|
|
71
|
+
title: 'Describe Table',
|
|
72
|
+
description: 'Get the structure of a table including columns, types, and constraints. If EASYDB_DATABASE is not set, the database parameter must be provided.',
|
|
73
|
+
inputSchema: {
|
|
74
|
+
table: z.string().describe('Table name'),
|
|
75
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set, otherwise required)'),
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
async ({ table, database }) => {
|
|
79
|
+
const dbName = getDatabase(database);
|
|
80
|
+
const columns = await adapter.describeTable(table, dbName);
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: 'text', text: JSON.stringify(columns, null, 2) }],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
server.registerTool(
|
|
88
|
+
'execute_query',
|
|
89
|
+
{
|
|
90
|
+
title: 'Execute Query',
|
|
91
|
+
description: 'Execute a SELECT query (read-only). If EASYDB_DATABASE is not set, the database parameter must be provided.',
|
|
92
|
+
inputSchema: {
|
|
93
|
+
sql: z.string().describe('SQL SELECT query to execute'),
|
|
94
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set, otherwise required)'),
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
async ({ sql, database }) => {
|
|
98
|
+
const dbName = getDatabase(database);
|
|
99
|
+
const result = await adapter.executeQuery(sql, dbName);
|
|
100
|
+
return {
|
|
101
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
server.registerTool(
|
|
107
|
+
'execute_sql',
|
|
108
|
+
{
|
|
109
|
+
title: 'Execute SQL',
|
|
110
|
+
description: 'Execute any SQL statement (requires EASYDB_ALLOW_WRITE=true). If EASYDB_DATABASE is not set, the database parameter must be provided.',
|
|
111
|
+
inputSchema: {
|
|
112
|
+
sql: z.string().describe('SQL statement to execute (INSERT, UPDATE, DELETE, DDL, etc.)'),
|
|
113
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set, otherwise required)'),
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
async ({ sql, database }) => {
|
|
117
|
+
if (!cachedConfig.allowWrite) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
'SQL execution requires EASYDB_ALLOW_WRITE=true for safety. Please enable this environment variable if you want to allow write operations.'
|
|
120
|
+
);
|
|
122
121
|
}
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
|
|
123
|
+
// Improved DDL detection using regex to handle various formats
|
|
124
|
+
const trimmed = sql.trim().toUpperCase();
|
|
125
|
+
const ddlPattern = /^(CREATE|DROP|ALTER|TRUNCATE|RENAME)\s+(TABLE|INDEX|DATABASE|SCHEMA|VIEW|PROCEDURE|FUNCTION|TRIGGER)/;
|
|
126
|
+
const isDDL = ddlPattern.test(trimmed);
|
|
127
|
+
|
|
128
|
+
if (isDDL && !cachedConfig.allowDDL) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
'DDL statements require EASYDB_ALLOW_DDL=true for safety. Please enable this environment variable if you want to allow DDL operations.'
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const dbName = getDatabase(database);
|
|
135
|
+
const result = await adapter.executeSQL(sql, dbName);
|
|
136
|
+
return {
|
|
137
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
server.registerTool(
|
|
143
|
+
'execute_transaction',
|
|
144
|
+
{
|
|
145
|
+
title: 'Execute Transaction',
|
|
146
|
+
description: 'Execute multiple SQL statements as a transaction. All queries will succeed or all will be rolled back.',
|
|
147
|
+
inputSchema: {
|
|
148
|
+
sql: z.array(z.string()).max(100).describe('Array of SQL statements to execute in transaction (max 100)'),
|
|
149
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set)'),
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
async ({ sql, database }) => {
|
|
153
|
+
if (!cachedConfig.allowWrite) {
|
|
154
|
+
throw new Error('Transaction execution requires EASYDB_ALLOW_WRITE=true');
|
|
125
155
|
}
|
|
126
|
-
throw new Error(
|
|
127
|
-
'Database name is required. Either set EASYDB_DATABASE environment variable or pass the database parameter in the tool call.'
|
|
128
|
-
);
|
|
129
|
-
};
|
|
130
156
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
{
|
|
139
|
-
type: 'text',
|
|
140
|
-
text: JSON.stringify(tables, null, 2),
|
|
141
|
-
},
|
|
142
|
-
],
|
|
143
|
-
};
|
|
144
|
-
}
|
|
157
|
+
const dbName = getDatabase(database);
|
|
158
|
+
const result = await adapter.executeTransaction(sql, dbName);
|
|
159
|
+
return {
|
|
160
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
);
|
|
145
164
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
server.registerTool(
|
|
166
|
+
'batch_insert',
|
|
167
|
+
{
|
|
168
|
+
title: 'Batch Insert',
|
|
169
|
+
description: 'Insert multiple rows into a table efficiently',
|
|
170
|
+
inputSchema: {
|
|
171
|
+
table: z.string().describe('Table name'),
|
|
172
|
+
data: z.array(z.record(z.string(), z.any())).describe('Array of row data objects'),
|
|
173
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set)'),
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
async ({ table, data, database }) => {
|
|
177
|
+
if (!cachedConfig.allowWrite) {
|
|
178
|
+
throw new Error('Batch insert requires EASYDB_ALLOW_WRITE=true');
|
|
179
|
+
}
|
|
161
180
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
content: [
|
|
170
|
-
{
|
|
171
|
-
type: 'text',
|
|
172
|
-
text: JSON.stringify(result, null, 2),
|
|
173
|
-
},
|
|
174
|
-
],
|
|
175
|
-
};
|
|
176
|
-
}
|
|
181
|
+
const dbName = getDatabase(database);
|
|
182
|
+
const result = await adapter.batchInsert(table, data, dbName);
|
|
183
|
+
return {
|
|
184
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
);
|
|
177
188
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
189
|
+
server.registerTool(
|
|
190
|
+
'batch_update',
|
|
191
|
+
{
|
|
192
|
+
title: 'Batch Update',
|
|
193
|
+
description: 'Update multiple rows in a table based on conditions',
|
|
194
|
+
inputSchema: {
|
|
195
|
+
table: z.string().describe('Table name'),
|
|
196
|
+
updates: z.object({
|
|
197
|
+
set: z.record(z.string(), z.any()).describe('Column values to set'),
|
|
198
|
+
where: z.string().describe('WHERE clause condition'),
|
|
199
|
+
}),
|
|
200
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set)'),
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
async ({ table, updates, database }) => {
|
|
204
|
+
if (!cachedConfig.allowWrite) {
|
|
205
|
+
throw new Error('Batch update requires EASYDB_ALLOW_WRITE=true');
|
|
206
|
+
}
|
|
184
207
|
|
|
185
|
-
|
|
186
|
-
|
|
208
|
+
const dbName = getDatabase(database);
|
|
209
|
+
const result = await adapter.batchUpdate(table, updates, dbName);
|
|
210
|
+
return {
|
|
211
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
);
|
|
187
215
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
216
|
+
server.registerTool(
|
|
217
|
+
'export_data',
|
|
218
|
+
{
|
|
219
|
+
title: 'Export Data',
|
|
220
|
+
description: 'Export table data to JSON or CSV file. Default saves to ~/table_name.json',
|
|
221
|
+
inputSchema: {
|
|
222
|
+
table: z.string().describe('Table name to export'),
|
|
223
|
+
format: z.enum(['json', 'csv']).describe('Export format'),
|
|
224
|
+
filePath: z.string().optional().describe('Optional file path (default: ~/table_name.format)'),
|
|
225
|
+
limit: z.number().optional().describe('Optional row limit'),
|
|
226
|
+
where: z.string().optional().describe('Optional WHERE clause'),
|
|
227
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set)'),
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
async ({ table, format, filePath, limit, where, database }) => {
|
|
231
|
+
const dbName = getDatabase(database);
|
|
232
|
+
const result = await adapter.exportData(table, format, filePath, { limit, where }, dbName);
|
|
233
|
+
return {
|
|
234
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
);
|
|
194
238
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
239
|
+
server.registerTool(
|
|
240
|
+
'create_table',
|
|
241
|
+
{
|
|
242
|
+
title: 'Create Table',
|
|
243
|
+
description: 'Create a new table with specified columns',
|
|
244
|
+
inputSchema: {
|
|
245
|
+
table: z.string().describe('Table name'),
|
|
246
|
+
columns: z.array(z.object({
|
|
247
|
+
name: z.string().describe('Column name'),
|
|
248
|
+
type: z.string().describe('Column data type'),
|
|
249
|
+
nullable: z.boolean().optional().describe('Whether column allows NULL'),
|
|
250
|
+
primaryKey: z.boolean().optional().describe('Whether column is primary key'),
|
|
251
|
+
defaultValue: z.any().optional().describe('Default value'),
|
|
252
|
+
})).describe('Column definitions'),
|
|
253
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set)'),
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
async ({ table, columns, database }) => {
|
|
257
|
+
if (!cachedConfig.allowDDL) {
|
|
258
|
+
throw new Error('Create table requires EASYDB_ALLOW_DDL=true');
|
|
259
|
+
}
|
|
200
260
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
{
|
|
209
|
-
type: 'text',
|
|
210
|
-
text: JSON.stringify(result, null, 2),
|
|
211
|
-
},
|
|
212
|
-
],
|
|
213
|
-
};
|
|
214
|
-
}
|
|
261
|
+
const dbName = getDatabase(database);
|
|
262
|
+
const result = await adapter.createTable(table, columns, dbName);
|
|
263
|
+
return {
|
|
264
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
);
|
|
215
268
|
|
|
216
|
-
|
|
217
|
-
|
|
269
|
+
server.registerTool(
|
|
270
|
+
'drop_table',
|
|
271
|
+
{
|
|
272
|
+
title: 'Drop Table',
|
|
273
|
+
description: 'Delete a table from the database',
|
|
274
|
+
inputSchema: {
|
|
275
|
+
table: z.string().describe('Table name to drop'),
|
|
276
|
+
ifExists: z.boolean().optional().describe('Use IF EXISTS to avoid error if table does not exist'),
|
|
277
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set)'),
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
async ({ table, ifExists, database }) => {
|
|
281
|
+
if (!cachedConfig.allowDDL) {
|
|
282
|
+
throw new Error('Drop table requires EASYDB_ALLOW_DDL=true');
|
|
218
283
|
}
|
|
219
|
-
|
|
284
|
+
|
|
285
|
+
const dbName = getDatabase(database);
|
|
286
|
+
const result = await adapter.dropTable(table, ifExists, dbName);
|
|
220
287
|
return {
|
|
221
|
-
content: [
|
|
222
|
-
{
|
|
223
|
-
type: 'text',
|
|
224
|
-
text: JSON.stringify(
|
|
225
|
-
{
|
|
226
|
-
success: false,
|
|
227
|
-
error: error instanceof Error ? error.message : String(error),
|
|
228
|
-
},
|
|
229
|
-
null,
|
|
230
|
-
2
|
|
231
|
-
),
|
|
232
|
-
},
|
|
233
|
-
],
|
|
234
|
-
isError: true,
|
|
288
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
235
289
|
};
|
|
236
290
|
}
|
|
237
|
-
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
server.registerTool(
|
|
294
|
+
'get_table_stats',
|
|
295
|
+
{
|
|
296
|
+
title: 'Get Table Statistics',
|
|
297
|
+
description: 'Get detailed statistics about a table including row count, columns, indexes, and size',
|
|
298
|
+
inputSchema: {
|
|
299
|
+
table: z.string().describe('Table name'),
|
|
300
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set)'),
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
async ({ table, database }) => {
|
|
304
|
+
const dbName = getDatabase(database);
|
|
305
|
+
const result = await adapter.getTableStats(table, dbName);
|
|
306
|
+
return {
|
|
307
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
server.registerTool(
|
|
313
|
+
'preview_data',
|
|
314
|
+
{
|
|
315
|
+
title: 'Preview Data',
|
|
316
|
+
description: 'Preview table data with pagination',
|
|
317
|
+
inputSchema: {
|
|
318
|
+
table: z.string().describe('Table name'),
|
|
319
|
+
page: z.number().optional().describe('Page number (default: 1)'),
|
|
320
|
+
pageSize: z.number().optional().describe('Rows per page (default: 50)'),
|
|
321
|
+
orderBy: z.string().optional().describe('Order by column name'),
|
|
322
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set)'),
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
async ({ table, page = 1, pageSize = 50, orderBy, database }) => {
|
|
326
|
+
const dbName = getDatabase(database);
|
|
327
|
+
const result = await adapter.previewData(table, page, pageSize, orderBy, dbName);
|
|
328
|
+
return {
|
|
329
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
server.registerTool(
|
|
335
|
+
'sample_data',
|
|
336
|
+
{
|
|
337
|
+
title: 'Sample Data',
|
|
338
|
+
description: 'Get a random sample of rows from a table',
|
|
339
|
+
inputSchema: {
|
|
340
|
+
table: z.string().describe('Table name'),
|
|
341
|
+
count: z.number().optional().describe('Number of rows to sample (default: 10)'),
|
|
342
|
+
database: z.string().optional().describe('Database name (optional if EASYDB_DATABASE is set)'),
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
async ({ table, count = 10, database }) => {
|
|
346
|
+
const dbName = getDatabase(database);
|
|
347
|
+
const result = await adapter.sampleData(table, count, dbName);
|
|
348
|
+
return {
|
|
349
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
);
|
|
238
353
|
|
|
239
354
|
// Start server
|
|
240
355
|
async function main() {
|
|
241
356
|
const transport = new StdioServerTransport();
|
|
242
357
|
await server.connect(transport);
|
|
243
|
-
console.error('EasyDB MCP Server running');
|
|
358
|
+
console.error('EasyDB MCP Server v2.0.0 running');
|
|
244
359
|
}
|
|
245
360
|
|
|
246
361
|
main().catch((error) => {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
const homedir = os.homedir();
|
|
6
|
+
|
|
7
|
+
export function expandTilde(filePath: string): string {
|
|
8
|
+
if (filePath.startsWith('~')) {
|
|
9
|
+
return path.join(homedir, filePath.slice(1));
|
|
10
|
+
}
|
|
11
|
+
return filePath;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function formatFileSize(bytes: number): string {
|
|
15
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
16
|
+
let size = bytes;
|
|
17
|
+
let unitIndex = 0;
|
|
18
|
+
|
|
19
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
20
|
+
size /= 1024;
|
|
21
|
+
unitIndex++;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return size.toFixed(2) + ' ' + units[unitIndex];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function ensureDirectoryExists(filePath: string): void {
|
|
28
|
+
const dir = path.dirname(filePath);
|
|
29
|
+
if (!fs.existsSync(dir)) {
|
|
30
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getDefaultFilePath(baseName: string, extension: string): string {
|
|
35
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
|
|
36
|
+
const time = new Date().toTimeString().split(' ')[0].replace(/:/g, '');
|
|
37
|
+
let fileName = baseName + '.' + extension;
|
|
38
|
+
|
|
39
|
+
// Check if file exists, add timestamp if it does
|
|
40
|
+
const fullPath = path.join(homedir, fileName);
|
|
41
|
+
if (fs.existsSync(fullPath)) {
|
|
42
|
+
fileName = baseName + '_' + timestamp + '_' + time + '.' + extension;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return path.join(homedir, fileName);
|
|
46
|
+
}
|