mcp-database-inspector 2.0.1

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.
Files changed (105) hide show
  1. package/README.md +197 -0
  2. package/dist/database/connection.d.ts +13 -0
  3. package/dist/database/connection.d.ts.map +1 -0
  4. package/dist/database/connection.js +155 -0
  5. package/dist/database/connection.js.map +1 -0
  6. package/dist/database/manager.d.ts +28 -0
  7. package/dist/database/manager.d.ts.map +1 -0
  8. package/dist/database/manager.js +621 -0
  9. package/dist/database/manager.js.map +1 -0
  10. package/dist/database/postgres-connection.d.ts +10 -0
  11. package/dist/database/postgres-connection.d.ts.map +1 -0
  12. package/dist/database/postgres-connection.js +113 -0
  13. package/dist/database/postgres-connection.js.map +1 -0
  14. package/dist/database/types.d.ts +84 -0
  15. package/dist/database/types.d.ts.map +1 -0
  16. package/dist/database/types.js +6 -0
  17. package/dist/database/types.js.map +1 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +120 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/server.d.ts +14 -0
  23. package/dist/server.d.ts.map +1 -0
  24. package/dist/server.js +186 -0
  25. package/dist/server.js.map +1 -0
  26. package/dist/test-defaults.d.ts +2 -0
  27. package/dist/test-defaults.d.ts.map +1 -0
  28. package/dist/test-defaults.js +57 -0
  29. package/dist/test-defaults.js.map +1 -0
  30. package/dist/tools/analyze-query.d.ts +27 -0
  31. package/dist/tools/analyze-query.d.ts.map +1 -0
  32. package/dist/tools/analyze-query.js +71 -0
  33. package/dist/tools/analyze-query.js.map +1 -0
  34. package/dist/tools/execute-query.d.ts +33 -0
  35. package/dist/tools/execute-query.d.ts.map +1 -0
  36. package/dist/tools/execute-query.js +57 -0
  37. package/dist/tools/execute-query.js.map +1 -0
  38. package/dist/tools/get-foreign-keys.d.ts +38 -0
  39. package/dist/tools/get-foreign-keys.d.ts.map +1 -0
  40. package/dist/tools/get-foreign-keys.js +391 -0
  41. package/dist/tools/get-foreign-keys.js.map +1 -0
  42. package/dist/tools/get-indexes.d.ts +38 -0
  43. package/dist/tools/get-indexes.d.ts.map +1 -0
  44. package/dist/tools/get-indexes.js +472 -0
  45. package/dist/tools/get-indexes.js.map +1 -0
  46. package/dist/tools/information-schema-query.d.ts +33 -0
  47. package/dist/tools/information-schema-query.d.ts.map +1 -0
  48. package/dist/tools/information-schema-query.js +76 -0
  49. package/dist/tools/information-schema-query.js.map +1 -0
  50. package/dist/tools/inspect-table.d.ts +38 -0
  51. package/dist/tools/inspect-table.d.ts.map +1 -0
  52. package/dist/tools/inspect-table.js +351 -0
  53. package/dist/tools/inspect-table.js.map +1 -0
  54. package/dist/tools/list-databases.d.ts +14 -0
  55. package/dist/tools/list-databases.d.ts.map +1 -0
  56. package/dist/tools/list-databases.js +83 -0
  57. package/dist/tools/list-databases.js.map +1 -0
  58. package/dist/tools/list-tables.d.ts +19 -0
  59. package/dist/tools/list-tables.d.ts.map +1 -0
  60. package/dist/tools/list-tables.js +130 -0
  61. package/dist/tools/list-tables.js.map +1 -0
  62. package/dist/utils/errors.d.ts +32 -0
  63. package/dist/utils/errors.d.ts.map +1 -0
  64. package/dist/utils/errors.js +98 -0
  65. package/dist/utils/errors.js.map +1 -0
  66. package/dist/utils/logger.d.ts +28 -0
  67. package/dist/utils/logger.d.ts.map +1 -0
  68. package/dist/utils/logger.js +132 -0
  69. package/dist/utils/logger.js.map +1 -0
  70. package/dist/validators/input-validator.d.ts +76 -0
  71. package/dist/validators/input-validator.d.ts.map +1 -0
  72. package/dist/validators/input-validator.js +295 -0
  73. package/dist/validators/input-validator.js.map +1 -0
  74. package/dist/validators/query-validator.d.ts +19 -0
  75. package/dist/validators/query-validator.d.ts.map +1 -0
  76. package/dist/validators/query-validator.js +229 -0
  77. package/dist/validators/query-validator.js.map +1 -0
  78. package/enhanced_sql_prompt.md +324 -0
  79. package/examples/claude-config.json +23 -0
  80. package/examples/roo-config.json +16 -0
  81. package/package.json +42 -0
  82. package/src/database/connection.ts +165 -0
  83. package/src/database/manager.ts +682 -0
  84. package/src/database/postgres-connection.ts +123 -0
  85. package/src/database/types.ts +93 -0
  86. package/src/index.ts +136 -0
  87. package/src/server.ts +254 -0
  88. package/src/test-defaults.ts +63 -0
  89. package/src/tools/analyze-query.test.ts +100 -0
  90. package/src/tools/analyze-query.ts +112 -0
  91. package/src/tools/execute-query.ts +91 -0
  92. package/src/tools/get-foreign-keys.test.ts +51 -0
  93. package/src/tools/get-foreign-keys.ts +488 -0
  94. package/src/tools/get-indexes.test.ts +51 -0
  95. package/src/tools/get-indexes.ts +570 -0
  96. package/src/tools/information-schema-query.ts +125 -0
  97. package/src/tools/inspect-table.test.ts +59 -0
  98. package/src/tools/inspect-table.ts +440 -0
  99. package/src/tools/list-databases.ts +119 -0
  100. package/src/tools/list-tables.ts +181 -0
  101. package/src/utils/errors.ts +103 -0
  102. package/src/utils/logger.ts +158 -0
  103. package/src/validators/input-validator.ts +318 -0
  104. package/src/validators/query-validator.ts +267 -0
  105. package/tsconfig.json +30 -0
