@berthojoris/mcp-mysql-server 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.
- package/LICENSE +21 -0
- package/README.md +1142 -0
- package/bin/mcp-mysql.js +122 -0
- package/dist/auth/authService.d.ts +29 -0
- package/dist/auth/authService.js +114 -0
- package/dist/config/config.d.ts +12 -0
- package/dist/config/config.js +19 -0
- package/dist/config/featureConfig.d.ts +51 -0
- package/dist/config/featureConfig.js +130 -0
- package/dist/db/connection.d.ts +22 -0
- package/dist/db/connection.js +135 -0
- package/dist/index.d.ts +236 -0
- package/dist/index.js +273 -0
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.js +748 -0
- package/dist/security/securityLayer.d.ts +52 -0
- package/dist/security/securityLayer.js +213 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +283 -0
- package/dist/tools/crudTools.d.ts +59 -0
- package/dist/tools/crudTools.js +443 -0
- package/dist/tools/databaseTools.d.ts +33 -0
- package/dist/tools/databaseTools.js +108 -0
- package/dist/tools/ddlTools.d.ts +69 -0
- package/dist/tools/ddlTools.js +199 -0
- package/dist/tools/queryTools.d.ts +29 -0
- package/dist/tools/queryTools.js +119 -0
- package/dist/tools/storedProcedureTools.d.ts +80 -0
- package/dist/tools/storedProcedureTools.js +411 -0
- package/dist/tools/transactionTools.d.ts +45 -0
- package/dist/tools/transactionTools.js +130 -0
- package/dist/tools/utilityTools.d.ts +30 -0
- package/dist/tools/utilityTools.js +121 -0
- package/dist/validation/schemas.d.ts +423 -0
- package/dist/validation/schemas.js +287 -0
- package/manifest.json +248 -0
- package/package.json +83 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DdlTools = void 0;
|
|
7
|
+
const connection_1 = __importDefault(require("../db/connection"));
|
|
8
|
+
class DdlTools {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.db = connection_1.default.getInstance();
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Create a new table
|
|
14
|
+
*/
|
|
15
|
+
async createTable(params) {
|
|
16
|
+
try {
|
|
17
|
+
const { table_name, columns, indexes } = params;
|
|
18
|
+
// Build column definitions
|
|
19
|
+
const columnDefs = columns.map(col => {
|
|
20
|
+
let def = `\`${col.name}\` ${col.type}`;
|
|
21
|
+
if (col.nullable === false) {
|
|
22
|
+
def += ' NOT NULL';
|
|
23
|
+
}
|
|
24
|
+
if (col.auto_increment) {
|
|
25
|
+
def += ' AUTO_INCREMENT';
|
|
26
|
+
}
|
|
27
|
+
if (col.default !== undefined) {
|
|
28
|
+
def += ` DEFAULT ${col.default}`;
|
|
29
|
+
}
|
|
30
|
+
if (col.primary_key) {
|
|
31
|
+
def += ' PRIMARY KEY';
|
|
32
|
+
}
|
|
33
|
+
return def;
|
|
34
|
+
}).join(', ');
|
|
35
|
+
// Build the CREATE TABLE query
|
|
36
|
+
let query = `CREATE TABLE \`${table_name}\` (${columnDefs})`;
|
|
37
|
+
// Execute the query
|
|
38
|
+
await this.db.query(query);
|
|
39
|
+
// Create indexes if specified
|
|
40
|
+
if (indexes && indexes.length > 0) {
|
|
41
|
+
for (const index of indexes) {
|
|
42
|
+
const indexType = index.unique ? 'UNIQUE INDEX' : 'INDEX';
|
|
43
|
+
const indexColumns = index.columns.map(c => `\`${c}\``).join(', ');
|
|
44
|
+
const indexQuery = `CREATE ${indexType} \`${index.name}\` ON \`${table_name}\` (${indexColumns})`;
|
|
45
|
+
await this.db.query(indexQuery);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
status: 'success',
|
|
50
|
+
data: {
|
|
51
|
+
message: `Table '${table_name}' created successfully`,
|
|
52
|
+
table_name
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return {
|
|
58
|
+
status: 'error',
|
|
59
|
+
error: error.message
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Alter an existing table
|
|
65
|
+
*/
|
|
66
|
+
async alterTable(params) {
|
|
67
|
+
try {
|
|
68
|
+
const { table_name, operations } = params;
|
|
69
|
+
for (const op of operations) {
|
|
70
|
+
let query = `ALTER TABLE \`${table_name}\``;
|
|
71
|
+
switch (op.type) {
|
|
72
|
+
case 'add_column':
|
|
73
|
+
if (!op.column_name || !op.column_type) {
|
|
74
|
+
return { status: 'error', error: 'column_name and column_type required for add_column' };
|
|
75
|
+
}
|
|
76
|
+
query += ` ADD COLUMN \`${op.column_name}\` ${op.column_type}`;
|
|
77
|
+
if (op.nullable === false)
|
|
78
|
+
query += ' NOT NULL';
|
|
79
|
+
if (op.default !== undefined)
|
|
80
|
+
query += ` DEFAULT ${op.default}`;
|
|
81
|
+
break;
|
|
82
|
+
case 'drop_column':
|
|
83
|
+
if (!op.column_name) {
|
|
84
|
+
return { status: 'error', error: 'column_name required for drop_column' };
|
|
85
|
+
}
|
|
86
|
+
query += ` DROP COLUMN \`${op.column_name}\``;
|
|
87
|
+
break;
|
|
88
|
+
case 'modify_column':
|
|
89
|
+
if (!op.column_name || !op.column_type) {
|
|
90
|
+
return { status: 'error', error: 'column_name and column_type required for modify_column' };
|
|
91
|
+
}
|
|
92
|
+
query += ` MODIFY COLUMN \`${op.column_name}\` ${op.column_type}`;
|
|
93
|
+
if (op.nullable === false)
|
|
94
|
+
query += ' NOT NULL';
|
|
95
|
+
if (op.default !== undefined)
|
|
96
|
+
query += ` DEFAULT ${op.default}`;
|
|
97
|
+
break;
|
|
98
|
+
case 'rename_column':
|
|
99
|
+
if (!op.column_name || !op.new_column_name || !op.column_type) {
|
|
100
|
+
return { status: 'error', error: 'column_name, new_column_name, and column_type required for rename_column' };
|
|
101
|
+
}
|
|
102
|
+
query += ` CHANGE COLUMN \`${op.column_name}\` \`${op.new_column_name}\` ${op.column_type}`;
|
|
103
|
+
break;
|
|
104
|
+
case 'add_index':
|
|
105
|
+
if (!op.index_name || !op.index_columns) {
|
|
106
|
+
return { status: 'error', error: 'index_name and index_columns required for add_index' };
|
|
107
|
+
}
|
|
108
|
+
const indexType = op.unique ? 'UNIQUE INDEX' : 'INDEX';
|
|
109
|
+
const columns = op.index_columns.map(c => `\`${c}\``).join(', ');
|
|
110
|
+
query += ` ADD ${indexType} \`${op.index_name}\` (${columns})`;
|
|
111
|
+
break;
|
|
112
|
+
case 'drop_index':
|
|
113
|
+
if (!op.index_name) {
|
|
114
|
+
return { status: 'error', error: 'index_name required for drop_index' };
|
|
115
|
+
}
|
|
116
|
+
query += ` DROP INDEX \`${op.index_name}\``;
|
|
117
|
+
break;
|
|
118
|
+
default:
|
|
119
|
+
return { status: 'error', error: `Unknown operation type: ${op.type}` };
|
|
120
|
+
}
|
|
121
|
+
await this.db.query(query);
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
status: 'success',
|
|
125
|
+
data: {
|
|
126
|
+
message: `Table '${table_name}' altered successfully`,
|
|
127
|
+
table_name,
|
|
128
|
+
operations_count: operations.length
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
return {
|
|
134
|
+
status: 'error',
|
|
135
|
+
error: error.message
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Drop a table
|
|
141
|
+
*/
|
|
142
|
+
async dropTable(params) {
|
|
143
|
+
try {
|
|
144
|
+
const { table_name, if_exists } = params;
|
|
145
|
+
const ifExistsClause = if_exists ? 'IF EXISTS ' : '';
|
|
146
|
+
const query = `DROP TABLE ${ifExistsClause}\`${table_name}\``;
|
|
147
|
+
await this.db.query(query);
|
|
148
|
+
return {
|
|
149
|
+
status: 'success',
|
|
150
|
+
data: {
|
|
151
|
+
message: `Table '${table_name}' dropped successfully`,
|
|
152
|
+
table_name
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return {
|
|
158
|
+
status: 'error',
|
|
159
|
+
error: error.message
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Execute raw DDL SQL
|
|
165
|
+
*/
|
|
166
|
+
async executeDdl(params) {
|
|
167
|
+
try {
|
|
168
|
+
const { query } = params;
|
|
169
|
+
// Basic validation - ensure it's a DDL query
|
|
170
|
+
const upperQuery = query.trim().toUpperCase();
|
|
171
|
+
const isDdl = upperQuery.startsWith('CREATE') ||
|
|
172
|
+
upperQuery.startsWith('ALTER') ||
|
|
173
|
+
upperQuery.startsWith('DROP') ||
|
|
174
|
+
upperQuery.startsWith('TRUNCATE') ||
|
|
175
|
+
upperQuery.startsWith('RENAME');
|
|
176
|
+
if (!isDdl) {
|
|
177
|
+
return {
|
|
178
|
+
status: 'error',
|
|
179
|
+
error: 'Only DDL operations (CREATE, ALTER, DROP, TRUNCATE, RENAME) are allowed with executeDdl'
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const result = await this.db.query(query);
|
|
183
|
+
return {
|
|
184
|
+
status: 'success',
|
|
185
|
+
data: {
|
|
186
|
+
message: 'DDL query executed successfully',
|
|
187
|
+
affected_rows: result.affectedRows || 0
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
return {
|
|
193
|
+
status: 'error',
|
|
194
|
+
error: error.message
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
exports.DdlTools = DdlTools;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import SecurityLayer from '../security/securityLayer';
|
|
2
|
+
export declare class QueryTools {
|
|
3
|
+
private db;
|
|
4
|
+
private security;
|
|
5
|
+
constructor(security: SecurityLayer);
|
|
6
|
+
/**
|
|
7
|
+
* Execute a safe read-only SELECT query
|
|
8
|
+
*/
|
|
9
|
+
runQuery(queryParams: {
|
|
10
|
+
query: string;
|
|
11
|
+
params?: any[];
|
|
12
|
+
}): Promise<{
|
|
13
|
+
status: string;
|
|
14
|
+
data?: any[];
|
|
15
|
+
error?: string;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Execute write operations (INSERT, UPDATE, DELETE) with validation
|
|
19
|
+
* Note: DDL operations are blocked by the security layer for safety
|
|
20
|
+
*/
|
|
21
|
+
executeSql(queryParams: {
|
|
22
|
+
query: string;
|
|
23
|
+
params?: any[];
|
|
24
|
+
}): Promise<{
|
|
25
|
+
status: string;
|
|
26
|
+
data?: any;
|
|
27
|
+
error?: string;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.QueryTools = void 0;
|
|
7
|
+
const connection_1 = __importDefault(require("../db/connection"));
|
|
8
|
+
const schemas_1 = require("../validation/schemas");
|
|
9
|
+
class QueryTools {
|
|
10
|
+
constructor(security) {
|
|
11
|
+
this.db = connection_1.default.getInstance();
|
|
12
|
+
this.security = security;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Execute a safe read-only SELECT query
|
|
16
|
+
*/
|
|
17
|
+
async runQuery(queryParams) {
|
|
18
|
+
// Validate input schema
|
|
19
|
+
if (!(0, schemas_1.validateRunQuery)(queryParams)) {
|
|
20
|
+
return {
|
|
21
|
+
status: 'error',
|
|
22
|
+
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateRunQuery.errors)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const { query, params = [] } = queryParams;
|
|
27
|
+
// Validate query using security layer
|
|
28
|
+
const queryValidation = this.security.validateQuery(query);
|
|
29
|
+
if (!queryValidation.valid) {
|
|
30
|
+
return {
|
|
31
|
+
status: 'error',
|
|
32
|
+
error: `Query validation failed: ${queryValidation.error}`
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// Ensure it's a SELECT query
|
|
36
|
+
if (queryValidation.queryType !== 'SELECT') {
|
|
37
|
+
return {
|
|
38
|
+
status: 'error',
|
|
39
|
+
error: 'Only SELECT queries are allowed with runQuery. Use executeSql for other operations.'
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Validate parameters
|
|
43
|
+
const paramValidation = this.security.validateParameters(params);
|
|
44
|
+
if (!paramValidation.valid) {
|
|
45
|
+
return {
|
|
46
|
+
status: 'error',
|
|
47
|
+
error: `Parameter validation failed: ${paramValidation.error}`
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Execute the query with sanitized parameters
|
|
51
|
+
const results = await this.db.query(query, paramValidation.sanitizedParams);
|
|
52
|
+
return {
|
|
53
|
+
status: 'success',
|
|
54
|
+
data: results
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
return {
|
|
59
|
+
status: 'error',
|
|
60
|
+
error: error.message
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Execute write operations (INSERT, UPDATE, DELETE) with validation
|
|
66
|
+
* Note: DDL operations are blocked by the security layer for safety
|
|
67
|
+
*/
|
|
68
|
+
async executeSql(queryParams) {
|
|
69
|
+
// Validate input schema
|
|
70
|
+
if (!(0, schemas_1.validateRunQuery)(queryParams)) {
|
|
71
|
+
return {
|
|
72
|
+
status: 'error',
|
|
73
|
+
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateRunQuery.errors)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const { query, params = [] } = queryParams;
|
|
78
|
+
// Validate query using security layer
|
|
79
|
+
const queryValidation = this.security.validateQuery(query);
|
|
80
|
+
if (!queryValidation.valid) {
|
|
81
|
+
return {
|
|
82
|
+
status: 'error',
|
|
83
|
+
error: `Query validation failed: ${queryValidation.error}`
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Ensure it's not a SELECT query (use runQuery for that)
|
|
87
|
+
if (queryValidation.queryType === 'SELECT') {
|
|
88
|
+
return {
|
|
89
|
+
status: 'error',
|
|
90
|
+
error: 'SELECT queries should use runQuery method instead of executeSql.'
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// Validate parameters
|
|
94
|
+
const paramValidation = this.security.validateParameters(params);
|
|
95
|
+
if (!paramValidation.valid) {
|
|
96
|
+
return {
|
|
97
|
+
status: 'error',
|
|
98
|
+
error: `Parameter validation failed: ${paramValidation.error}`
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Execute the query with sanitized parameters
|
|
102
|
+
const result = await this.db.query(query, paramValidation.sanitizedParams);
|
|
103
|
+
return {
|
|
104
|
+
status: 'success',
|
|
105
|
+
data: {
|
|
106
|
+
affectedRows: result.affectedRows || 0,
|
|
107
|
+
insertId: result.insertId || null
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
return {
|
|
113
|
+
status: 'error',
|
|
114
|
+
error: error.message
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.QueryTools = QueryTools;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { SecurityLayer } from '../security/securityLayer';
|
|
2
|
+
export declare class StoredProcedureTools {
|
|
3
|
+
private db;
|
|
4
|
+
private security;
|
|
5
|
+
constructor(security: SecurityLayer);
|
|
6
|
+
/**
|
|
7
|
+
* List all stored procedures in the current database
|
|
8
|
+
*/
|
|
9
|
+
listStoredProcedures(params: {
|
|
10
|
+
database?: string;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
status: string;
|
|
13
|
+
data?: any[];
|
|
14
|
+
error?: string;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Get detailed information about a specific stored procedure
|
|
18
|
+
*/
|
|
19
|
+
getStoredProcedureInfo(params: {
|
|
20
|
+
procedure_name: string;
|
|
21
|
+
database?: string;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
status: string;
|
|
24
|
+
data?: any;
|
|
25
|
+
error?: string;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Execute a stored procedure with parameters
|
|
29
|
+
*/
|
|
30
|
+
executeStoredProcedure(params: {
|
|
31
|
+
procedure_name: string;
|
|
32
|
+
parameters?: any[];
|
|
33
|
+
database?: string;
|
|
34
|
+
}): Promise<{
|
|
35
|
+
status: string;
|
|
36
|
+
data?: any;
|
|
37
|
+
error?: string;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* Create a new stored procedure
|
|
41
|
+
*/
|
|
42
|
+
createStoredProcedure(params: {
|
|
43
|
+
procedure_name: string;
|
|
44
|
+
parameters?: Array<{
|
|
45
|
+
name: string;
|
|
46
|
+
mode: 'IN' | 'OUT' | 'INOUT';
|
|
47
|
+
data_type: string;
|
|
48
|
+
}>;
|
|
49
|
+
body: string;
|
|
50
|
+
comment?: string;
|
|
51
|
+
database?: string;
|
|
52
|
+
}): Promise<{
|
|
53
|
+
status: string;
|
|
54
|
+
data?: any;
|
|
55
|
+
error?: string;
|
|
56
|
+
}>;
|
|
57
|
+
/**
|
|
58
|
+
* Drop a stored procedure
|
|
59
|
+
*/
|
|
60
|
+
dropStoredProcedure(params: {
|
|
61
|
+
procedure_name: string;
|
|
62
|
+
if_exists?: boolean;
|
|
63
|
+
database?: string;
|
|
64
|
+
}): Promise<{
|
|
65
|
+
status: string;
|
|
66
|
+
message?: string;
|
|
67
|
+
error?: string;
|
|
68
|
+
}>;
|
|
69
|
+
/**
|
|
70
|
+
* Show the CREATE statement for a stored procedure
|
|
71
|
+
*/
|
|
72
|
+
showCreateProcedure(params: {
|
|
73
|
+
procedure_name: string;
|
|
74
|
+
database?: string;
|
|
75
|
+
}): Promise<{
|
|
76
|
+
status: string;
|
|
77
|
+
data?: any;
|
|
78
|
+
error?: string;
|
|
79
|
+
}>;
|
|
80
|
+
}
|