@berthojoris/mcp-mysql-server 1.16.4 → 1.18.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/bin/mcp-mysql.js CHANGED
@@ -10,55 +10,32 @@ const path = require("path");
10
10
  const { spawn } = require("child_process");
11
11
  require("dotenv").config();
12
12
 
13
- // Get MySQL connection string, permissions, categories, and optional preset
14
- const args = process.argv.slice(2);
15
- const mysqlUrl = args.shift();
16
-
17
- let permissions;
18
- let categories;
19
- let preset;
20
-
21
- for (let i = 0; i < args.length; i++) {
22
- const arg = args[i];
23
- const normalized = (arg || "").toLowerCase();
24
-
25
- if (normalized === "--preset") {
26
- preset = args[i + 1];
27
- i++;
28
- continue;
29
- }
30
-
31
- if (normalized.startsWith("--preset=")) {
32
- preset = arg.split("=")[1];
33
- continue;
34
- }
35
-
36
- if (normalized.startsWith("preset:")) {
37
- preset = arg.split(":")[1];
38
- continue;
39
- }
40
-
41
- if (["readonly", "analyst", "dba-lite", "dba_lite", "dba lite"].includes(normalized)) {
42
- preset = arg;
43
- continue;
44
- }
45
-
46
- if (permissions === undefined) {
47
- permissions = arg;
48
- continue;
49
- }
50
-
51
- if (categories === undefined) {
52
- categories = arg;
53
- continue;
54
- }
55
- }
13
+ // Get MySQL connection string, permissions, and optional categories
14
+ const args = process.argv.slice(2);
15
+ const mysqlUrl = args.shift();
16
+
17
+ let permissions;
18
+ let categories;
19
+
20
+ for (let i = 0; i < args.length; i++) {
21
+ const arg = args[i];
22
+
23
+ if (permissions === undefined) {
24
+ permissions = arg;
25
+ continue;
26
+ }
27
+
28
+ if (categories === undefined) {
29
+ categories = arg;
30
+ continue;
31
+ }
32
+ }
56
33
 
