@berthojoris/mcp-mysql-server 1.40.5 → 1.40.7
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/CHANGELOG.md +23 -0
- package/DOCUMENTATIONS.md +55 -15
- package/README.md +37 -3
- package/dist/config/featureConfig.js +7 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +11 -4
- package/dist/mcp-server.js +129 -3
- package/dist/security/securityLayer.d.ts +1 -1
- package/dist/security/securityLayer.js +2 -2
- package/dist/tools/dataExportTools.d.ts +11 -0
- package/dist/tools/dataExportTools.js +68 -27
- package/dist/tools/ddlTools.d.ts +2 -0
- package/dist/tools/ddlTools.js +125 -20
- package/dist/tools/toolArgumentValidation.js +5 -0
- package/dist/tools/utilityTools.d.ts +16 -1
- package/dist/tools/utilityTools.js +73 -18
- package/manifest.json +2538 -732
- package/package.json +1 -1
|
@@ -62,6 +62,33 @@ class DataExportTools {
|
|
|
62
62
|
.replace(/\0/g, "\\0");
|
|
63
63
|
return `'${escaped}'`;
|
|
64
64
|
}
|
|
65
|
+
escapeCsvValue(value) {
|
|
66
|
+
if (value === null || value === undefined)
|
|
67
|
+
return "";
|
|
68
|
+
const normalizedValue = value instanceof Date
|
|
69
|
+
? value.toISOString()
|
|
70
|
+
: Buffer.isBuffer(value)
|
|
71
|
+
? value.toString("base64")
|
|
72
|
+
: String(value);
|
|
73
|
+
if (/[",\r\n]/.test(normalizedValue)) {
|
|
74
|
+
return `"${normalizedValue.replace(/"/g, '""')}"`;
|
|
75
|
+
}
|
|
76
|
+
return normalizedValue;
|
|
77
|
+
}
|
|
78
|
+
rowsToCSV(rows, includeHeaders) {
|
|
79
|
+
if (rows.length === 0) {
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
const columns = Object.keys(rows[0]);
|
|
83
|
+
const csvRows = [];
|
|
84
|
+
if (includeHeaders) {
|
|
85
|
+
csvRows.push(columns.map((column) => this.escapeCsvValue(column)).join(","));
|
|
86
|
+
}
|
|
87
|
+
for (const row of rows) {
|
|
88
|
+
csvRows.push(columns.map((column) => this.escapeCsvValue(row[column])).join(","));
|
|
89
|
+
}
|
|
90
|
+
return `${csvRows.join("\n")}\n`;
|
|
91
|
+
}
|
|
65
92
|
/**
|
|
66
93
|
* Export table data to CSV format
|
|
67
94
|
*/
|
|
@@ -179,33 +206,7 @@ class DataExportTools {
|
|
|
179
206
|
},
|
|
180
207
|
};
|
|
181
208
|
}
|
|
182
|
-
|
|
183
|
-
let csv = "";
|
|
184
|
-
// Add headers if requested
|
|
185
|
-
if (include_headers) {
|
|
186
|
-
const headers = Object.keys(results[0]).join(",");
|
|
187
|
-
csv += headers + "\n";
|
|
188
|
-
}
|
|
189
|
-
// Add data rows
|
|
190
|
-
for (const row of results) {
|
|
191
|
-
const values = Object.values(row)
|
|
192
|
-
.map((value) => {
|
|
193
|
-
if (value === null)
|
|
194
|
-
return "";
|
|
195
|
-
if (typeof value === "string") {
|
|
196
|
-
// Escape quotes and wrap in quotes if contains comma or newline
|
|
197
|
-
if (value.includes(",") ||
|
|
198
|
-
value.includes("\n") ||
|
|
199
|
-
value.includes('"')) {
|
|
200
|
-
return `"${value.replace(/"/g, '""')}"`;
|
|
201
|
-
}
|
|
202
|
-
return value;
|
|
203
|
-
}
|
|
204
|
-
return String(value);
|
|
205
|
-
})
|
|
206
|
-
.join(",");
|
|
207
|
-
csv += values + "\n";
|
|
208
|
-
}
|
|
209
|
+
const csv = this.rowsToCSV(results, include_headers);
|
|
209
210
|
return {
|
|
210
211
|
status: "success",
|
|
211
212
|
data: {
|
|
@@ -221,5 +222,45 @@ class DataExportTools {
|
|
|
221
222
|
};
|
|
222
223
|
}
|
|
223
224
|
}
|
|
225
|
+
async exportQueryToCSV(queryParams) {
|
|
226
|
+
try {
|
|
227
|
+
const { query, params = [], include_headers = true, } = queryParams;
|
|
228
|
+
const queryValidation = this.security.validateQuery(query, this.security.hasExecutePermission());
|
|
229
|
+
if (!queryValidation.valid) {
|
|
230
|
+
return {
|
|
231
|
+
status: "error",
|
|
232
|
+
error: `Query validation failed: ${queryValidation.error}`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (queryValidation.queryType !== "SELECT") {
|
|
236
|
+
return {
|
|
237
|
+
status: "error",
|
|
238
|
+
error: "export_query_to_csv only accepts SELECT queries.",
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const paramValidation = this.security.validateParameters(params);
|
|
242
|
+
if (!paramValidation.valid) {
|
|
243
|
+
return {
|
|
244
|
+
status: "error",
|
|
245
|
+
error: `Parameter validation failed: ${paramValidation.error}`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const results = await this.db.query(query, paramValidation.sanitizedParams, false);
|
|
249
|
+
const maskedResults = this.security.masking.processResults(results);
|
|
250
|
+
return {
|
|
251
|
+
status: "success",
|
|
252
|
+
data: {
|
|
253
|
+
csv: this.rowsToCSV(maskedResults, include_headers),
|
|
254
|
+
row_count: maskedResults.length,
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
return {
|
|
260
|
+
status: "error",
|
|
261
|
+
error: error.message,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
224
265
|
}
|
|
225
266
|
exports.DataExportTools = DataExportTools;
|
package/dist/tools/ddlTools.d.ts
CHANGED
package/dist/tools/ddlTools.js
CHANGED
|
@@ -52,16 +52,59 @@ class DdlTools {
|
|
|
52
52
|
// For other types, convert to string and escape
|
|
53
53
|
return `'${String(defaultValue).replace(/\\/g, "\\\\").replace(/'/g, "''")}'`;
|
|
54
54
|
}
|
|
55
|
+
validateColumnType(columnType) {
|
|
56
|
+
if (!columnType || typeof columnType !== "string") {
|
|
57
|
+
return { valid: false, error: "Column type must be a non-empty string" };
|
|
58
|
+
}
|
|
59
|
+
const normalizedType = columnType.trim().replace(/\s+/g, " ");
|
|
60
|
+
if (normalizedType.length > 128) {
|
|
61
|
+
return { valid: false, error: "Column type is too long" };
|
|
62
|
+
}
|
|
63
|
+
const safeTypePattern = /^(?:(?:TINY|SMALL|MEDIUM|BIG)?INT(?:EGER)?|DECIMAL|NUMERIC|FLOAT|DOUBLE(?: PRECISION)?|REAL|BIT|BOOL(?:EAN)?|CHAR|VARCHAR|BINARY|VARBINARY|TINYTEXT|TEXT|MEDIUMTEXT|LONGTEXT|TINYBLOB|BLOB|MEDIUMBLOB|LONGBLOB|DATE|DATETIME|TIMESTAMP|TIME|YEAR|JSON)(?:\s*\(\s*\d+(?:\s*,\s*\d+)?\s*\))?(?:\s+(?:UNSIGNED|ZEROFILL))*$/i;
|
|
64
|
+
if (!safeTypePattern.test(normalizedType)) {
|
|
65
|
+
return {
|
|
66
|
+
valid: false,
|
|
67
|
+
error: "Invalid or unsupported column type. Use a standard MySQL data type such as INT, VARCHAR(255), DECIMAL(10,2), TEXT, DATETIME, or JSON.",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return { valid: true };
|
|
71
|
+
}
|
|
72
|
+
validateIdentifier(identifier, label) {
|
|
73
|
+
const validation = this.security.validateIdentifier(identifier);
|
|
74
|
+
if (!validation.valid) {
|
|
75
|
+
return {
|
|
76
|
+
valid: false,
|
|
77
|
+
error: `Invalid ${label}: ${validation.error}`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return { valid: true };
|
|
81
|
+
}
|
|
55
82
|
/**
|
|
56
83
|
* Create a new table
|
|
57
84
|
*/
|
|
58
85
|
async createTable(params) {
|
|
59
86
|
try {
|
|
60
87
|
const { table_name, columns, indexes } = params;
|
|
88
|
+
const tableValidation = this.validateIdentifier(table_name, "table name");
|
|
89
|
+
if (!tableValidation.valid) {
|
|
90
|
+
return { status: "error", error: tableValidation.error };
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(columns) || columns.length === 0) {
|
|
93
|
+
return { status: "error", error: "At least one column is required" };
|
|
94
|
+
}
|
|
95
|
+
const escapedTableName = this.security.escapeIdentifier(table_name);
|
|
61
96
|
// Build column definitions
|
|
62
97
|
const columnDefs = columns
|
|
63
98
|
.map((col) => {
|
|
64
|
-
|
|
99
|
+
const columnValidation = this.validateIdentifier(col.name, "column name");
|
|
100
|
+
if (!columnValidation.valid) {
|
|
101
|
+
throw new Error(columnValidation.error);
|
|
102
|
+
}
|
|
103
|
+
const typeValidation = this.validateColumnType(col.type);
|
|
104
|
+
if (!typeValidation.valid) {
|
|
105
|
+
throw new Error(`Invalid column type for '${col.name}': ${typeValidation.error}`);
|
|
106
|
+
}
|
|
107
|
+
let def = `${this.security.escapeIdentifier(col.name)} ${col.type.trim()}`;
|
|
65
108
|
if (col.nullable === false) {
|
|
66
109
|
def += " NOT NULL";
|
|
67
110
|
}
|
|
@@ -80,16 +123,29 @@ class DdlTools {
|
|
|
80
123
|
})
|
|
81
124
|
.join(", ");
|
|
82
125
|
// Build the CREATE TABLE query
|
|
83
|
-
let query = `CREATE TABLE
|
|
126
|
+
let query = `CREATE TABLE ${escapedTableName} (${columnDefs})`;
|
|
84
127
|
// Execute the query
|
|
85
128
|
await this.db.query(query);
|
|
86
129
|
// Create indexes if specified
|
|
87
130
|
let queryCount = 1;
|
|
88
131
|
if (indexes && indexes.length > 0) {
|
|
89
132
|
for (const index of indexes) {
|
|
133
|
+
const indexValidation = this.validateIdentifier(index.name, "index name");
|
|
134
|
+
if (!indexValidation.valid) {
|
|
135
|
+
return { status: "error", error: indexValidation.error };
|
|
136
|
+
}
|
|
137
|
+
if (!Array.isArray(index.columns) || index.columns.length === 0) {
|
|
138
|
+
return { status: "error", error: "Index columns are required" };
|
|
139
|
+
}
|
|
90
140
|
const indexType = index.unique ? "UNIQUE INDEX" : "INDEX";
|
|
91
|
-
const indexColumns = index.columns.map((c) =>
|
|
92
|
-
|
|
141
|
+
const indexColumns = index.columns.map((c) => {
|
|
142
|
+
const columnValidation = this.validateIdentifier(c, "index column name");
|
|
143
|
+
if (!columnValidation.valid) {
|
|
144
|
+
throw new Error(columnValidation.error);
|
|
145
|
+
}
|
|
146
|
+
return this.security.escapeIdentifier(c);
|
|
147
|
+
}).join(", ");
|
|
148
|
+
const indexQuery = `CREATE ${indexType} ${this.security.escapeIdentifier(index.name)} ON ${escapedTableName} (${indexColumns})`;
|
|
93
149
|
await this.db.query(indexQuery);
|
|
94
150
|
queryCount++;
|
|
95
151
|
}
|
|
@@ -115,8 +171,16 @@ class DdlTools {
|
|
|
115
171
|
async alterTable(params) {
|
|
116
172
|
try {
|
|
117
173
|
const { table_name, operations } = params;
|
|
174
|
+
const tableValidation = this.validateIdentifier(table_name, "table name");
|
|
175
|
+
if (!tableValidation.valid) {
|
|
176
|
+
return { status: "error", error: tableValidation.error };
|
|
177
|
+
}
|
|
178
|
+
if (!Array.isArray(operations) || operations.length === 0) {
|
|
179
|
+
return { status: "error", error: "At least one alter operation is required" };
|
|
180
|
+
}
|
|
181
|
+
const escapedTableName = this.security.escapeIdentifier(table_name);
|
|
118
182
|
for (const op of operations) {
|
|
119
|
-
let query = `ALTER TABLE
|
|
183
|
+
let query = `ALTER TABLE ${escapedTableName}`;
|
|
120
184
|
switch (op.type) {
|
|
121
185
|
case "add_column":
|
|
122
186
|
if (!op.column_name || !op.column_type) {
|
|
@@ -125,7 +189,13 @@ class DdlTools {
|
|
|
125
189
|
error: "column_name and column_type required for add_column",
|
|
126
190
|
};
|
|
127
191
|
}
|
|
128
|
-
|
|
192
|
+
const addColumnValidation = this.validateIdentifier(op.column_name, "column name");
|
|
193
|
+
if (!addColumnValidation.valid)
|
|
194
|
+
return { status: "error", error: addColumnValidation.error };
|
|
195
|
+
const addTypeValidation = this.validateColumnType(op.column_type);
|
|
196
|
+
if (!addTypeValidation.valid)
|
|
197
|
+
return { status: "error", error: addTypeValidation.error };
|
|
198
|
+
query += ` ADD COLUMN ${this.security.escapeIdentifier(op.column_name)} ${op.column_type.trim()}`;
|
|
129
199
|
if (op.nullable === false)
|
|
130
200
|
query += " NOT NULL";
|
|
131
201
|
if (op.default !== undefined) {
|
|
@@ -141,7 +211,10 @@ class DdlTools {
|
|
|
141
211
|
error: "column_name required for drop_column",
|
|
142
212
|
};
|
|
143
213
|
}
|
|
144
|
-
|
|
214
|
+
const dropColumnValidation = this.validateIdentifier(op.column_name, "column name");
|
|
215
|
+
if (!dropColumnValidation.valid)
|
|
216
|
+
return { status: "error", error: dropColumnValidation.error };
|
|
217
|
+
query += ` DROP COLUMN ${this.security.escapeIdentifier(op.column_name)}`;
|
|
145
218
|
break;
|
|
146
219
|
case "modify_column":
|
|
147
220
|
if (!op.column_name || !op.column_type) {
|
|
@@ -150,7 +223,13 @@ class DdlTools {
|
|
|
150
223
|
error: "column_name and column_type required for modify_column",
|
|
151
224
|
};
|
|
152
225
|
}
|
|
153
|
-
|
|
226
|
+
const modifyColumnValidation = this.validateIdentifier(op.column_name, "column name");
|
|
227
|
+
if (!modifyColumnValidation.valid)
|
|
228
|
+
return { status: "error", error: modifyColumnValidation.error };
|
|
229
|
+
const modifyTypeValidation = this.validateColumnType(op.column_type);
|
|
230
|
+
if (!modifyTypeValidation.valid)
|
|
231
|
+
return { status: "error", error: modifyTypeValidation.error };
|
|
232
|
+
query += ` MODIFY COLUMN ${this.security.escapeIdentifier(op.column_name)} ${op.column_type.trim()}`;
|
|
154
233
|
if (op.nullable === false)
|
|
155
234
|
query += " NOT NULL";
|
|
156
235
|
if (op.default !== undefined) {
|
|
@@ -166,7 +245,16 @@ class DdlTools {
|
|
|
166
245
|
error: "column_name, new_column_name, and column_type required for rename_column",
|
|
167
246
|
};
|
|
168
247
|
}
|
|
169
|
-
|
|
248
|
+
const oldColumnValidation = this.validateIdentifier(op.column_name, "column name");
|
|
249
|
+
if (!oldColumnValidation.valid)
|
|
250
|
+
return { status: "error", error: oldColumnValidation.error };
|
|
251
|
+
const newColumnValidation = this.validateIdentifier(op.new_column_name, "new column name");
|
|
252
|
+
if (!newColumnValidation.valid)
|
|
253
|
+
return { status: "error", error: newColumnValidation.error };
|
|
254
|
+
const renameTypeValidation = this.validateColumnType(op.column_type);
|
|
255
|
+
if (!renameTypeValidation.valid)
|
|
256
|
+
return { status: "error", error: renameTypeValidation.error };
|
|
257
|
+
query += ` CHANGE COLUMN ${this.security.escapeIdentifier(op.column_name)} ${this.security.escapeIdentifier(op.new_column_name)} ${op.column_type.trim()}`;
|
|
170
258
|
break;
|
|
171
259
|
case "add_index":
|
|
172
260
|
if (!op.index_name || !op.index_columns) {
|
|
@@ -175,9 +263,18 @@ class DdlTools {
|
|
|
175
263
|
error: "index_name and index_columns required for add_index",
|
|
176
264
|
};
|
|
177
265
|
}
|
|
266
|
+
const addIndexValidation = this.validateIdentifier(op.index_name, "index name");
|
|
267
|
+
if (!addIndexValidation.valid)
|
|
268
|
+
return { status: "error", error: addIndexValidation.error };
|
|
178
269
|
const indexType = op.unique ? "UNIQUE INDEX" : "INDEX";
|
|
179
|
-
const columns = op.index_columns.map((c) =>
|
|
180
|
-
|
|
270
|
+
const columns = op.index_columns.map((c) => {
|
|
271
|
+
const columnValidation = this.validateIdentifier(c, "index column name");
|
|
272
|
+
if (!columnValidation.valid) {
|
|
273
|
+
throw new Error(columnValidation.error);
|
|
274
|
+
}
|
|
275
|
+
return this.security.escapeIdentifier(c);
|
|
276
|
+
}).join(", ");
|
|
277
|
+
query += ` ADD ${indexType} ${this.security.escapeIdentifier(op.index_name)} (${columns})`;
|
|
181
278
|
break;
|
|
182
279
|
case "drop_index":
|
|
183
280
|
if (!op.index_name) {
|
|
@@ -186,7 +283,10 @@ class DdlTools {
|
|
|
186
283
|
error: "index_name required for drop_index",
|
|
187
284
|
};
|
|
188
285
|
}
|
|
189
|
-
|
|
286
|
+
const dropIndexValidation = this.validateIdentifier(op.index_name, "index name");
|
|
287
|
+
if (!dropIndexValidation.valid)
|
|
288
|
+
return { status: "error", error: dropIndexValidation.error };
|
|
289
|
+
query += ` DROP INDEX ${this.security.escapeIdentifier(op.index_name)}`;
|
|
190
290
|
break;
|
|
191
291
|
default:
|
|
192
292
|
return {
|
|
@@ -218,8 +318,12 @@ class DdlTools {
|
|
|
218
318
|
async dropTable(params) {
|
|
219
319
|
try {
|
|
220
320
|
const { table_name, if_exists } = params;
|
|
321
|
+
const tableValidation = this.validateIdentifier(table_name, "table name");
|
|
322
|
+
if (!tableValidation.valid) {
|
|
323
|
+
return { status: "error", error: tableValidation.error };
|
|
324
|
+
}
|
|
221
325
|
const ifExistsClause = if_exists ? "IF EXISTS " : "";
|
|
222
|
-
const query = `DROP TABLE ${ifExistsClause}
|
|
326
|
+
const query = `DROP TABLE ${ifExistsClause}${this.security.escapeIdentifier(table_name)}`;
|
|
223
327
|
await this.db.query(query);
|
|
224
328
|
return {
|
|
225
329
|
status: "success",
|
|
@@ -242,13 +346,14 @@ class DdlTools {
|
|
|
242
346
|
async executeDdl(params) {
|
|
243
347
|
try {
|
|
244
348
|
const { query } = params;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
349
|
+
const queryValidation = this.security.validateQuery(query);
|
|
350
|
+
if (!queryValidation.valid) {
|
|
351
|
+
return {
|
|
352
|
+
status: "error",
|
|
353
|
+
error: `DDL validation failed: ${queryValidation.error}`,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
const isDdl = ["CREATE", "ALTER", "DROP", "TRUNCATE", "RENAME"].includes(queryValidation.queryType || "");
|
|
252
357
|
if (!isDdl) {
|
|
253
358
|
return {
|
|
254
359
|
status: "error",
|
|
@@ -19,6 +19,11 @@ function validateToolArguments(name, args) {
|
|
|
19
19
|
case "execute_write_query":
|
|
20
20
|
case "execute_ddl":
|
|
21
21
|
return (0, inputValidation_js_1.validateQuery)({ query: args?.query || "" });
|
|
22
|
+
case "export_query_to_csv":
|
|
23
|
+
return (0, inputValidation_js_1.validateQuery)({
|
|
24
|
+
query: args?.query || "",
|
|
25
|
+
params: args?.params,
|
|
26
|
+
});
|
|
22
27
|
case "bulk_insert":
|
|
23
28
|
return (0, inputValidation_js_1.validateBulkInsert)(args);
|
|
24
29
|
case "list_tables":
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
interface RuntimeToolDefinition {
|
|
2
|
+
name: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
inputSchema?: any;
|
|
5
|
+
input_schema?: any;
|
|
6
|
+
output_schema?: any;
|
|
7
|
+
}
|
|
8
|
+
interface ListAllToolsOptions {
|
|
9
|
+
tools?: RuntimeToolDefinition[];
|
|
10
|
+
enabledToolNames?: string[];
|
|
11
|
+
accessProfile?: any;
|
|
12
|
+
serverName?: string;
|
|
13
|
+
serverVersion?: string;
|
|
14
|
+
}
|
|
1
15
|
export declare class UtilityTools {
|
|
2
16
|
private db;
|
|
3
17
|
constructor();
|
|
@@ -41,7 +55,7 @@ export declare class UtilityTools {
|
|
|
41
55
|
/**
|
|
42
56
|
* Lists all available tools in this MySQL MCP server
|
|
43
57
|
*/
|
|
44
|
-
listAllTools(): Promise<{
|
|
58
|
+
listAllTools(params?: ListAllToolsOptions): Promise<{
|
|
45
59
|
status: string;
|
|
46
60
|
data?: any;
|
|
47
61
|
error?: string;
|
|
@@ -58,3 +72,4 @@ export declare class UtilityTools {
|
|
|
58
72
|
error?: string;
|
|
59
73
|
}>;
|
|
60
74
|
}
|
|
75
|
+
export {};
|
|
@@ -295,33 +295,88 @@ class UtilityTools {
|
|
|
295
295
|
/**
|
|
296
296
|
* Lists all available tools in this MySQL MCP server
|
|
297
297
|
*/
|
|
298
|
-
async listAllTools() {
|
|
298
|
+
async listAllTools(params) {
|
|
299
299
|
try {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
300
|
+
let source = "runtime";
|
|
301
|
+
let serverName = params?.serverName || "mysql-mcp-server";
|
|
302
|
+
let serverVersion = params?.serverVersion || "unknown";
|
|
303
|
+
let toolDefinitions = params?.tools || [];
|
|
304
|
+
if (toolDefinitions.length === 0) {
|
|
305
|
+
const manifestPath = path_1.default.resolve(__dirname, "..", "..", "manifest.json");
|
|
306
|
+
if (!fs_1.default.existsSync(manifestPath)) {
|
|
307
|
+
return {
|
|
308
|
+
status: "error",
|
|
309
|
+
error: "Runtime tool catalog was not supplied and manifest.json was not found.",
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, "utf-8"));
|
|
313
|
+
source = "manifest_fallback";
|
|
314
|
+
serverName = manifest.name || serverName;
|
|
315
|
+
serverVersion = manifest.version || serverVersion;
|
|
316
|
+
toolDefinitions = manifest.tools || [];
|
|
307
317
|
}
|
|
308
|
-
const
|
|
309
|
-
const
|
|
310
|
-
const tools = manifest.tools.map((tool) => ({
|
|
318
|
+
const enabledToolNames = new Set(params?.enabledToolNames || toolDefinitions.map((tool) => tool.name));
|
|
319
|
+
const tools = toolDefinitions.map((tool) => ({
|
|
311
320
|
name: tool.name,
|
|
312
321
|
description: tool.description,
|
|
313
|
-
|
|
314
|
-
|
|
322
|
+
enabled: enabledToolNames.has(tool.name),
|
|
323
|
+
input_schema: tool.inputSchema || tool.input_schema || {},
|
|
324
|
+
output_schema: tool.output_schema || { type: "object" },
|
|
315
325
|
}));
|
|
316
326
|
return {
|
|
317
327
|
status: "success",
|
|
318
328
|
data: {
|
|
329
|
+
source,
|
|
319
330
|
total_tools: tools.length,
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
331
|
+
enabled_tools: tools.filter((tool) => tool.enabled).length,
|
|
332
|
+
disabled_tools: tools.filter((tool) => !tool.enabled).length,
|
|
333
|
+
server_name: serverName,
|
|
334
|
+
server_version: serverVersion,
|
|
335
|
+
access_profile: params?.accessProfile,
|
|
336
|
+
agent_guidance: {
|
|
337
|
+
recommended_first_calls: [
|
|
338
|
+
"describe_connection",
|
|
339
|
+
"list_databases",
|
|
340
|
+
"list_tables",
|
|
341
|
+
"get_schema_rag_context",
|
|
342
|
+
],
|
|
343
|
+
workflows: {
|
|
344
|
+
explore_schema: [
|
|
345
|
+
"describe_connection",
|
|
346
|
+
"list_tables",
|
|
347
|
+
"get_database_summary",
|
|
348
|
+
"get_schema_rag_context",
|
|
349
|
+
],
|
|
350
|
+
inspect_table: [
|
|
351
|
+
"read_table_schema",
|
|
352
|
+
"get_column_statistics",
|
|
353
|
+
"read_records",
|
|
354
|
+
],
|
|
355
|
+
run_safe_query: [
|
|
356
|
+
"get_schema_rag_context",
|
|
357
|
+
"run_select_query with dry_run=true",
|
|
358
|
+
"run_select_query",
|
|
359
|
+
],
|
|
360
|
+
export_data: [
|
|
361
|
+
"export_table_to_csv for simple table exports",
|
|
362
|
+
"export_query_to_csv for SELECT query exports",
|
|
363
|
+
],
|
|
364
|
+
modify_data: [
|
|
365
|
+
"begin_transaction",
|
|
366
|
+
"execute_in_transaction",
|
|
367
|
+
"commit_transaction or rollback_transaction",
|
|
368
|
+
],
|
|
369
|
+
},
|
|
370
|
+
selection_rules: [
|
|
371
|
+
"Use get_schema_rag_context before generating SQL to reduce token usage.",
|
|
372
|
+
"Use run_select_query only for SELECT statements.",
|
|
373
|
+
"Use execute_write_query for INSERT, UPDATE, and DELETE.",
|
|
374
|
+
"Use execute_ddl only for CREATE, ALTER, DROP, TRUNCATE, and RENAME.",
|
|
375
|
+
"Prefer structured tools over raw SQL when possible.",
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
tools,
|
|
379
|
+
},
|
|
325
380
|
};
|
|
326
381
|
}
|
|
327
382
|
catch (error) {
|