@@ -0,0 +1,27 @@
1
+ import { z } from 'zod';
2
+ import { DatabaseManager } from '../database/manager.js';
3
+ export declare const AnalyzeQueryArgsSchema: z.ZodObject<{
4
+ database: z.ZodString;
5
+ query: z.ZodString;
6
+ }, z.core.$strip>;
7
+ export interface AnalyzeQueryTool {
8
+ name: 'analyze_query';
9
+ description: 'Analyze a SQL query performance and provide recommendations';
10
+ inputSchema: {
11
+ type: 'object';
12
+ properties: {
13
+ database: {
14
+ type: 'string';
15
+ description: 'Name of the database to run the analysis against';
16
+ };
17
+ query: {
18
+ type: 'string';
19
+ description: 'The SQL query to analyze';
20
+ };
21
+ };
22
+ required: ['database', 'query'];
23
+ };
24
+ }
25
+ export declare const analyzeQueryToolDefinition: AnalyzeQueryTool;
26
+ export declare function handleAnalyzeQuery(args: unknown, dbManager: DatabaseManager): Promise<any>;
27
+ //# sourceMappingURL=analyze-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-query.d.ts","sourceRoot":"","sources":["../../src/tools/analyze-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAMzD,eAAO,MAAM,sBAAsB;;;iBAGjC,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;IACtB,WAAW,EAAE,6DAA6D,CAAC;IAC3E,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE;YACV,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ,CAAC;gBACf,WAAW,EAAE,kDAAkD,CAAC;aACjE,CAAC;YACF,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ,CAAC;gBACf,WAAW,EAAE,0BAA0B,CAAC;aACzC,CAAC;SACH,CAAC;QACF,QAAQ,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;CACH;AAED,eAAO,MAAM,0BAA0B,EAAE,gBAiBxC,CAAC;AAEF,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,eAAe,GACzB,OAAO,CAAC,GAAG,CAAC,CA0Dd"}
@@ -0,0 +1,71 @@
1
+ import { z } from 'zod';
2
+ import { InputValidator } from '../validators/input-validator.js';
3
+ import { Logger } from '../utils/logger.js';
4
+ import { ToolError } from '../utils/errors.js';
5
+ // Tool schema
6
+ export const AnalyzeQueryArgsSchema = z.object({
7
+ database: z.string().min(1, 'Database name is required'),
8
+ query: z.string().min(1, 'SQL query is required')
9
+ });
10
+ export const analyzeQueryToolDefinition = {
11
+ name: 'analyze_query',
12
+ description: 'Analyze a SQL query performance and provide recommendations',
13
+ inputSchema: {
14
+ type: 'object',
15
+ properties: {
16
+ database: {
17
+ type: 'string',
18
+ description: 'Name of the database to run the analysis against'
19
+ },
20
+ query: {
21
+ type: 'string',
22
+ description: 'The SQL query to analyze'
23
+ }
24
+ },
25
+ required: ['database', 'query']
26
+ }
27
+ };
28
+ export async function handleAnalyzeQuery(args, dbManager) {
29
+ try {
30
+ Logger.info('Executing analyze_query tool');
31
+ // Validate arguments
32
+ const validationResult = AnalyzeQueryArgsSchema.safeParse(args);
33
+ if (!validationResult.success) {
34
+ Logger.warn('Invalid arguments for analyze_query', validationResult.error);
35
+ throw new ToolError(`Invalid arguments: ${validationResult.error.issues.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`, 'analyze_query');
36
+ }
37
+ const { database, query } = validationResult.data;
38
+ // Sanitize database name
39
+ const sanitizedDatabase = InputValidator.sanitizeString(database);
40
+ Logger.info(`Analyzing query for database: ${sanitizedDatabase}`);
41
+ const analysis = await dbManager.analyzeQuery(sanitizedDatabase, query);
42
+ const response = {
43
+ database: sanitizedDatabase,
44
+ query: analysis.query,
45
+ type: analysis.type,
46
+ summary: {
47
+ cost: analysis.summary.cost,
48
+ operations: analysis.summary.operations,
49
+ potentialIssues: analysis.summary.potentialIssues,
50
+ recommendation: analysis.summary.potentialIssues.length > 0
51
+ ? `Found ${analysis.summary.potentialIssues.length} potential performance issues. Consider adding indexes or refactoring the query.`
52
+ : 'No major performance issues detected in the execution plan.'
53
+ },
54
+ executionPlan: analysis.plan
55
+ };
56
+ return {
57
+ content: [{
58
+ type: 'text',
59
+ text: JSON.stringify(response, null, 2)
60
+ }]
61
+ };
62
+ }
63
+ catch (error) {
64
+ Logger.error('Error in analyze_query tool', error);
65
+ if (error instanceof ToolError) {
66
+ throw error;
67
+ }
68
+ throw new ToolError(`Failed to analyze query: ${error instanceof Error ? error.message : 'Unknown error'}`, 'analyze_query', error instanceof Error ? error : undefined);
69
+ }
70
+ }
71
+ //# sourceMappingURL=analyze-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-query.js","sourceRoot":"","sources":["../../src/tools/analyze-query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { DatabaseManager } from '../database/manager.js';
2
+ export interface ExecuteQueryTool {
3
+ name: 'execute_query';
4
+ description: 'Execute a safe read-only SQL query (SELECT, SHOW, DESCRIBE, EXPLAIN). Automatic row limits are applied for security.';
5
+ inputSchema: {
6
+ type: 'object';
7
+ properties: {
8
+ database: {
9
+ type: 'string';
10
+ description: 'Database name';
11
+ };
12
+ query: {
13
+ type: 'string';
14
+ description: 'SQL query to execute';
15
+ };
16
+ params?: {
17
+ type: 'array';
18
+ items: {
19
+ type: 'any';
20
+ };
21
+ description: 'Optional parameters for the query';
22
+ };
23
+ limit?: {
24
+ type: 'number';
25
+ description: 'Optional limit for number of rows (default 1000, max 1000)';
26
+ };
27
+ };
28
+ required: ['database', 'query'];
29
+ };
30
+ }
31
+ export declare const executeQueryToolDefinition: ExecuteQueryTool;
32
+ export declare function handleExecuteQuery(args: unknown, dbManager: DatabaseManager): Promise<any>;
33
+ //# sourceMappingURL=execute-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute-query.d.ts","sourceRoot":"","sources":["../../src/tools/execute-query.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAazD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;IACtB,WAAW,EAAE,sHAAsH,CAAC;IACpI,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE;YACV,QAAQ,EAAE;gBAAE,IAAI,EAAE,QAAQ,CAAC;gBAAC,WAAW,EAAE,eAAe,CAAA;aAAE,CAAC;YAC3D,KAAK,EAAE;gBAAE,IAAI,EAAE,QAAQ,CAAC;gBAAC,WAAW,EAAE,sBAAsB,CAAA;aAAE,CAAC;YAC/D,MAAM,CAAC,EAAE;gBAAE,IAAI,EAAE,OAAO,CAAC;gBAAC,KAAK,EAAE;oBAAE,IAAI,EAAE,KAAK,CAAA;iBAAE,CAAC;gBAAC,WAAW,EAAE,mCAAmC,CAAA;aAAE,CAAC;YACrG,KAAK,CAAC,EAAE;gBAAE,IAAI,EAAE,QAAQ,CAAC;gBAAC,WAAW,EAAE,4DAA4D,CAAA;aAAE,CAAC;SACvG,CAAC;QACF,QAAQ,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;CACH;AAED,eAAO,MAAM,0BAA0B,EAAE,gBAaxC,CAAC;AAEF,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,eAAe,GACzB,OAAO,CAAC,GAAG,CAAC,CA2Cd"}
@@ -0,0 +1,57 @@
1
+ import { z } from 'zod';
2
+ import { InputValidator } from '../validators/input-validator.js';
3
+ import { Logger } from '../utils/logger.js';
4
+ import { ToolError } from '../utils/errors.js';
5
+ // Zod schema for arguments
6
+ const ExecuteQueryArgsSchema = z.object({
7
+ database: z.string().min(1, 'Database name is required'),
8
+ query: z.string().min(1, 'SQL query is required'),
9
+ params: z.array(z.any()).optional(),
10
+ limit: z.number().int().min(1).max(1000).optional()
11
+ });
12
+ export const executeQueryToolDefinition = {
13
+ name: 'execute_query',
14
+ description: 'Execute a safe read-only SQL query (SELECT, SHOW, DESCRIBE, EXPLAIN). Automatic row limits are applied for security.',
15
+ inputSchema: {
16
+ type: 'object',
17
+ properties: {
18
+ database: { type: 'string', description: 'Database name' },
19
+ query: { type: 'string', description: 'SQL query to execute' },
20
+ params: { type: 'array', items: { type: 'any' }, description: 'Optional parameters for the query' },
21
+ limit: { type: 'number', description: 'Optional limit for number of rows (default 1000, max 1000)' }
22
+ },
23
+ required: ['database', 'query']
24
+ }
25
+ };
26
+ export async function handleExecuteQuery(args, dbManager) {
27
+ try {
28
+ Logger.info('Executing execute_query tool');
29
+ const validationResult = ExecuteQueryArgsSchema.safeParse(args);
30
+ if (!validationResult.success) {
31
+ Logger.warn('Invalid arguments for execute_query', validationResult.error);
32
+ throw new ToolError(`Invalid arguments: ${validationResult.error.issues.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`, 'execute_query');
33
+ }
34
+ const { database, query, params, limit } = validationResult.data;
35
+ // Sanitize and validate database name
36
+ const sanitizedDatabase = InputValidator.sanitizeString(database);
37
+ const dbNameValidation = InputValidator.validateDatabaseName(sanitizedDatabase);
38
+ if (!dbNameValidation.isValid) {
39
+ throw new ToolError(`Invalid database name: ${dbNameValidation.error}`, 'execute_query');
40
+ }
41
+ Logger.info(`Executing query on database ${sanitizedDatabase}`);
42
+ const result = await dbManager.executeQuery(sanitizedDatabase, query, params);
43
+ return {
44
+ content: [{
45
+ type: 'text',
46
+ text: JSON.stringify(result.rows, null, 2)
47
+ }]
48
+ };
49
+ }
50
+ catch (error) {
51
+ Logger.error('Error in execute_query tool', error);
52
+ if (error instanceof ToolError)
53
+ throw error;
54
+ throw new ToolError(`Failed to execute query: ${error instanceof Error ? error.message : 'Unknown error'}`, 'execute_query', error instanceof Error ? error : undefined);
55
+ }
56
+ }
57
+ //# sourceMappingURL=execute-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute-query.js","sourceRoot":"","sources":["../../src/tools/execute-query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { DatabaseManager } from '../database/manager.js';
2
+ /**
3
+ * Tool: get_foreign_keys
4
+ * Get foreign key relationships for one or more tables, or the entire database.
5
+ *
6
+ * Supports both single-table and multi-table inspection:
7
+ * - Provide either `table` (string) for a single table, or `tables` (string[]) for multiple tables.
8
+ * - If `tables` is provided, returns a mapping of table names to their foreign key analysis.
9
+ * - Do not provide both `table` and `tables` at the same time.
10
+ */
11
+ export interface GetForeignKeysTool {
12
+ name: 'get_foreign_keys';
13
+ description: 'Get foreign key relationships for one or more tables, or the entire database. Supports multi-table inspection via the tables: string[] parameter.';
14
+ inputSchema: {
15
+ type: 'object';
16
+ properties: {
17
+ database: {
18
+ type: 'string';
19
+ description: 'Name of the database to analyze';
20
+ };
21
+ table: {
22
+ type: 'string';
23
+ description: 'Specific table name to get foreign keys for (single-table mode)';
24
+ };
25
+ tables: {
26
+ type: 'array';
27
+ items: {
28
+ type: 'string';
29
+ };
30
+ description: 'Array of table names to get foreign keys for (multi-table mode)';
31
+ };
32
+ };
33
+ required: ['database'];
34
+ };
35
+ }
36
+ export declare const getForeignKeysToolDefinition: GetForeignKeysTool;
37
+ export declare function handleGetForeignKeys(args: unknown, dbManager: DatabaseManager): Promise<any>;
38
+ //# sourceMappingURL=get-foreign-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-foreign-keys.d.ts","sourceRoot":"","sources":["../../src/tools/get-foreign-keys.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AA0BzD;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,kBAAkB,CAAC;IACzB,WAAW,EAAE,mJAAmJ,CAAC;IACjK,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE;YACV,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ,CAAC;gBACf,WAAW,EAAE,iCAAiC,CAAC;aAChD,CAAC;YACF,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ,CAAC;gBACf,WAAW,EAAE,iEAAiE,CAAC;aAChF,CAAC;YACF,MAAM,EAAE;gBACN,IAAI,EAAE,OAAO,CAAC;gBACd,KAAK,EAAE;oBAAE,IAAI,EAAE,QAAQ,CAAA;iBAAE,CAAC;gBAC1B,WAAW,EAAE,iEAAiE,CAAC;aAChF,CAAC;SACH,CAAC;QACF,QAAQ,EAAE,CAAC,UAAU,CAAC,CAAC;KACxB,CAAC;CACH;AAED,eAAO,MAAM,4BAA4B,EAAE,kBAsB1C,CAAC;AAEF,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,eAAe,GACzB,OAAO,CAAC,GAAG,CAAC,CAqOd"}
@@ -0,0 +1,391 @@
1
+ import { z } from 'zod';
2
+ import { InputValidator } from '../validators/input-validator.js';
3
+ import { Logger } from '../utils/logger.js';
4
+ import { ToolError } from '../utils/errors.js';
5
+ // Tool schema
6
+ const GetForeignKeysArgsSchema = z.object({
7
+ database: z.string().min(1, 'Database name is required'),
8
+ table: z.string().optional(),
9
+ tables: z.array(z.string().min(1)).optional()
10
+ }).superRefine((data, ctx) => {
11
+ const hasTable = typeof data.table === 'string' && data.table.length > 0;
12
+ const hasTables = Array.isArray(data.tables) && data.tables.length > 0;
13
+ if (hasTable && hasTables) {
14
+ ctx.addIssue({
15
+ code: z.ZodIssueCode.custom,
16
+ message: "Provide either 'table' or 'tables', not both"
17
+ });
18
+ }
19
+ else if (!hasTable && !hasTables) {
20
+ ctx.addIssue({
21
+ code: z.ZodIssueCode.custom,
22
+ message: "Either 'table' or non-empty 'tables' must be provided"
23
+ });
24
+ }
25
+ });
26
+ export const getForeignKeysToolDefinition = {
27
+ name: 'get_foreign_keys',
28
+ description: 'Get foreign key relationships for one or more tables, or the entire database. Supports multi-table inspection via the tables: string[] parameter.',
29
+ inputSchema: {
30
+ type: 'object',
31
+ properties: {
32
+ database: {
33
+ type: 'string',
34
+ description: 'Name of the database to analyze'
35
+ },
36
+ table: {
37
+ type: 'string',
38
+ description: 'Specific table name to get foreign keys for (single-table mode)'
39
+ },
40
+ tables: {
41
+ type: 'array',
42
+ items: { type: 'string' },
43
+ description: 'Array of table names to get foreign keys for (multi-table mode)'
44
+ }
45
+ },
46
+ required: ['database']
47
+ }
48
+ };
49
+ export async function handleGetForeignKeys(args, dbManager) {
50
+ try {
51
+ Logger.info('Executing get_foreign_keys tool');
52
+ // Validate arguments
53
+ const validationResult = GetForeignKeysArgsSchema.safeParse(args);
54
+ if (!validationResult.success) {
55
+ Logger.warn('Invalid arguments for get_foreign_keys', validationResult.error);
56
+ throw new ToolError(`Invalid arguments: ${validationResult.error.issues.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`, 'get_foreign_keys');
57
+ }
58
+ const { database, table, tables } = validationResult.data;
59
+ // Sanitize inputs
60
+ const sanitizedDatabase = InputValidator.sanitizeString(database);
61
+ const sanitizedTable = table ? InputValidator.sanitizeString(table) : undefined;
62
+ const sanitizedTables = tables ? tables.map(InputValidator.sanitizeString) : undefined;
63
+ // Validate database name format
64
+ const dbNameValidation = InputValidator.validateDatabaseName(sanitizedDatabase);
65
+ if (!dbNameValidation.isValid) {
66
+ throw new ToolError(`Invalid database name: ${dbNameValidation.error}`, 'get_foreign_keys');
67
+ }
68
+ // Multi-table mode
69
+ if (sanitizedTables && sanitizedTables.length > 0) {
70
+ Logger.info(`Multi-table mode: Getting foreign keys for tables: ${sanitizedTables.join(', ')} in database: ${sanitizedDatabase}`);
71
+ const results = {};
72
+ const errors = {};
73
+ for (const tbl of sanitizedTables) {
74
+ try {
75
+ const tableNameValidation = InputValidator.validateTableName(tbl);
76
+ if (!tableNameValidation.isValid) {
77
+ throw new ToolError(`Invalid table name: ${tableNameValidation.error}`, 'get_foreign_keys');
78
+ }
79
+ Logger.info(`Getting foreign keys for table: ${tbl} in database: ${sanitizedDatabase}`);
80
+ Logger.time(`get_foreign_keys_execution_${tbl}`);
81
+ const foreignKeys = await dbManager.getForeignKeys(sanitizedDatabase, tbl);
82
+ Logger.timeEnd(`get_foreign_keys_execution_${tbl}`);
83
+ Logger.info(`Found ${foreignKeys.length} foreign key(s) in table: ${tbl}`);
84
+ const analysis = analyzeForeignKeyRelationships(foreignKeys);
85
+ const foreignKeysByTable = groupForeignKeysByTable(foreignKeys);
86
+ results[tbl] = {
87
+ database: sanitizedDatabase,
88
+ table: tbl,
89
+ scope: 'table',
90
+ foreignKeys: foreignKeys.map(fk => ({
91
+ constraintName: fk.constraintName,
92
+ sourceTable: fk.tableName,
93
+ sourceColumn: fk.columnName,
94
+ targetTable: fk.referencedTableName,
95
+ targetColumn: fk.referencedColumnName,
96
+ updateRule: fk.updateRule,
97
+ deleteRule: fk.deleteRule,
98
+ relationshipType: determineRelationshipType(fk.updateRule || 'NO ACTION', fk.deleteRule || 'NO ACTION')
99
+ })),
100
+ relationships: analysis.relationships,
101
+ statistics: {
102
+ totalForeignKeys: foreignKeys.length,
103
+ uniqueConstraints: [...new Set(foreignKeys.map(fk => fk.constraintName))].length,
104
+ affectedTables: [...new Set(foreignKeys.map(fk => fk.tableName))].length,
105
+ referencedTables: [...new Set(foreignKeys.map(fk => fk.referencedTableName))].length,
106
+ cascadeDeleteCount: foreignKeys.filter(fk => fk.deleteRule === 'CASCADE').length,
107
+ cascadeUpdateCount: foreignKeys.filter(fk => fk.updateRule === 'CASCADE').length,
108
+ restrictDeleteCount: foreignKeys.filter(fk => fk.deleteRule === 'RESTRICT').length,
109
+ restrictUpdateCount: foreignKeys.filter(fk => fk.updateRule === 'RESTRICT').length
110
+ },
111
+ foreignKeysByTable,
112
+ analysis: {
113
+ ...analysis,
114
+ integrityRules: analyzeIntegrityRules(foreignKeys),
115
+ potentialIssues: identifyPotentialIssues(foreignKeys)
116
+ },
117
+ summary: {
118
+ hasRelationships: foreignKeys.length > 0,
119
+ message: foreignKeys.length === 0
120
+ ? `No foreign key relationships found in table '${tbl}' of database '${sanitizedDatabase}'`
121
+ : `Found ${foreignKeys.length} foreign key relationship(s) in table '${tbl}' involving ${analysis.relationships.length} table connection(s)`
122
+ }
123
+ };
124
+ Logger.debug('get_foreign_keys completed for table', {
125
+ database: sanitizedDatabase,
126
+ table: tbl,
127
+ foreignKeyCount: foreignKeys.length,
128
+ relationshipCount: analysis.relationships.length
129
+ });
130
+ }
131
+ catch (err) {
132
+ Logger.error(`Error processing table '${tbl}' in get_foreign_keys`, err);
133
+ errors[tbl] = err instanceof Error ? err.message : String(err);
134
+ }
135
+ }
136
+ return {
137
+ content: [{
138
+ type: 'text',
139
+ text: JSON.stringify({ database: sanitizedDatabase, results, errors }, null, 2)
140
+ }]
141
+ };
142
+ }
143
+ // Single-table or all-tables mode (original behavior)
144
+ // Validate table name format if provided
145
+ if (sanitizedTable) {
146
+ const tableNameValidation = InputValidator.validateTableName(sanitizedTable);
147
+ if (!tableNameValidation.isValid) {
148
+ throw new ToolError(`Invalid table name: ${tableNameValidation.error}`, 'get_foreign_keys');
149
+ }
150
+ }
151
+ const scope = sanitizedTable ? `table: ${sanitizedTable}` : 'entire database';
152
+ Logger.info(`Getting foreign keys for ${scope} in database: ${sanitizedDatabase}`);
153
+ Logger.time('get_foreign_keys_execution');
154
+ // Get foreign keys
155
+ const foreignKeys = await dbManager.getForeignKeys(sanitizedDatabase, sanitizedTable);
156
+ Logger.timeEnd('get_foreign_keys_execution');
157
+ Logger.info(`Found ${foreignKeys.length} foreign key(s) in ${scope}`);
158
+ // Analyze relationships
159
+ const analysis = analyzeForeignKeyRelationships(foreignKeys);
160
+ // Group foreign keys by table
161
+ const foreignKeysByTable = groupForeignKeysByTable(foreignKeys);
162
+ // Create response
163
+ const response = {
164
+ database: sanitizedDatabase,
165
+ table: sanitizedTable || null,
166
+ scope: sanitizedTable ? 'table' : 'database',
167
+ foreignKeys: foreignKeys.map(fk => ({
168
+ constraintName: fk.constraintName,
169
+ sourceTable: fk.tableName,
170
+ sourceColumn: fk.columnName,
171
+ targetTable: fk.referencedTableName,
172
+ targetColumn: fk.referencedColumnName,
173
+ updateRule: fk.updateRule,
174
+ deleteRule: fk.deleteRule,
175
+ relationshipType: determineRelationshipType(fk.updateRule || 'NO ACTION', fk.deleteRule || 'NO ACTION')
176
+ })),
177
+ relationships: analysis.relationships,
178
+ statistics: {
179
+ totalForeignKeys: foreignKeys.length,
180
+ uniqueConstraints: [...new Set(foreignKeys.map(fk => fk.constraintName))].length,
181
+ affectedTables: [...new Set(foreignKeys.map(fk => fk.tableName))].length,
182
+ referencedTables: [...new Set(foreignKeys.map(fk => fk.referencedTableName))].length,
183
+ cascadeDeleteCount: foreignKeys.filter(fk => fk.deleteRule === 'CASCADE').length,
184
+ cascadeUpdateCount: foreignKeys.filter(fk => fk.updateRule === 'CASCADE').length,
185
+ restrictDeleteCount: foreignKeys.filter(fk => fk.deleteRule === 'RESTRICT').length,
186
+ restrictUpdateCount: foreignKeys.filter(fk => fk.updateRule === 'RESTRICT').length
187
+ },
188
+ foreignKeysByTable,
189
+ analysis: {
190
+ ...analysis,
191
+ integrityRules: analyzeIntegrityRules(foreignKeys),
192
+ potentialIssues: identifyPotentialIssues(foreignKeys)
193
+ },
194
+ summary: {
195
+ hasRelationships: foreignKeys.length > 0,
196
+ message: foreignKeys.length === 0
197
+ ? `No foreign key relationships found in ${scope} of database '${sanitizedDatabase}'`
198
+ : `Found ${foreignKeys.length} foreign key relationship(s) in ${scope} involving ${analysis.relationships.length} table connection(s)`
199
+ }
200
+ };
201
+ Logger.debug('get_foreign_keys completed successfully', {
202
+ database: sanitizedDatabase,
203
+ table: sanitizedTable,
204
+ foreignKeyCount: foreignKeys.length,
205
+ relationshipCount: analysis.relationships.length
206
+ });
207
+ return {
208
+ content: [{
209
+ type: 'text',
210
+ text: JSON.stringify(response, null, 2)
211
+ }]
212
+ };
213
+ }
214
+ catch (error) {
215
+ // Add table context to error logs
216
+ let tableContext;
217
+ let argTables;
218
+ let argTable;
219
+ if (args && typeof args === 'object') {
220
+ // @ts-ignore
221
+ argTables = Array.isArray(args.tables) ? args.tables : undefined;
222
+ // @ts-ignore
223
+ argTable = typeof args.table === 'string' ? args.table : undefined;
224
+ }
225
+ if (Array.isArray(argTables) && argTables.length > 0) {
226
+ tableContext = `tables: [${argTables.join(', ')}]`;
227
+ }
228
+ else if (typeof argTable === 'string' && argTable.length > 0) {
229
+ tableContext = `table: ${argTable}`;
230
+ }
231
+ else {
232
+ tableContext = 'no table(s) specified';
233
+ }
234
+ Logger.error(`Error in get_foreign_keys tool (${tableContext})`, error);
235
+ if (error instanceof ToolError) {
236
+ throw error;
237
+ }
238
+ throw new ToolError(`Failed to get foreign keys (${tableContext}): ${error instanceof Error ? error.message : 'Unknown error'}`, 'get_foreign_keys', error instanceof Error ? error : undefined);
239
+ }
240
+ }
241
+ // Helper functions
242
+ function groupForeignKeysByTable(foreignKeys) {
243
+ const groups = {};
244
+ foreignKeys.forEach(fk => {
245
+ if (!groups[fk.tableName]) {
246
+ groups[fk.tableName] = [];
247
+ }
248
+ groups[fk.tableName].push({
249
+ constraintName: fk.constraintName,
250
+ column: fk.columnName,
251
+ referencedTable: fk.referencedTableName,
252
+ referencedColumn: fk.referencedColumnName,
253
+ updateRule: fk.updateRule,
254
+ deleteRule: fk.deleteRule
255
+ });
256
+ });
257
+ return groups;
258
+ }
259
+ function analyzeForeignKeyRelationships(foreignKeys) {
260
+ const relationships = [];
261
+ const tableConnections = {};
262
+ // Group by constraint to handle composite foreign keys
263
+ const constraintGroups = {};
264
+ foreignKeys.forEach(fk => {
265
+ if (!constraintGroups[fk.constraintName]) {
266
+ constraintGroups[fk.constraintName] = [];
267
+ }
268
+ constraintGroups[fk.constraintName].push(fk);
269
+ });
270
+ // Analyze each constraint
271
+ Object.keys(constraintGroups).forEach(constraintName => {
272
+ const fks = constraintGroups[constraintName];
273
+ const firstFK = fks[0];
274
+ relationships.push({
275
+ constraintName,
276
+ fromTable: firstFK.tableName,
277
+ toTable: firstFK.referencedTableName,
278
+ columns: fks.map(fk => ({
279
+ from: fk.columnName,
280
+ to: fk.referencedColumnName
281
+ })),
282
+ isComposite: fks.length > 1,
283
+ updateRule: firstFK.updateRule,
284
+ deleteRule: firstFK.deleteRule,
285
+ relationshipStrength: determineRelationshipStrength(firstFK.updateRule, firstFK.deleteRule)
286
+ });
287
+ // Track table connections
288
+ if (!tableConnections[firstFK.tableName]) {
289
+ tableConnections[firstFK.tableName] = new Set();
290
+ }
291
+ tableConnections[firstFK.tableName].add(firstFK.referencedTableName);
292
+ });
293
+ return {
294
+ relationships,
295
+ tableConnections: Object.fromEntries(Object.entries(tableConnections).map(([table, connections]) => [
296
+ table,
297
+ Array.from(connections)
298
+ ])),
299
+ totalRelationships: relationships.length,
300
+ compositeRelationships: relationships.filter(r => r.isComposite).length
301
+ };
302
+ }
303
+ function determineRelationshipType(updateRule, deleteRule) {
304
+ const ur = updateRule || 'NO ACTION';
305
+ const dr = deleteRule || 'NO ACTION';
306
+ if (ur === 'CASCADE' || dr === 'CASCADE') {
307
+ return 'strong_dependency'; // Child cannot exist without parent
308
+ }
309
+ else if (dr === 'RESTRICT' || dr === 'NO ACTION') {
310
+ return 'protective'; // Prevents accidental deletion
311
+ }
312
+ else if (dr === 'SET NULL') {
313
+ return 'optional_reference'; // Relationship can be broken
314
+ }
315
+ else {
316
+ return 'unknown';
317
+ }
318
+ }
319
+ function determineRelationshipStrength(updateRule, deleteRule) {
320
+ if (deleteRule === 'CASCADE' && updateRule === 'CASCADE') {
321
+ return 'strong';
322
+ }
323
+ else if (deleteRule === 'RESTRICT' || updateRule === 'RESTRICT') {
324
+ return 'medium';
325
+ }
326
+ else {
327
+ return 'weak';
328
+ }
329
+ }
330
+ function analyzeIntegrityRules(foreignKeys) {
331
+ const rules = {
332
+ cascade: {
333
+ delete: foreignKeys.filter(fk => fk.deleteRule === 'CASCADE'),
334
+ update: foreignKeys.filter(fk => fk.updateRule === 'CASCADE')
335
+ },
336
+ restrict: {
337
+ delete: foreignKeys.filter(fk => fk.deleteRule === 'RESTRICT' || fk.deleteRule === 'NO ACTION'),
338
+ update: foreignKeys.filter(fk => fk.updateRule === 'RESTRICT' || fk.updateRule === 'NO ACTION')
339
+ },
340
+ setNull: {
341
+ delete: foreignKeys.filter(fk => fk.deleteRule === 'SET NULL'),
342
+ update: foreignKeys.filter(fk => fk.updateRule === 'SET NULL')
343
+ }
344
+ };
345
+ return {
346
+ rules,
347
+ summary: {
348
+ cascadeDeleteTables: [...new Set(rules.cascade.delete.map(fk => fk.tableName))],
349
+ protectedTables: [...new Set(rules.restrict.delete.map(fk => fk.referencedTableName))],
350
+ weaklyReferencedTables: [...new Set(rules.setNull.delete.map(fk => fk.referencedTableName))]
351
+ }
352
+ };
353
+ }
354
+ function identifyPotentialIssues(foreignKeys) {
355
+ const issues = [];
356
+ // Check for circular references
357
+ const tableGraph = {};
358
+ foreignKeys.forEach(fk => {
359
+ if (!tableGraph[fk.tableName]) {
360
+ tableGraph[fk.tableName] = [];
361
+ }
362
+ tableGraph[fk.tableName].push(fk.referencedTableName);
363
+ });
364
+ // Simple cycle detection (this is a basic check, not comprehensive)
365
+ Object.keys(tableGraph).forEach(table => {
366
+ tableGraph[table].forEach(referenced => {
367
+ if (tableGraph[referenced] && tableGraph[referenced].includes(table)) {
368
+ issues.push(`Potential circular reference detected between tables '${table}' and '${referenced}'`);
369
+ }
370
+ });
371
+ });
372
+ // Check for many cascade delete relationships from same parent
373
+ const cascadeParents = {};
374
+ foreignKeys.filter(fk => fk.deleteRule === 'CASCADE').forEach(fk => {
375
+ cascadeParents[fk.referencedTableName] = (cascadeParents[fk.referencedTableName] || 0) + 1;
376
+ });
377
+ Object.entries(cascadeParents).forEach(([table, count]) => {
378
+ if (count > 5) {
379
+ issues.push(`Table '${table}' has ${count} cascade delete relationships - consider the impact of deletions`);
380
+ }
381
+ });
382
+ // Check for inconsistent naming patterns
383
+ const constraintNames = foreignKeys.map(fk => fk.constraintName);
384
+ const hasConsistentNaming = constraintNames.every(name => name.startsWith('fk_') || name.startsWith('FK_') ||
385
+ name.includes('_fk') || name.includes('_FK'));
386
+ if (!hasConsistentNaming && constraintNames.length > 1) {
387
+ issues.push('Foreign key constraint names follow inconsistent naming patterns');
388
+ }
389
+ return issues;
390
+ }
391
+ //# sourceMappingURL=get-foreign-keys.js.map