@berthojoris/mcp-mysql-server 1.40.6 → 1.42.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.
@@ -2,6 +2,108 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validateToolArguments = validateToolArguments;
4
4
  const inputValidation_js_1 = require("../validation/inputValidation.js");
5
+ const validatePlanSeedDataArgs = (args) => {
6
+ const errors = [];
7
+ if (!Array.isArray(args.target_tables) || args.target_tables.length === 0) {
8
+ errors.push("target_tables must be a non-empty array");
9
+ }
10
+ else {
11
+ for (const table of args.target_tables) {
12
+ const validation = (0, inputValidation_js_1.validateTableName)(table);
13
+ if (!validation.valid) {
14
+ errors.push(`Invalid target table '${table}': ${validation.error}`);
15
+ }
16
+ }
17
+ }
18
+ if (args.database !== undefined) {
19
+ const validation = (0, inputValidation_js_1.validateValue)(args.database);
20
+ if (!validation.valid)
21
+ errors.push(validation.error || "Invalid database name");
22
+ }
23
+ validateRowsPerTable(args.rows_per_table, errors);
24
+ return errors.length ? { valid: false, errors } : { valid: true };
25
+ };
26
+ const validateRowsPerTable = (rowsPerTable, errors) => {
27
+ if (rowsPerTable === undefined)
28
+ return;
29
+ if (typeof rowsPerTable === "number") {
30
+ if (!Number.isFinite(rowsPerTable) || rowsPerTable < 0) {
31
+ errors.push("rows_per_table must be a non-negative number");
32
+ }
33
+ return;
34
+ }
35
+ if (typeof rowsPerTable === "object" && rowsPerTable !== null && !Array.isArray(rowsPerTable)) {
36
+ for (const [table, count] of Object.entries(rowsPerTable)) {
37
+ const tableValidation = (0, inputValidation_js_1.validateTableName)(table);
38
+ if (!tableValidation.valid)
39
+ errors.push(`Invalid rows_per_table key '${table}': ${tableValidation.error}`);
40
+ if (typeof count !== "number" || !Number.isFinite(count) || count < 0) {
41
+ errors.push(`rows_per_table.${table} must be a non-negative number`);
42
+ }
43
+ }
44
+ return;
45
+ }
46
+ errors.push("rows_per_table must be a number or object map");
47
+ };
48
+ const validateTableList = (value, key, errors) => {
49
+ if (value === undefined)
50
+ return;
51
+ if (!Array.isArray(value)) {
52
+ errors.push(`${key} must be an array`);
53
+ return;
54
+ }
55
+ for (const table of value) {
56
+ const validation = (0, inputValidation_js_1.validateTableName)(table);
57
+ if (!validation.valid)
58
+ errors.push(`Invalid ${key} table '${table}': ${validation.error}`);
59
+ }
60
+ };
61
+ const validateInferSeedRulesArgs = (args) => {
62
+ const errors = [];
63
+ if (args.database !== undefined) {
64
+ const validation = (0, inputValidation_js_1.validateValue)(args.database);
65
+ if (!validation.valid)
66
+ errors.push(validation.error || "Invalid database name");
67
+ }
68
+ validateTableList(args.tables, "tables", errors);
69
+ if (args.domain !== undefined && !["auto", "generic", "ecommerce", "pos", "crm"].includes(args.domain)) {
70
+ errors.push("domain must be one of auto, generic, ecommerce, pos, crm");
71
+ }
72
+ if (args.sample_size !== undefined && (!Number.isFinite(Number(args.sample_size)) || Number(args.sample_size) < 0)) {
73
+ errors.push("sample_size must be a non-negative number");
74
+ }
75
+ if (args.max_tables !== undefined && (!Number.isFinite(Number(args.max_tables)) || Number(args.max_tables) < 1)) {
76
+ errors.push("max_tables must be a positive number");
77
+ }
78
+ return errors.length ? { valid: false, errors } : { valid: true };
79
+ };
80
+ const validateSeedFromTemplateArgs = (args) => {
81
+ const errors = [];
82
+ if (!args.template || !["ecommerce", "pos", "crm"].includes(args.template)) {
83
+ errors.push("template must be one of ecommerce, pos, crm");
84
+ }
85
+ if (args.database !== undefined) {
86
+ const validation = (0, inputValidation_js_1.validateValue)(args.database);
87
+ if (!validation.valid)
88
+ errors.push(validation.error || "Invalid database name");
89
+ }
90
+ if (args.scale !== undefined && !["small", "medium", "large"].includes(args.scale)) {
91
+ errors.push("scale must be one of small, medium, large");
92
+ }
93
+ validateTableList(args.include, "include", errors);
94
+ validateTableList(args.exclude, "exclude", errors);
95
+ validateRowsPerTable(args.rows_per_table, errors);
96
+ return errors.length ? { valid: false, errors } : { valid: true };
97
+ };
98
+ const validatePlanIdArgs = (args) => {
99
+ if (!args.plan_id || typeof args.plan_id !== "string") {
100
+ return { valid: false, errors: ["plan_id is required"] };
101
+ }
102
+ if (!/^seed_plan_[A-Za-z0-9_]+$/.test(args.plan_id)) {
103
+ return { valid: false, errors: ["plan_id has invalid format"] };
104
+ }
105
+ return { valid: true };
106
+ };
5
107
  function validateToolArguments(name, args) {
6
108
  if (!args)
7
109
  return { valid: true };
@@ -19,8 +121,23 @@ function validateToolArguments(name, args) {
19
121
  case "execute_write_query":
20
122
  case "execute_ddl":
21
123
  return (0, inputValidation_js_1.validateQuery)({ query: args?.query || "" });
124
+ case "export_query_to_csv":
125
+ return (0, inputValidation_js_1.validateQuery)({
126
+ query: args?.query || "",
127
+ params: args?.params,
128
+ });
22
129
  case "bulk_insert":
23
130
  return (0, inputValidation_js_1.validateBulkInsert)(args);
131
+ case "plan_seed_data":
132
+ return validatePlanSeedDataArgs(args);
133
+ case "generate_seed_preview":
134
+ case "execute_seed_plan":
135
+ case "validate_seed_integrity":
136
+ return validatePlanIdArgs(args);
137
+ case "infer_seed_rules":
138
+ return validateInferSeedRulesArgs(args);
139
+ case "seed_from_template":
140
+ return validateSeedFromTemplateArgs(args);
24
141
  case "list_tables":
25
142
  case "get_schema_erd":
26
143
  case "get_schema_rag_context":
@@ -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,96 @@ 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
- // Read manifest.json to get tool definitions
301
- const manifestPath = path_1.default.resolve(__dirname, "..", "..", "manifest.json");
302
- if (!fs_1.default.existsSync(manifestPath)) {
303
- return {
304
- status: "error",
305
- error: "manifest.json not found in project root.",
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 manifestContent = fs_1.default.readFileSync(manifestPath, "utf-8");
309
- const manifest = JSON.parse(manifestContent);
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
- input_schema: tool.input_schema,
314
- output_schema: tool.output_schema
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
- server_name: manifest.name,
321
- server_version: manifest.version,
322
- server_description: manifest.description,
323
- tools: tools
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
+ seed_relational_data: [
370
+ "infer_seed_rules or seed_from_template when domain/sample-based rules are useful",
371
+ "plan_seed_data",
372
+ "generate_seed_preview",
373
+ "execute_seed_plan with dry_run=false and confirm_token after user approval",
374
+ "validate_seed_integrity",
375
+ ],
376
+ },
377
+ selection_rules: [
378
+ "Use get_schema_rag_context before generating SQL to reduce token usage.",
379
+ "Use run_select_query only for SELECT statements.",
380
+ "Use execute_write_query for INSERT, UPDATE, and DELETE.",
381
+ "Use execute_ddl only for CREATE, ALTER, DROP, TRUNCATE, and RENAME.",
382
+ "Use seed_operations for relational dummy data instead of manually chaining bulk_insert across foreign keys.",
383
+ "Prefer structured tools over raw SQL when possible.",
384
+ ],
385
+ },
386
+ tools,
387
+ },
325
388
  };
326
389
  }
327
390
  catch (error) {