mpx-db 1.0.3 → 1.1.2
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 +135 -2
- package/package.json +8 -2
- package/src/cli.js +125 -15
- package/src/commands/connections.js +63 -10
- package/src/commands/data.js +20 -7
- package/src/commands/migrate.js +142 -36
- package/src/commands/query.js +44 -14
- package/src/commands/schema.js +120 -50
- package/src/mcp.js +307 -0
- package/src/schema.js +594 -0
- package/src/update.js +72 -0
package/src/mcp.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP (Model Context Protocol) Server
|
|
3
|
+
*
|
|
4
|
+
* Exposes mpx-db capabilities as MCP tools for AI agent integration.
|
|
5
|
+
* Runs over stdio transport.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import {
|
|
11
|
+
ListToolsRequestSchema,
|
|
12
|
+
CallToolRequestSchema
|
|
13
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
14
|
+
|
|
15
|
+
import { readFileSync } from 'fs';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
17
|
+
import { dirname, join } from 'path';
|
|
18
|
+
import { createConnection } from './db/connection.js';
|
|
19
|
+
import { getConnection } from './utils/config.js';
|
|
20
|
+
import { getSchema } from './schema.js';
|
|
21
|
+
|
|
22
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
+
const __dirname = dirname(__filename);
|
|
24
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolve connection string from target (name or URL)
|
|
28
|
+
*/
|
|
29
|
+
function resolveConnection(target) {
|
|
30
|
+
const saved = getConnection(target);
|
|
31
|
+
if (saved) {
|
|
32
|
+
return saved.url;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (target.includes('://')) {
|
|
36
|
+
return target;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
throw new Error(`Connection "${target}" not found`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function startMCPServer() {
|
|
43
|
+
const server = new Server(
|
|
44
|
+
{ name: 'mpx-db', version: pkg.version },
|
|
45
|
+
{ capabilities: { tools: {} } }
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// List available tools
|
|
49
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
50
|
+
return {
|
|
51
|
+
tools: [
|
|
52
|
+
{
|
|
53
|
+
name: 'query',
|
|
54
|
+
description: 'Execute a SQL query or statement on a database. Returns rows for SELECT queries, or execution details for INSERT/UPDATE/DELETE/DDL statements.',
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
target: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: 'Connection name (saved) or connection URL (sqlite://, postgres://, mysql://)'
|
|
61
|
+
},
|
|
62
|
+
sql: {
|
|
63
|
+
type: 'string',
|
|
64
|
+
description: 'SQL query or statement to execute'
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
required: ['target', 'sql']
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'list_tables',
|
|
72
|
+
description: 'List all tables in the database with row counts.',
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
target: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
description: 'Connection name or URL'
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
required: ['target']
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'describe_table',
|
|
86
|
+
description: 'Show the schema/structure of a specific table (columns, types, constraints).',
|
|
87
|
+
inputSchema: {
|
|
88
|
+
type: 'object',
|
|
89
|
+
properties: {
|
|
90
|
+
target: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
description: 'Connection name or URL'
|
|
93
|
+
},
|
|
94
|
+
table: {
|
|
95
|
+
type: 'string',
|
|
96
|
+
description: 'Table name'
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
required: ['target', 'table']
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'get_info',
|
|
104
|
+
description: 'Get database information (type, version, size, table count).',
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {
|
|
108
|
+
target: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
description: 'Connection name or URL'
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
required: ['target']
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'export_table',
|
|
118
|
+
description: 'Export all data from a table as JSON.',
|
|
119
|
+
inputSchema: {
|
|
120
|
+
type: 'object',
|
|
121
|
+
properties: {
|
|
122
|
+
target: {
|
|
123
|
+
type: 'string',
|
|
124
|
+
description: 'Connection name or URL'
|
|
125
|
+
},
|
|
126
|
+
table: {
|
|
127
|
+
type: 'string',
|
|
128
|
+
description: 'Table name'
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
required: ['target', 'table']
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'get_schema',
|
|
136
|
+
description: 'Get the full JSON schema describing all mpx-db commands, flags, and output formats.',
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Handle tool calls
|
|
147
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
148
|
+
const { name, arguments: args } = request.params;
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
switch (name) {
|
|
152
|
+
case 'query': {
|
|
153
|
+
const connectionString = resolveConnection(args.target);
|
|
154
|
+
const db = await createConnection(connectionString);
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const sql = args.sql.trim();
|
|
158
|
+
const isSelectQuery = /^(SELECT|PRAGMA|EXPLAIN|WITH|SHOW|DESCRIBE|DESC)\s/i.test(sql);
|
|
159
|
+
|
|
160
|
+
const startTime = Date.now();
|
|
161
|
+
let result;
|
|
162
|
+
|
|
163
|
+
if (isSelectQuery) {
|
|
164
|
+
const rows = await db.query(sql);
|
|
165
|
+
const duration = Date.now() - startTime;
|
|
166
|
+
result = {
|
|
167
|
+
success: true,
|
|
168
|
+
type: 'query',
|
|
169
|
+
rows,
|
|
170
|
+
rowCount: rows.length,
|
|
171
|
+
duration
|
|
172
|
+
};
|
|
173
|
+
} else {
|
|
174
|
+
const execResult = await db.execute(sql);
|
|
175
|
+
const duration = Date.now() - startTime;
|
|
176
|
+
result = {
|
|
177
|
+
success: true,
|
|
178
|
+
type: 'statement',
|
|
179
|
+
affectedRows: execResult.affectedRows,
|
|
180
|
+
insertId: execResult.insertId,
|
|
181
|
+
duration
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
await db.disconnect();
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
content: [{
|
|
189
|
+
type: 'text',
|
|
190
|
+
text: JSON.stringify(result, null, 2)
|
|
191
|
+
}]
|
|
192
|
+
};
|
|
193
|
+
} catch (err) {
|
|
194
|
+
await db.disconnect();
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
case 'list_tables': {
|
|
200
|
+
const connectionString = resolveConnection(args.target);
|
|
201
|
+
const db = await createConnection(connectionString);
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
const tables = await db.getTables();
|
|
205
|
+
await db.disconnect();
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
content: [{
|
|
209
|
+
type: 'text',
|
|
210
|
+
text: JSON.stringify({ tables }, null, 2)
|
|
211
|
+
}]
|
|
212
|
+
};
|
|
213
|
+
} catch (err) {
|
|
214
|
+
await db.disconnect();
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
case 'describe_table': {
|
|
220
|
+
const connectionString = resolveConnection(args.target);
|
|
221
|
+
const db = await createConnection(connectionString);
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const schema = await db.getTableSchema(args.table);
|
|
225
|
+
await db.disconnect();
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
content: [{
|
|
229
|
+
type: 'text',
|
|
230
|
+
text: JSON.stringify({ table: args.table, columns: schema }, null, 2)
|
|
231
|
+
}]
|
|
232
|
+
};
|
|
233
|
+
} catch (err) {
|
|
234
|
+
await db.disconnect();
|
|
235
|
+
throw err;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
case 'get_info': {
|
|
240
|
+
const connectionString = resolveConnection(args.target);
|
|
241
|
+
const db = await createConnection(connectionString);
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const info = await db.getInfo();
|
|
245
|
+
await db.disconnect();
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
content: [{
|
|
249
|
+
type: 'text',
|
|
250
|
+
text: JSON.stringify(info, null, 2)
|
|
251
|
+
}]
|
|
252
|
+
};
|
|
253
|
+
} catch (err) {
|
|
254
|
+
await db.disconnect();
|
|
255
|
+
throw err;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
case 'export_table': {
|
|
260
|
+
const connectionString = resolveConnection(args.target);
|
|
261
|
+
const db = await createConnection(connectionString);
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const rows = await db.query(`SELECT * FROM ${args.table}`);
|
|
265
|
+
await db.disconnect();
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
content: [{
|
|
269
|
+
type: 'text',
|
|
270
|
+
text: JSON.stringify({ table: args.table, rows, count: rows.length }, null, 2)
|
|
271
|
+
}]
|
|
272
|
+
};
|
|
273
|
+
} catch (err) {
|
|
274
|
+
await db.disconnect();
|
|
275
|
+
throw err;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
case 'get_schema': {
|
|
280
|
+
return {
|
|
281
|
+
content: [{
|
|
282
|
+
type: 'text',
|
|
283
|
+
text: JSON.stringify(getSchema(), null, 2)
|
|
284
|
+
}]
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
default:
|
|
289
|
+
return {
|
|
290
|
+
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
291
|
+
isError: true
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
} catch (err) {
|
|
295
|
+
return {
|
|
296
|
+
content: [{
|
|
297
|
+
type: 'text',
|
|
298
|
+
text: JSON.stringify({ error: err.message, code: 'ERR_QUERY' }, null, 2)
|
|
299
|
+
}],
|
|
300
|
+
isError: true
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const transport = new StdioServerTransport();
|
|
306
|
+
await server.connect(transport);
|
|
307
|
+
}
|