57
34
  if (!mysqlUrl) {
58
35
  console.error("Error: MySQL connection URL is required");
59
- console.error(
60
- "Usage: mcp-mysql mysql://user:password@host:port/dbname [permissions] [categories] [--preset <name>]",
61
- );
36
+ console.error(
37
+ "Usage: mcp-mysql mysql://user:password@host:port/dbname [permissions] [categories]",
38
+ );
62
39
  console.error("");
63
40
  console.error("Examples:");
64
41
  console.error(" # All tools enabled (no filtering)");
@@ -72,17 +49,9 @@ if (!mysqlUrl) {
72
49
  console.error(
73
50
  " # Dual-layer: Permissions + Categories (fine-grained control)",
74
51
  );
75
- console.error(
76
- ' mcp-mysql mysql://root:pass@localhost:3306/mydb "list,read,utility" "database_discovery,performance_monitoring"',
77
- );
78
- console.error("");
79
- console.error(" # Adaptive presets (auto-merge with overrides)");
80
- console.error(
81
- ' mcp-mysql mysql://root:pass@localhost:3306/mydb --preset readonly',
82
- );
83
- console.error(
84
- ' mcp-mysql mysql://root:pass@localhost:3306/mydb --preset analyst "performance_monitoring"',
85
- );
52
+ console.error(
53
+ ' mcp-mysql mysql://root:pass@localhost:3306/mydb "list,read,utility" "database_discovery,performance_monitoring"',
54
+ );
86
55
  console.error("");
87
56
  console.error("Permissions (Layer 1 - Broad Control):");
88
57
  console.error(
@@ -105,26 +74,20 @@ if (!mysqlUrl) {
105
74
  console.error(
106
75
  " performance_monitoring, cache_management, query_optimization,",
107
76
  );
108
- console.error(
109
- " backup_restore, import_export, data_migration, schema_migrations",
110
- );
111
- console.error("");
112
- console.error("Filtering Logic:");
77
+ console.error(
78
+ " backup_restore, import_export, data_migration, schema_migrations",
79
+ );
80
+ console.error("");
81
+ console.error("Filtering Logic:");
113
82
  console.error(
114
83
  " - If only permissions: All tools within those permissions enabled",
115
84
  );
116
- console.error(
117
- " - If permissions + categories: Only tools matching BOTH layers enabled",
118
- );
119
- console.error(" - If nothing specified: All 119 tools enabled");
120
- console.error("");
121
- console.error("Presets:");
122
- console.error(" readonly, analyst, dba-lite");
123
- console.error(
124
- " Presets merge with provided permissions/categories so you can add project-specific overrides.",
125
- );
126
- process.exit(1);
127
- }
85
+ console.error(
86
+ " - If permissions + categories: Only tools matching BOTH layers enabled",
87
+ );
88
+ console.error(" - If nothing specified: All tools enabled");
89
+ process.exit(1);
90
+ }
128
91
 
129
92
  // Parse the MySQL URL to extract connection details
130
93
  let connectionConfig;
@@ -168,39 +131,26 @@ const dbMessage = database
168
131
  ? `${connectionConfig.host}:${connectionConfig.port}/${database}`
169
132
  : `${connectionConfig.host}:${connectionConfig.port} (no specific database selected)`;
170
133
 
171
- // Set permissions/categories/preset as environment variables if provided
172
- if (permissions) {
173
- process.env.MCP_PERMISSIONS = permissions;
174
- console.error(`Permissions (Layer 1): ${permissions}`);
175
- }
176
-
177
- if (categories) {
178
- process.env.MCP_CATEGORIES = categories;
179
- console.error(`Categories (Layer 2): ${categories}`);
180
- }
181
-
182
- if (preset) {
183
- process.env.MCP_PRESET = preset;
184
- console.error(`Permission preset: ${preset}`);
185
- }
186
-
187
- if (!permissions && !categories && !preset) {
188
- console.error("Access Control: All tools enabled (no filtering)");
189
- } else if (preset && !permissions && !categories) {
190
- console.error("Filtering Mode: Preset-based (auto permissions + categories)");
191
- } else if (permissions && categories) {
192
- console.error(
193
- `Filtering Mode: Dual-layer (Permissions + Categories${preset ? ", preset merged" : ""})`,
194
- );
195
- } else if (permissions && !categories) {
196
- console.error(
197
- `Filtering Mode: Permission-based only${preset ? " (preset merged)" : ""}`,
198
- );
199
- } else if (!permissions && categories) {
200
- console.error(
201
- `Filtering Mode: Category-only${preset ? " (preset merged)" : ""}`,
202
- );
203
- }
134
+ // Set permissions/categories as environment variables if provided
135
+ if (permissions) {
136
+ process.env.MCP_PERMISSIONS = permissions;
137
+ console.error(`Permissions (Layer 1): ${permissions}`);
138
+ }
139
+
140
+ if (categories) {
141
+ process.env.MCP_CATEGORIES = categories;
142
+ console.error(`Categories (Layer 2): ${categories}`);
143
+ }
144
+
145
+ if (!permissions && !categories) {
146
+ console.error("Access Control: All tools enabled (no filtering)");
147
+ } else if (permissions && categories) {
148
+ console.error("Filtering Mode: Dual-layer (Permissions + Categories)");
149
+ } else if (permissions && !categories) {
150
+ console.error("Filtering Mode: Permission-based only");
151
+ } else if (!permissions && categories) {
152
+ console.error("Filtering Mode: Category-only");
153
+ }
204
154
 
205
155
  // Log to stderr (not stdout, which is used for MCP protocol)
206
156
  console.error(`Starting MySQL MCP server with connection to ${dbMessage}`);
@@ -43,17 +43,6 @@ export declare enum DocCategory {
43
43
  ANALYSIS = "analysis",
44
44
  AI_ENHANCEMENT = "ai_enhancement"
45
45
  }
46
- /**
47
- * Permission preset bundles for faster, safer configuration
48
- */
49
- export interface PermissionPreset {
50
- name: string;
51
- description: string;
52
- permissions: ToolCategory[];
53
- categories: DocCategory[];
54
- allowedTools?: string[];
55
- deniedTools?: string[];
56
- }
57
46
  /**
58
47
  * Map of tool names to their legacy categories
59
48
  */
@@ -71,22 +60,14 @@ export declare const toolDocCategoryMap: Record<string, DocCategory>;
71
60
  export declare class FeatureConfig {
72
61
  private enabledLegacyCategories;
73
62
  private enabledDocCategories;
74
- private allowedTools;
75
- private deniedTools;
76
63
  private originalPermissionsString;
77
64
  private originalCategoriesString;
78
65
  private useDualLayer;
79
- private activePreset?;
80
- private presetName?;
81
- constructor(permissionsStr?: string, categoriesStr?: string, presetName?: string);
66
+ constructor(permissionsStr?: string, categoriesStr?: string);
82
67
  /**
83
68
  * Normalize and merge preset + user-supplied configuration lists
84
69
  */
85
70
  private mergeConfigStrings;
86
- /**
87
- * Resolve a preset name to its configuration
88
- */
89
- private resolvePreset;
90
71
  /**
91
72
  * Parse permissions and categories for dual-layer filtering
92
73
  * Layer 1 (permissions): Broad control using legacy categories
@@ -96,7 +77,7 @@ export declare class FeatureConfig {
96
77
  /**
97
78
  * Update configuration at runtime
98
79
  */
99
- setConfig(permissionsStr: string, categoriesStr?: string, presetName?: string): void;
80
+ setConfig(permissionsStr: string, categoriesStr?: string): void;
100
81
  /**
101
82
  * Check if a specific tool is enabled
102
83
  * Dual-layer logic:
@@ -136,18 +117,10 @@ export declare class FeatureConfig {
136
117
  * Check if using dual-layer filtering mode
137
118
  */
138
119
  isUsingDualLayer(): boolean;
139
- /**
140
- * Get the active preset (if any)
141
- */
142
- getActivePreset(): PermissionPreset | undefined;
143
120
  /**
144
121
  * Snapshot of the resolved configuration for logging/telemetry
145
122
  */
146
123
  getConfigSnapshot(): {
147
- preset?: {
148
- name: string;
149
- description: string;
150
- };
151
124
  permissions: string;
152
125
  categories: string;
153
126
  filteringMode: string;
@@ -54,122 +54,6 @@ var DocCategory;
54
54
  DocCategory["ANALYSIS"] = "analysis";
55
55
  DocCategory["AI_ENHANCEMENT"] = "ai_enhancement";
56
56
  })(DocCategory || (exports.DocCategory = DocCategory = {}));
57
- const normalizePresetName = (value) => (value || "").toLowerCase().replace(/[\s_-]/g, "");
58
- const permissionPresets = {
59
- readonly: {
60
- name: "readonly",
61
- description: "Safe read-only profile with discovery, querying, exports, and diagnostics",
62
- permissions: [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY],
63
- categories: [
64
- DocCategory.DATABASE_DISCOVERY,
65
- DocCategory.CRUD_OPERATIONS,
66
- DocCategory.CUSTOM_QUERIES,
67
- DocCategory.UTILITIES,
68
- DocCategory.IMPORT_EXPORT,
69
- DocCategory.PERFORMANCE_MONITORING,
70
- DocCategory.ANALYSIS,
71
- ],
72
- },
73
- analyst: {
74
- name: "analyst",
75
- description: "Exploratory analytics profile with query insights and safe exports",
76
- permissions: [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY],
77
- categories: [
78
- DocCategory.DATABASE_DISCOVERY,
79
- DocCategory.CRUD_OPERATIONS,
80
- DocCategory.CUSTOM_QUERIES,
81
- DocCategory.UTILITIES,
82
- DocCategory.IMPORT_EXPORT,
83
- DocCategory.PERFORMANCE_MONITORING,
84
- DocCategory.ANALYSIS,
85
- DocCategory.QUERY_OPTIMIZATION,
86
- DocCategory.CACHE_MANAGEMENT,
87
- DocCategory.SERVER_MANAGEMENT,
88
- DocCategory.AI_ENHANCEMENT,
89
- ],
90
- },
91
- dbalite: {
92
- name: "dba-lite",
93
- description: "Admin-lite profile for schema care, migrations, and maintenance",
94
- permissions: [
95
- ToolCategory.LIST,
96
- ToolCategory.READ,
97
- ToolCategory.UTILITY,
98
- ToolCategory.DDL,
99
- ToolCategory.TRANSACTION,
100
- ToolCategory.PROCEDURE,
101
- ],
102
- categories: [
103
- DocCategory.DATABASE_DISCOVERY,
104
- DocCategory.CUSTOM_QUERIES,
105
- DocCategory.UTILITIES,
106
- DocCategory.SERVER_MANAGEMENT,
107
- DocCategory.SCHEMA_MANAGEMENT,
108
- DocCategory.TABLE_MAINTENANCE,
109
- DocCategory.INDEX_MANAGEMENT,
110
- DocCategory.CONSTRAINT_MANAGEMENT,
111
- DocCategory.BACKUP_RESTORE,
112
- DocCategory.SCHEMA_MIGRATIONS,
113
- DocCategory.PERFORMANCE_MONITORING,
114
- DocCategory.VIEWS_MANAGEMENT,
115
- DocCategory.TRIGGERS_MANAGEMENT,
116
- DocCategory.FUNCTIONS_MANAGEMENT,
117
- DocCategory.STORED_PROCEDURES,
118
- ],
119
- },
120
- dev: {
121
- name: "dev",
122
- description: "Development profile with full access to all tools",
123
- permissions: Object.values(ToolCategory),
124
- categories: Object.values(DocCategory),
125
- deniedTools: [], // Explicitly allow everything
126
- },
127
- stage: {
128
- name: "stage",
129
- description: "Staging profile with data modification but no destructive DDL",
130
- permissions: [
131
- ToolCategory.LIST,
132
- ToolCategory.READ,
133
- ToolCategory.CREATE,
134
- ToolCategory.UPDATE,
135
- ToolCategory.DELETE,
136
- ToolCategory.UTILITY,
137
- ToolCategory.TRANSACTION,
138
- ],
139
- categories: [
140
- DocCategory.DATABASE_DISCOVERY,
141
- DocCategory.CRUD_OPERATIONS,
142
- DocCategory.BULK_OPERATIONS,
143
- DocCategory.CUSTOM_QUERIES,
144
- DocCategory.UTILITIES,
145
- DocCategory.TRANSACTION_MANAGEMENT,
146
- DocCategory.IMPORT_EXPORT,
147
- DocCategory.DATA_MIGRATION,
148
- DocCategory.PERFORMANCE_MONITORING,
149
- DocCategory.ANALYSIS,
150
- ],
151
- deniedTools: ["drop_table", "truncate_table", "drop_database"],
152
- },
153
- prod: {
154
- name: "prod",
155
- description: "Production profile with strict read-only access and safety checks",
156
- permissions: [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY],
157
- categories: [
158
- DocCategory.DATABASE_DISCOVERY,
159
- DocCategory.CRUD_OPERATIONS, // Read only via permissions
160
- DocCategory.CUSTOM_QUERIES,
161
- DocCategory.UTILITIES,
162
- DocCategory.PERFORMANCE_MONITORING,
163
- DocCategory.ANALYSIS,
164
- ],
165
- deniedTools: [
166
- "create_table", "alter_table", "drop_table", "truncate_table",
167
- "create_record", "update_record", "delete_record",
168
- "bulk_insert", "bulk_update", "bulk_delete",
169
- "execute_sql", "execute_ddl"
170
- ],
171
- },
172
- };
173
57
  /**
174
58
  * Map of tool names to their legacy categories
175
59
  */
@@ -325,6 +209,12 @@ exports.toolCategoryMap = {
325
209
  designSchemaFromRequirements: ToolCategory.UTILITY,
326
210
  auditDatabaseSecurity: ToolCategory.UTILITY,
327
211
  recommendIndexes: ToolCategory.UTILITY,
212
+ // Phase 3: AI Enhancement Tools
213
+ generateTestData: ToolCategory.UTILITY,
214
+ analyzeSchemaPatterns: ToolCategory.UTILITY,
215
+ visualizeQuery: ToolCategory.UTILITY,
216
+ predictQueryPerformance: ToolCategory.UTILITY,
217
+ forecastDatabaseGrowth: ToolCategory.UTILITY,
328
218
  };
329
219
  /**
330
220
  * Map of tool names to their documentation categories (New Enhanced System)
@@ -491,6 +381,12 @@ exports.toolDocCategoryMap = {
491
381
  designSchemaFromRequirements: DocCategory.AI_ENHANCEMENT,
492
382
  auditDatabaseSecurity: DocCategory.AI_ENHANCEMENT,
493
383
  recommendIndexes: DocCategory.AI_ENHANCEMENT,
384
+ // Phase 3: AI Enhancement
385
+ generateTestData: DocCategory.AI_ENHANCEMENT,
386
+ analyzeSchemaPatterns: DocCategory.AI_ENHANCEMENT,
387
+ visualizeQuery: DocCategory.AI_ENHANCEMENT,
388
+ predictQueryPerformance: DocCategory.AI_ENHANCEMENT,
389
+ forecastDatabaseGrowth: DocCategory.AI_ENHANCEMENT,
494
390
  };
495
391
  /**
496
392
  * Mapping between legacy categories and documentation categories
@@ -558,52 +454,21 @@ const legacyToDocCategoryMap = {
558
454
  * - Layer 2 (Categories): Documentation categories (fine-grained control, optional)
559
455
  */
560
456
  class FeatureConfig {
561
- constructor(permissionsStr, categoriesStr, presetName) {
562
- const presetInput = presetName ||
563
- process.env.MCP_PERMISSION_PRESET ||
564
- process.env.MCP_PRESET ||
565
- "";
566
- this.activePreset = this.resolvePreset(presetInput);
567
- this.presetName = this.activePreset?.name;
568
- const presetRequested = !!presetInput.trim();
457
+ constructor(permissionsStr, categoriesStr) {
569
458
  // Support both old single-parameter and new dual-parameter signatures
570
459
  const permissionsInput = permissionsStr ||
571
460
  process.env.MCP_PERMISSIONS ||
572
461
  process.env.MCP_CONFIG ||
573
462
  "";
574
463
  const categoriesInput = categoriesStr || process.env.MCP_CATEGORIES || "";
575
- // Use preset values when available, otherwise fall back to user input
576
- // If an unknown preset is requested without explicit permissions/categories,
577
- // default to a safe read-only baseline rather than enabling everything.
578
- const basePermissions = this.activePreset
579
- ? this.activePreset.permissions.join(",")
580
- : presetRequested && !permissionsInput
581
- ? [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY].join(",")
582
- : "";
583
- const baseCategories = this.activePreset
584
- ? this.activePreset.categories.join(",")
585
- : presetRequested && !categoriesInput
586
- ? [
587
- DocCategory.DATABASE_DISCOVERY,
588
- DocCategory.CRUD_OPERATIONS,
589
- DocCategory.CUSTOM_QUERIES,
590
- DocCategory.UTILITIES,
591
- ].join(",")
592
- : "";
593
- if (presetRequested && !this.activePreset) {
594
- console.warn(`Preset '${presetInput}' not recognized. Falling back to safe read-only defaults.`);
595
- }
596
- const mergedPermissions = this.mergeConfigStrings(basePermissions, permissionsInput);
597
- const mergedCategories = this.mergeConfigStrings(baseCategories, categoriesInput);
464
+ const mergedPermissions = this.mergeConfigStrings("", permissionsInput);
465
+ const mergedCategories = this.mergeConfigStrings("", categoriesInput);
598
466
  this.originalPermissionsString = mergedPermissions;
599
467
  this.originalCategoriesString = mergedCategories;
600
468
  this.useDualLayer = !!mergedCategories.trim();
601
469
  const parsed = this.parseConfig(mergedPermissions, mergedCategories);
602
470
  this.enabledLegacyCategories = parsed.legacy;
603
471
  this.enabledDocCategories = parsed.doc;
604
- // Initialize Allow/Deny Lists
605
- this.allowedTools = new Set(this.activePreset?.allowedTools || []);
606
- this.deniedTools = new Set(this.activePreset?.deniedTools || []);
607
472
  }
608
473
  /**
609
474
  * Normalize and merge preset + user-supplied configuration lists
@@ -614,13 +479,6 @@ class FeatureConfig {
614
479
  .filter(Boolean);
615
480
  return Array.from(new Set(items)).join(",");
616
481
  }
617
- /**
618
- * Resolve a preset name to its configuration
619
- */
620
- resolvePreset(name) {
621
- const normalized = normalizePresetName(name);
622
- return normalized ? permissionPresets[normalized] : undefined;
623
- }
624
482
  /**
625
483
  * Parse permissions and categories for dual-layer filtering
626
484
  * Layer 1 (permissions): Broad control using legacy categories
@@ -662,9 +520,6 @@ class FeatureConfig {
662
520
  docCats.forEach((dc) => docSet.add(dc));
663
521
  });
664
522
  }
665
- // Re-initialize Allow/Deny Lists if preset changed
666
- this.allowedTools = new Set(this.activePreset?.allowedTools || []);
667
- this.deniedTools = new Set(this.activePreset?.deniedTools || []);
668
523
  return {
669
524
  legacy: legacySet,
670
525
  doc: docSet,
@@ -673,33 +528,9 @@ class FeatureConfig {
673
528
  /**
674
529
  * Update configuration at runtime
675
530
  */
676
- setConfig(permissionsStr, categoriesStr, presetName) {
677
- const presetRequested = presetName !== undefined ? !!presetName.trim() : !!this.presetName;
678
- this.activePreset =
679
- presetName === ""
680
- ? undefined
681
- : this.resolvePreset(presetName || this.presetName);
682
- this.presetName = this.activePreset?.name;
683
- const basePermissions = this.activePreset
684
- ? this.activePreset.permissions.join(",")
685
- : presetRequested && !permissionsStr
686
- ? [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY].join(",")
687
- : "";
688
- const baseCategories = this.activePreset
689
- ? this.activePreset.categories.join(",")
690
- : presetRequested && !categoriesStr
691
- ? [
692
- DocCategory.DATABASE_DISCOVERY,
693
- DocCategory.CRUD_OPERATIONS,
694
- DocCategory.CUSTOM_QUERIES,
695
- DocCategory.UTILITIES,
696
- ].join(",")
697
- : "";
698
- if (presetRequested && !this.activePreset) {
699
- console.warn(`Preset '${presetName}' not recognized. Falling back to safe read-only defaults.`);
700
- }
701
- const mergedPermissions = this.mergeConfigStrings(basePermissions, permissionsStr);
702
- const mergedCategories = this.mergeConfigStrings(baseCategories, categoriesStr || "");
531
+ setConfig(permissionsStr, categoriesStr) {
532
+ const mergedPermissions = this.mergeConfigStrings("", permissionsStr);
533
+ const mergedCategories = this.mergeConfigStrings("", categoriesStr || "");
703
534
  this.originalPermissionsString = mergedPermissions;
704
535
  this.originalCategoriesString = mergedCategories;
705
536
  this.useDualLayer = !!(mergedCategories && mergedCategories.trim());
@@ -721,15 +552,6 @@ class FeatureConfig {
721
552
  console.warn(`Unknown tool: ${toolName}`);
722
553
  return false;
723
554
  }
724
- // Layer 0: Check explicit Deny/Allow lists
725
- // Deny takes precedence
726
- if (this.deniedTools.has(toolName)) {
727
- return false;
728
- }
729
- // Allow overrides other checks
730
- if (this.allowedTools.has(toolName)) {
731
- return true;
732
- }
733
555
  // Layer 1: Check permission (legacy category)
734
556
  const hasPermission = legacyCategory
735
557
  ? this.enabledLegacyCategories.has(legacyCategory)
@@ -756,9 +578,6 @@ class FeatureConfig {
756
578
  if (!docCategory && !legacyCategory) {
757
579
  return `Unknown tool '${toolName}'. This tool is not recognized by the MCP server.`;
758
580
  }
759
- if (this.deniedTools.has(toolName)) {
760
- return `Permission denied: Tool '${toolName}' is explicitly denied by the current profile ('${this.presetName}').`;
761
- }
762
581
  const isAllEnabled = !this.originalPermissionsString.trim() &&
763
582
  !this.originalCategoriesString.trim();
764
583
  if (isAllEnabled) {
@@ -841,23 +660,11 @@ class FeatureConfig {
841
660
  isUsingDualLayer() {
842
661
  return this.useDualLayer;
843
662
  }
844
- /**
845
- * Get the active preset (if any)
846
- */
847
- getActivePreset() {
848
- return this.activePreset;
849
- }
850
663
  /**
851
664
  * Snapshot of the resolved configuration for logging/telemetry
852
665
  */
853
666
  getConfigSnapshot() {
854
667
  return {
855
- preset: this.activePreset
856
- ? {
857
- name: this.activePreset.name,
858
- description: this.activePreset.description,
859
- }
860
- : undefined,
861
668
  permissions: this.originalPermissionsString || "all",
862
669
  categories: this.originalCategoriesString ||
863
670
  (this.useDualLayer ? "" : "derived from permissions"),