code-auditor-mcp 1.20.0 → 2.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.
Files changed (180) hide show
  1. package/dist/adapters/GoAdapter.d.ts +53 -0
  2. package/dist/adapters/GoAdapter.d.ts.map +1 -0
  3. package/dist/adapters/GoAdapter.js +602 -0
  4. package/dist/adapters/GoAdapter.js.map +1 -0
  5. package/dist/adapters/GoConfig.d.ts +35 -0
  6. package/dist/adapters/GoConfig.d.ts.map +1 -0
  7. package/dist/adapters/GoConfig.js +155 -0
  8. package/dist/adapters/GoConfig.js.map +1 -0
  9. package/dist/adapters/LanguageAdapter.d.ts +135 -0
  10. package/dist/adapters/LanguageAdapter.d.ts.map +1 -0
  11. package/dist/adapters/LanguageAdapter.js +47 -0
  12. package/dist/adapters/LanguageAdapter.js.map +1 -0
  13. package/dist/adapters/TypeScriptAdapter.d.ts +43 -0
  14. package/dist/adapters/TypeScriptAdapter.d.ts.map +1 -0
  15. package/dist/adapters/TypeScriptAdapter.js +386 -0
  16. package/dist/adapters/TypeScriptAdapter.js.map +1 -0
  17. package/dist/adapters/UniversalAnalyzer.d.ts +42 -0
  18. package/dist/adapters/UniversalAnalyzer.d.ts.map +1 -0
  19. package/dist/adapters/UniversalAnalyzer.js +103 -0
  20. package/dist/adapters/UniversalAnalyzer.js.map +1 -0
  21. package/dist/adapters/UniversalSOLIDAnalyzer.d.ts +48 -0
  22. package/dist/adapters/UniversalSOLIDAnalyzer.d.ts.map +1 -0
  23. package/dist/adapters/UniversalSOLIDAnalyzer.js +178 -0
  24. package/dist/adapters/UniversalSOLIDAnalyzer.js.map +1 -0
  25. package/dist/adapters/solidAnalyzerCompat.d.ts +21 -0
  26. package/dist/adapters/solidAnalyzerCompat.d.ts.map +1 -0
  27. package/dist/adapters/solidAnalyzerCompat.js +78 -0
  28. package/dist/adapters/solidAnalyzerCompat.js.map +1 -0
  29. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +103 -0
  30. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -0
  31. package/dist/analyzers/cross-language/APIContractAnalyzer.js +425 -0
  32. package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -0
  33. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts +117 -0
  34. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts.map +1 -0
  35. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js +618 -0
  36. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js.map +1 -0
  37. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +129 -0
  38. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -0
  39. package/dist/analyzers/cross-language/DependencyGraphBuilder.js +473 -0
  40. package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -0
  41. package/dist/analyzers/cross-language/SchemaValidator.d.ts +126 -0
  42. package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -0
  43. package/dist/analyzers/cross-language/SchemaValidator.js +522 -0
  44. package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -0
  45. package/dist/analyzers/dataAccessAnalyzerCompat.d.ts +20 -0
  46. package/dist/analyzers/dataAccessAnalyzerCompat.d.ts.map +1 -0
  47. package/dist/analyzers/dataAccessAnalyzerCompat.js +59 -0
  48. package/dist/analyzers/dataAccessAnalyzerCompat.js.map +1 -0
  49. package/dist/analyzers/documentationAnalyzerCompat.d.ts +20 -0
  50. package/dist/analyzers/documentationAnalyzerCompat.d.ts.map +1 -0
  51. package/dist/analyzers/documentationAnalyzerCompat.js +52 -0
  52. package/dist/analyzers/documentationAnalyzerCompat.js.map +1 -0
  53. package/dist/analyzers/dryAnalyzerCompat.d.ts +20 -0
  54. package/dist/analyzers/dryAnalyzerCompat.d.ts.map +1 -0
  55. package/dist/analyzers/dryAnalyzerCompat.js +44 -0
  56. package/dist/analyzers/dryAnalyzerCompat.js.map +1 -0
  57. package/dist/analyzers/schemaAnalyzer.d.ts +21 -0
  58. package/dist/analyzers/schemaAnalyzer.d.ts.map +1 -0
  59. package/dist/analyzers/schemaAnalyzer.js +405 -0
  60. package/dist/analyzers/schemaAnalyzer.js.map +1 -0
  61. package/dist/analyzers/schemaAnalyzerCompat.d.ts +20 -0
  62. package/dist/analyzers/schemaAnalyzerCompat.d.ts.map +1 -0
  63. package/dist/analyzers/schemaAnalyzerCompat.js +69 -0
  64. package/dist/analyzers/schemaAnalyzerCompat.js.map +1 -0
  65. package/dist/analyzers/solidAnalyzerCompat.d.ts +20 -0
  66. package/dist/analyzers/solidAnalyzerCompat.d.ts.map +1 -0
  67. package/dist/analyzers/solidAnalyzerCompat.js +78 -0
  68. package/dist/analyzers/solidAnalyzerCompat.js.map +1 -0
  69. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +71 -0
  70. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -0
  71. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +276 -0
  72. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -0
  73. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +82 -0
  74. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -0
  75. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +489 -0
  76. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -0
  77. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts +48 -0
  78. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -0
  79. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +175 -0
  80. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -0
  81. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +64 -0
  82. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -0
  83. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +286 -0
  84. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -0
  85. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +119 -0
  86. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -0
  87. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +812 -0
  88. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -0
  89. package/dist/auditRunner.d.ts.map +1 -1
  90. package/dist/auditRunner.js +23 -7
  91. package/dist/auditRunner.js.map +1 -1
  92. package/dist/codeIndexDB-enhanced.d.ts +80 -0
  93. package/dist/codeIndexDB-enhanced.d.ts.map +1 -0
  94. package/dist/codeIndexDB-enhanced.js +488 -0
  95. package/dist/codeIndexDB-enhanced.js.map +1 -0
  96. package/dist/codeIndexDb.d.ts +70 -1
  97. package/dist/codeIndexDb.d.ts.map +1 -1
  98. package/dist/codeIndexDb.js +199 -2
  99. package/dist/codeIndexDb.js.map +1 -1
  100. package/dist/config/defaults.js +1 -1
  101. package/dist/config/defaults.js.map +1 -1
  102. package/dist/languages/LanguageOrchestrator.d.ts +181 -0
  103. package/dist/languages/LanguageOrchestrator.d.ts.map +1 -0
  104. package/dist/languages/LanguageOrchestrator.js +365 -0
  105. package/dist/languages/LanguageOrchestrator.js.map +1 -0
  106. package/dist/languages/LanguageRegistry.d.ts +43 -0
  107. package/dist/languages/LanguageRegistry.d.ts.map +1 -0
  108. package/dist/languages/LanguageRegistry.js +86 -0
  109. package/dist/languages/LanguageRegistry.js.map +1 -0
  110. package/dist/languages/RuntimeManager.d.ts +181 -0
  111. package/dist/languages/RuntimeManager.d.ts.map +1 -0
  112. package/dist/languages/RuntimeManager.js +841 -0
  113. package/dist/languages/RuntimeManager.js.map +1 -0
  114. package/dist/languages/UniversalAnalyzer.d.ts +37 -0
  115. package/dist/languages/UniversalAnalyzer.d.ts.map +1 -0
  116. package/dist/languages/UniversalAnalyzer.js +101 -0
  117. package/dist/languages/UniversalAnalyzer.js.map +1 -0
  118. package/dist/languages/go/GoAdapter.d.ts +57 -0
  119. package/dist/languages/go/GoAdapter.d.ts.map +1 -0
  120. package/dist/languages/go/GoAdapter.js +360 -0
  121. package/dist/languages/go/GoAdapter.js.map +1 -0
  122. package/dist/languages/index.d.ts +13 -0
  123. package/dist/languages/index.d.ts.map +1 -0
  124. package/dist/languages/index.js +23 -0
  125. package/dist/languages/index.js.map +1 -0
  126. package/dist/languages/types.d.ts +182 -0
  127. package/dist/languages/types.d.ts.map +1 -0
  128. package/dist/languages/types.js +5 -0
  129. package/dist/languages/types.js.map +1 -0
  130. package/dist/languages/typescript/TypeScriptAdapter.d.ts +108 -0
  131. package/dist/languages/typescript/TypeScriptAdapter.d.ts.map +1 -0
  132. package/dist/languages/typescript/TypeScriptAdapter.js +567 -0
  133. package/dist/languages/typescript/TypeScriptAdapter.js.map +1 -0
  134. package/dist/languages/typescript/test-extraction.d.ts +2 -0
  135. package/dist/languages/typescript/test-extraction.d.ts.map +1 -0
  136. package/dist/languages/typescript/test-extraction.js +36 -0
  137. package/dist/languages/typescript/test-extraction.js.map +1 -0
  138. package/dist/mcp-hybrid.d.ts +10 -0
  139. package/dist/mcp-hybrid.d.ts.map +1 -0
  140. package/dist/mcp-hybrid.js +41 -0
  141. package/dist/mcp-hybrid.js.map +1 -0
  142. package/dist/mcp-index.d.ts +11 -0
  143. package/dist/mcp-index.d.ts.map +1 -0
  144. package/dist/mcp-index.js +101 -0
  145. package/dist/mcp-index.js.map +1 -0
  146. package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
  147. package/dist/mcp-tools/workflowGuide.js +67 -10
  148. package/dist/mcp-tools/workflowGuide.js.map +1 -1
  149. package/dist/mcp-tools-shared.d.ts +49 -0
  150. package/dist/mcp-tools-shared.d.ts.map +1 -0
  151. package/dist/mcp-tools-shared.js +1282 -0
  152. package/dist/mcp-tools-shared.js.map +1 -0
  153. package/dist/mcp-ui-server.d.ts +27 -0
  154. package/dist/mcp-ui-server.d.ts.map +1 -0
  155. package/dist/mcp-ui-server.js +484 -0
  156. package/dist/mcp-ui-server.js.map +1 -0
  157. package/dist/mcp-ui-simple.d.ts +27 -0
  158. package/dist/mcp-ui-simple.d.ts.map +1 -0
  159. package/dist/mcp-ui-simple.js +425 -0
  160. package/dist/mcp-ui-simple.js.map +1 -0
  161. package/dist/mcp.js +7 -4
  162. package/dist/mcp.js.map +1 -1
  163. package/dist/services/SchemaParser.d.ts +89 -0
  164. package/dist/services/SchemaParser.d.ts.map +1 -0
  165. package/dist/services/SchemaParser.js +434 -0
  166. package/dist/services/SchemaParser.js.map +1 -0
  167. package/dist/types/crossLanguage.d.ts +267 -0
  168. package/dist/types/crossLanguage.d.ts.map +1 -0
  169. package/dist/types/crossLanguage.js +6 -0
  170. package/dist/types/crossLanguage.js.map +1 -0
  171. package/dist/types.d.ts +128 -0
  172. package/dist/types.d.ts.map +1 -1
  173. package/dist/types.js.map +1 -1
  174. package/dist/utils/fileDiscovery.d.ts +6 -0
  175. package/dist/utils/fileDiscovery.d.ts.map +1 -1
  176. package/dist/utils/fileDiscovery.js +12 -1
  177. package/dist/utils/fileDiscovery.js.map +1 -1
  178. package/examples/schema-example.json +364 -0
  179. package/examples/schema-example.yaml +137 -0
  180. package/package.json +10 -3
@@ -0,0 +1,1282 @@
1
+ /**
2
+ * Shared MCP Tools Logic
3
+ *
4
+ * This contains all the tool definitions and handlers that are shared
5
+ * between stdio and HTTP/UI MCP server implementations.
6
+ */
7
+ import { createAuditRunner } from './auditRunner.js';
8
+ import { LanguageOrchestrator } from './languages/LanguageOrchestrator.js';
9
+ import { RuntimeManager } from './languages/RuntimeManager.js';
10
+ import { searchFunctions, findDefinition, syncFileIndex } from './codeIndexService.js';
11
+ import { CodeMapGenerator } from './services/CodeMapGenerator.js';
12
+ import { analyzeDocumentation } from './analyzers/documentationAnalyzer.js';
13
+ import { CodeIndexDB } from './codeIndexDB.js';
14
+ import path from 'node:path';
15
+ import fs from 'node:fs/promises';
16
+ import chalk from 'chalk';
17
+ export const tools = [
18
+ // Core Audit Tools
19
+ {
20
+ name: 'audit',
21
+ description: 'Run a comprehensive code audit on files or directories, including React component analysis',
22
+ parameters: [
23
+ {
24
+ name: 'path',
25
+ type: 'string',
26
+ required: false,
27
+ description: 'The file or directory path to audit (defaults to current directory)',
28
+ default: process.cwd(),
29
+ },
30
+ {
31
+ name: 'analyzers',
32
+ type: 'array',
33
+ required: false,
34
+ description: 'List of analyzers to run (solid, dry, documentation, react, data-access)',
35
+ default: ['solid', 'dry', 'documentation', 'react', 'data-access'],
36
+ },
37
+ {
38
+ name: 'minSeverity',
39
+ type: 'string',
40
+ required: false,
41
+ description: 'Minimum severity level to report',
42
+ default: 'warning',
43
+ enum: ['info', 'warning', 'critical'],
44
+ },
45
+ {
46
+ name: 'indexFunctions',
47
+ type: 'boolean',
48
+ required: false,
49
+ description: 'Automatically index functions during audit',
50
+ default: true,
51
+ },
52
+ {
53
+ name: 'analyzerConfigs',
54
+ type: 'object',
55
+ required: false,
56
+ description: 'Analyzer-specific configuration overrides (e.g., SOLID thresholds, DRY settings)',
57
+ },
58
+ {
59
+ name: 'generateCodeMap',
60
+ type: 'boolean',
61
+ required: false,
62
+ description: 'Generate and return a human-readable code map as part of the audit results',
63
+ default: true,
64
+ },
65
+ ],
66
+ },
67
+ {
68
+ name: 'audit_health',
69
+ description: 'Quick health check of a codebase with key metrics',
70
+ parameters: [
71
+ {
72
+ name: 'path',
73
+ type: 'string',
74
+ required: false,
75
+ description: 'The directory path to check',
76
+ default: process.cwd(),
77
+ },
78
+ {
79
+ name: 'threshold',
80
+ type: 'number',
81
+ required: false,
82
+ description: 'Health score threshold (0-100) for pass/fail',
83
+ default: 70,
84
+ },
85
+ {
86
+ name: 'indexFunctions',
87
+ type: 'boolean',
88
+ required: false,
89
+ description: 'Automatically index functions during health check',
90
+ default: true,
91
+ },
92
+ {
93
+ name: 'analyzerConfigs',
94
+ type: 'object',
95
+ required: false,
96
+ description: 'Analyzer-specific configuration overrides (e.g., SOLID thresholds, DRY settings)',
97
+ },
98
+ {
99
+ name: 'generateCodeMap',
100
+ type: 'boolean',
101
+ required: false,
102
+ description: 'Generate and return a human-readable code map as part of the health check results',
103
+ default: true,
104
+ },
105
+ ],
106
+ },
107
+ // Code Index Tools
108
+ {
109
+ name: 'search_code',
110
+ description: 'Search indexed functions and React components with natural language queries. Supports operators: entity:component, component:functional|class|memo|forwardRef, hook:useState|useEffect|etc, prop:propName, dep:packageName, dependency:lodash, uses:express, calls:functionName, calledby:functionName, dependents-of:functionName, used-by:functionName, depends-on:module, imports-from:file, unused-imports, dead-imports, type:fileType, file:path, lang:language, complexity:1-10, jsdoc:true|false',
111
+ parameters: [
112
+ {
113
+ name: 'query',
114
+ type: 'string',
115
+ required: true,
116
+ description: 'Search query with natural language and/or operators. Examples: "Button component:functional", "entity:component hook:useState", "render prop:onClick", "dep:lodash", "calls:validateUser", "unused-imports", "dependents-of:authenticate"',
117
+ },
118
+ {
119
+ name: 'filters',
120
+ type: 'object',
121
+ required: false,
122
+ description: 'Optional filters (language, filePath, dependencies, componentType, entityType, searchMode). Set searchMode to "content" to search within function bodies, "metadata" for names/signatures only, or "both" for combined search',
123
+ },
124
+ {
125
+ name: 'limit',
126
+ type: 'number',
127
+ required: false,
128
+ description: 'Maximum results to return',
129
+ default: 50,
130
+ },
131
+ {
132
+ name: 'offset',
133
+ type: 'number',
134
+ required: false,
135
+ description: 'Offset for pagination',
136
+ default: 0,
137
+ },
138
+ ],
139
+ },
140
+ {
141
+ name: 'find_definition',
142
+ description: 'Find the exact definition of a specific function or React component',
143
+ parameters: [
144
+ {
145
+ name: 'name',
146
+ type: 'string',
147
+ required: true,
148
+ description: 'Function or component name to find',
149
+ },
150
+ {
151
+ name: 'filePath',
152
+ type: 'string',
153
+ required: false,
154
+ description: 'Optional file path to narrow search',
155
+ },
156
+ ],
157
+ },
158
+ {
159
+ name: 'sync_index',
160
+ description: 'Synchronize, cleanup, or reset the code index',
161
+ parameters: [
162
+ {
163
+ name: 'mode',
164
+ type: 'string',
165
+ required: false,
166
+ description: 'Operation mode: sync (update), cleanup (remove stale), or reset (clear all)',
167
+ default: 'sync',
168
+ enum: ['sync', 'cleanup', 'reset'],
169
+ },
170
+ {
171
+ name: 'path',
172
+ type: 'string',
173
+ required: false,
174
+ description: 'Optional specific path to sync',
175
+ },
176
+ ],
177
+ },
178
+ // AI Configuration Tool
179
+ {
180
+ name: 'generate_ai_config',
181
+ description: 'Generate configuration files for AI coding assistants',
182
+ parameters: [
183
+ {
184
+ name: 'tools',
185
+ type: 'array',
186
+ required: true,
187
+ description: 'AI tools to configure (cursor, continue, copilot, claude, zed, windsurf, cody, aider, cline, pearai)',
188
+ },
189
+ {
190
+ name: 'outputDir',
191
+ type: 'string',
192
+ required: false,
193
+ description: 'Output directory for configuration files',
194
+ default: '.',
195
+ },
196
+ ],
197
+ },
198
+ // Workflow Guide Tool
199
+ {
200
+ name: 'get_workflow_guide',
201
+ description: 'Get recommended workflows and best practices for using code auditor tools effectively',
202
+ parameters: [
203
+ {
204
+ name: 'scenario',
205
+ type: 'string',
206
+ required: false,
207
+ description: 'Specific scenario: initial-setup, react-development, code-review, find-patterns, maintenance. Leave empty to see all.',
208
+ },
209
+ ],
210
+ },
211
+ // Analyzer Configuration Tools
212
+ {
213
+ name: 'set_analyzer_config',
214
+ description: 'Set or update analyzer configuration that persists across audit runs',
215
+ parameters: [
216
+ {
217
+ name: 'analyzerName',
218
+ type: 'string',
219
+ required: true,
220
+ description: 'The analyzer to configure (solid, dry, security, etc.)',
221
+ },
222
+ {
223
+ name: 'config',
224
+ type: 'object',
225
+ required: true,
226
+ description: 'Configuration object for the analyzer (e.g., thresholds, rules)',
227
+ },
228
+ {
229
+ name: 'projectPath',
230
+ type: 'string',
231
+ required: false,
232
+ description: 'Optional project path for project-specific config (defaults to global)',
233
+ },
234
+ ],
235
+ },
236
+ {
237
+ name: 'get_analyzer_config',
238
+ description: 'Get current configuration for an analyzer',
239
+ parameters: [
240
+ {
241
+ name: 'analyzerName',
242
+ type: 'string',
243
+ required: false,
244
+ description: 'Specific analyzer name, or omit to get all configs',
245
+ },
246
+ {
247
+ name: 'projectPath',
248
+ type: 'string',
249
+ required: false,
250
+ description: 'Optional project path to get project-specific config',
251
+ },
252
+ ],
253
+ },
254
+ {
255
+ name: 'reset_analyzer_config',
256
+ description: 'Reset analyzer configuration to defaults',
257
+ parameters: [
258
+ {
259
+ name: 'analyzerName',
260
+ type: 'string',
261
+ required: false,
262
+ description: 'Specific analyzer to reset, or omit to reset all',
263
+ },
264
+ {
265
+ name: 'projectPath',
266
+ type: 'string',
267
+ required: false,
268
+ description: 'Optional project path to reset only project-specific config',
269
+ },
270
+ ],
271
+ },
272
+ {
273
+ name: 'get_code_map_section',
274
+ description: 'Retrieve a specific section of a previously generated code map',
275
+ parameters: [
276
+ {
277
+ name: 'mapId',
278
+ type: 'string',
279
+ required: true,
280
+ description: 'The map ID returned from a previous audit with code map generation',
281
+ },
282
+ {
283
+ name: 'sectionType',
284
+ type: 'string',
285
+ required: true,
286
+ description: 'The section type to retrieve (e.g., overview, files, dependencies, documentation)',
287
+ },
288
+ ],
289
+ },
290
+ {
291
+ name: 'list_code_map_sections',
292
+ description: 'List all available sections for a code map',
293
+ parameters: [
294
+ {
295
+ name: 'mapId',
296
+ type: 'string',
297
+ required: true,
298
+ description: 'The map ID returned from a previous audit',
299
+ },
300
+ ],
301
+ },
302
+ // Database Schema Management Tools
303
+ {
304
+ name: 'generate_schema_discovery_sql',
305
+ description: 'Generate SQL queries for LLMs to extract database schema information automatically',
306
+ parameters: [
307
+ {
308
+ name: 'databaseType',
309
+ type: 'string',
310
+ required: true,
311
+ enum: ['postgresql', 'mysql', 'sqlite', 'sqlserver', 'oracle'],
312
+ description: 'Type of database to generate queries for',
313
+ },
314
+ {
315
+ name: 'includeIndexes',
316
+ type: 'boolean',
317
+ required: false,
318
+ default: true,
319
+ description: 'Include queries to discover indexes',
320
+ },
321
+ {
322
+ name: 'includeConstraints',
323
+ type: 'boolean',
324
+ required: false,
325
+ default: true,
326
+ description: 'Include queries to discover foreign key constraints',
327
+ },
328
+ {
329
+ name: 'specificTables',
330
+ type: 'array',
331
+ required: false,
332
+ description: 'Limit discovery to specific table names (optional)',
333
+ },
334
+ ],
335
+ },
336
+ {
337
+ name: 'get_schemas',
338
+ description: 'List all loaded database schemas with their metadata',
339
+ parameters: [],
340
+ },
341
+ {
342
+ name: 'search_schema',
343
+ description: 'Search for tables, columns, or relationships in loaded schemas',
344
+ parameters: [
345
+ {
346
+ name: 'query',
347
+ type: 'string',
348
+ required: true,
349
+ description: 'Search query for table/column names or descriptions',
350
+ },
351
+ {
352
+ name: 'schemaId',
353
+ type: 'string',
354
+ required: false,
355
+ description: 'Limit search to specific schema ID',
356
+ },
357
+ {
358
+ name: 'searchType',
359
+ type: 'string',
360
+ required: false,
361
+ enum: ['tables', 'columns', 'relationships', 'all'],
362
+ default: 'all',
363
+ description: 'Type of schema elements to search',
364
+ },
365
+ ],
366
+ },
367
+ {
368
+ name: 'analyze_schema_usage',
369
+ description: 'Analyze how database tables are used in the codebase',
370
+ parameters: [
371
+ {
372
+ name: 'path',
373
+ type: 'string',
374
+ required: false,
375
+ default: '.',
376
+ description: 'Path to analyze for schema usage patterns',
377
+ },
378
+ {
379
+ name: 'schemaId',
380
+ type: 'string',
381
+ required: false,
382
+ description: 'Schema ID to analyze against',
383
+ },
384
+ {
385
+ name: 'includeUsagePatterns',
386
+ type: 'boolean',
387
+ required: false,
388
+ default: true,
389
+ description: 'Whether to include detailed usage patterns',
390
+ },
391
+ ],
392
+ },
393
+ {
394
+ name: 'find_table_usage',
395
+ description: 'Find all functions that interact with a specific database table',
396
+ parameters: [
397
+ {
398
+ name: 'tableName',
399
+ type: 'string',
400
+ required: true,
401
+ description: 'Name of the table to find usage for',
402
+ },
403
+ {
404
+ name: 'usageType',
405
+ type: 'string',
406
+ required: false,
407
+ enum: ['query', 'insert', 'update', 'delete', 'reference', 'all'],
408
+ default: 'all',
409
+ description: 'Type of table usage to find',
410
+ },
411
+ ],
412
+ },
413
+ {
414
+ name: 'validate_schema_consistency',
415
+ description: 'Validate schema consistency and find potential issues',
416
+ parameters: [
417
+ {
418
+ name: 'schemaId',
419
+ type: 'string',
420
+ required: false,
421
+ description: 'Schema ID to validate (validates all if not specified)',
422
+ },
423
+ {
424
+ name: 'checkCircularDeps',
425
+ type: 'boolean',
426
+ required: false,
427
+ default: true,
428
+ description: 'Check for circular dependencies',
429
+ },
430
+ {
431
+ name: 'checkNamingConventions',
432
+ type: 'boolean',
433
+ required: false,
434
+ default: true,
435
+ description: 'Check naming convention compliance',
436
+ },
437
+ ],
438
+ },
439
+ ];
440
+ // UI-specific tools (only available in UI mode)
441
+ export const uiTools = [
442
+ {
443
+ name: 'audit_dashboard',
444
+ description: 'Generates an interactive dashboard with detailed audit findings, code maps, and remediation options.',
445
+ parameters: [
446
+ {
447
+ name: 'path',
448
+ type: 'string',
449
+ required: false,
450
+ description: 'Path to audit',
451
+ default: '.',
452
+ },
453
+ {
454
+ name: 'analyzers',
455
+ type: 'array',
456
+ required: false,
457
+ description: 'Analyzers to run',
458
+ default: ['solid', 'dry', 'documentation', 'react', 'data-access'],
459
+ },
460
+ {
461
+ name: 'minSeverity',
462
+ type: 'string',
463
+ required: false,
464
+ description: 'Minimum severity level',
465
+ default: 'warning',
466
+ enum: ['info', 'warning', 'critical'],
467
+ },
468
+ ],
469
+ },
470
+ {
471
+ name: 'code_map_viewer',
472
+ description: 'Generates an interactive, navigable code map with file structure, complexity analysis, and documentation coverage.',
473
+ parameters: [
474
+ {
475
+ name: 'path',
476
+ type: 'string',
477
+ required: false,
478
+ description: 'Path to analyze',
479
+ default: '.',
480
+ },
481
+ ],
482
+ },
483
+ ];
484
+ /**
485
+ * Shared tool handler implementations
486
+ */
487
+ export class ToolHandlers {
488
+ static async handleAudit(args) {
489
+ const auditPath = path.resolve(args.path || process.cwd());
490
+ const indexFunctions = args.indexFunctions !== false; // Default true
491
+ const generateCodeMap = args.generateCodeMap !== false; // Default true
492
+ // Check if path is a file or directory
493
+ const stats = await fs.stat(auditPath).catch(() => null);
494
+ const isFile = stats?.isFile() || false;
495
+ // Get stored analyzer configs from database
496
+ const db = CodeIndexDB.getInstance();
497
+ await db.initialize();
498
+ const storedConfigs = await db.getAllAnalyzerConfigs(auditPath);
499
+ // Merge stored configs with any provided configs
500
+ const analyzerConfigs = {
501
+ ...storedConfigs,
502
+ ...(args.analyzerConfigs || {})
503
+ };
504
+ const options = {
505
+ projectRoot: isFile ? path.dirname(auditPath) : auditPath,
506
+ enabledAnalyzers: args.analyzers || ['solid', 'dry', 'documentation', 'react', 'data-access'],
507
+ minSeverity: (args.minSeverity || 'warning'),
508
+ verbose: false,
509
+ indexFunctions,
510
+ ...(isFile && { includePaths: [auditPath] }),
511
+ ...(Object.keys(analyzerConfigs).length > 0 && { analyzerConfigs }),
512
+ };
513
+ // Check if this is a multi-language project by detecting Go files
514
+ console.error(chalk.yellow('[DEBUG]'), `Checking for Go files in: ${auditPath}`);
515
+ const hasGoFiles = await ToolHandlers.hasFilesWithExtensions(auditPath, ['.go']);
516
+ console.error(chalk.yellow('[DEBUG]'), `Go files detected: ${hasGoFiles}`);
517
+ let auditResult;
518
+ if (hasGoFiles) {
519
+ // Use multi-language orchestrator for projects with Go files
520
+ console.error(chalk.blue('[INFO]'), 'Multi-language project detected, using LanguageOrchestrator');
521
+ const runtimeManager = new RuntimeManager();
522
+ await runtimeManager.initialize();
523
+ const codeIndex = CodeIndexDB.getInstance();
524
+ await codeIndex.initialize();
525
+ const orchestrator = new LanguageOrchestrator(runtimeManager, codeIndex);
526
+ const polyglotResult = await orchestrator.analyzePolyglotProject(auditPath, {
527
+ analyzers: options.enabledAnalyzers,
528
+ minSeverity: options.minSeverity,
529
+ updateIndex: indexFunctions,
530
+ enableCrossLanguageAnalysis: true,
531
+ buildCrossReferences: true,
532
+ });
533
+ // Convert polyglot result to legacy audit result format
534
+ auditResult = ToolHandlers.convertPolyglotToAuditResult(polyglotResult, auditPath);
535
+ }
536
+ else {
537
+ // Use legacy audit system for TypeScript-only projects
538
+ const runner = createAuditRunner(options);
539
+ auditResult = await runner.run();
540
+ }
541
+ // Handle function indexing if enabled and functions were collected
542
+ let indexingResult = null;
543
+ if (indexFunctions && auditResult.metadata.fileToFunctionsMap) {
544
+ try {
545
+ const syncStats = { added: 0, updated: 0, removed: 0 };
546
+ // Sync each file's functions to handle additions, updates, and removals
547
+ for (const [filePath, functions] of Object.entries(auditResult.metadata.fileToFunctionsMap)) {
548
+ const fileStats = await syncFileIndex(filePath, functions);
549
+ syncStats.added += fileStats.added;
550
+ syncStats.updated += fileStats.updated;
551
+ syncStats.removed += fileStats.removed;
552
+ }
553
+ indexingResult = {
554
+ success: true,
555
+ registered: syncStats.added + syncStats.updated,
556
+ failed: 0,
557
+ syncStats
558
+ };
559
+ console.error(chalk.blue('[INFO]'), `Synced functions: ${syncStats.added} added, ${syncStats.updated} updated, ${syncStats.removed} removed`);
560
+ }
561
+ catch (error) {
562
+ console.error(chalk.yellow('[WARN]'), 'Failed to sync functions:', error);
563
+ }
564
+ }
565
+ // Generate code map if requested and functions were indexed
566
+ let codeMapResult = null;
567
+ if (generateCodeMap && indexingResult && indexingResult.success) {
568
+ try {
569
+ const mapGenerator = new CodeMapGenerator();
570
+ const mapOptions = {
571
+ includeComplexity: true,
572
+ includeDocumentation: true,
573
+ includeDependencies: true,
574
+ includeUsage: false,
575
+ groupByDirectory: true,
576
+ maxDepth: 10,
577
+ showUnusedImports: true,
578
+ minComplexity: 7,
579
+ };
580
+ // Generate documentation metrics
581
+ let documentation = undefined;
582
+ try {
583
+ const files = Object.keys(auditResult.metadata.fileToFunctionsMap || {});
584
+ if (files.length > 0) {
585
+ const docResult = await analyzeDocumentation(files);
586
+ documentation = docResult.metrics;
587
+ }
588
+ }
589
+ catch (docError) {
590
+ console.error(chalk.yellow('[WARN]'), 'Failed to analyze documentation:', docError);
591
+ }
592
+ // Use paginated code map generation
593
+ const paginatedResult = await mapGenerator.generatePaginatedCodeMap(auditPath, {
594
+ ...mapOptions,
595
+ includeDocumentation: !!documentation
596
+ });
597
+ codeMapResult = {
598
+ success: true,
599
+ mapId: paginatedResult.mapId,
600
+ summary: paginatedResult.summary,
601
+ quickPreview: paginatedResult.quickPreview,
602
+ sections: paginatedResult.summary.sectionsAvailable,
603
+ documentationCoverage: documentation?.coverageScore
604
+ };
605
+ console.error(chalk.blue('[INFO]'), `Generated paginated code map: ${paginatedResult.summary.stats.totalFiles} files, ${paginatedResult.summary.totalSections} sections`);
606
+ }
607
+ catch (error) {
608
+ console.error(chalk.yellow('[WARN]'), 'Failed to generate code map:', error);
609
+ codeMapResult = {
610
+ success: false,
611
+ error: error instanceof Error ? error.message : 'Failed to generate code map'
612
+ };
613
+ }
614
+ }
615
+ // Format for MCP
616
+ return {
617
+ summary: {
618
+ totalViolations: auditResult.summary.totalViolations,
619
+ criticalIssues: auditResult.summary.criticalIssues,
620
+ warnings: auditResult.summary.warnings,
621
+ suggestions: auditResult.summary.suggestions,
622
+ filesAnalyzed: auditResult.metadata.filesAnalyzed,
623
+ executionTime: auditResult.metadata.auditDuration,
624
+ healthScore: ToolHandlers.calculateHealthScore(auditResult),
625
+ },
626
+ violations: ToolHandlers.getAllViolations(auditResult).slice(0, 100), // Limit to first 100
627
+ recommendations: auditResult.recommendations,
628
+ ...(indexingResult && { functionIndexing: indexingResult }),
629
+ ...(codeMapResult && { codeMap: codeMapResult }),
630
+ };
631
+ }
632
+ static async handleAuditHealth(args) {
633
+ const auditPath = path.resolve(args.path || process.cwd());
634
+ const threshold = args.threshold || 70;
635
+ const indexFunctions = args.indexFunctions !== false; // Default true
636
+ const generateCodeMap = args.generateCodeMap !== false; // Default true
637
+ // Get stored analyzer configs from database
638
+ const db = CodeIndexDB.getInstance();
639
+ await db.initialize();
640
+ const storedConfigs = await db.getAllAnalyzerConfigs(auditPath);
641
+ // Merge stored configs with any provided configs
642
+ const analyzerConfigs = {
643
+ ...storedConfigs,
644
+ ...(args.analyzerConfigs || {})
645
+ };
646
+ const runner = createAuditRunner({
647
+ projectRoot: auditPath,
648
+ enabledAnalyzers: ['solid', 'dry', 'documentation', 'react', 'data-access'],
649
+ minSeverity: 'warning',
650
+ verbose: false,
651
+ indexFunctions,
652
+ ...(Object.keys(analyzerConfigs).length > 0 && { analyzerConfigs }),
653
+ });
654
+ const auditResult = await runner.run();
655
+ const healthScore = ToolHandlers.calculateHealthScore(auditResult);
656
+ // Handle function indexing if enabled and functions were collected
657
+ let indexingResult = null;
658
+ if (indexFunctions && auditResult.metadata.fileToFunctionsMap) {
659
+ try {
660
+ const syncStats = { added: 0, updated: 0, removed: 0 };
661
+ // Sync each file's functions to handle additions, updates, and removals
662
+ for (const [filePath, functions] of Object.entries(auditResult.metadata.fileToFunctionsMap)) {
663
+ const fileStats = await syncFileIndex(filePath, functions);
664
+ syncStats.added += fileStats.added;
665
+ syncStats.updated += fileStats.updated;
666
+ syncStats.removed += fileStats.removed;
667
+ }
668
+ indexingResult = {
669
+ success: true,
670
+ registered: syncStats.added + syncStats.updated,
671
+ failed: 0,
672
+ syncStats
673
+ };
674
+ console.error(chalk.blue('[INFO]'), `Synced functions: ${syncStats.added} added, ${syncStats.updated} updated, ${syncStats.removed} removed`);
675
+ }
676
+ catch (error) {
677
+ console.error(chalk.yellow('[WARN]'), 'Failed to sync functions:', error);
678
+ }
679
+ }
680
+ // Generate code map if requested and functions were indexed
681
+ let codeMapResult = null;
682
+ if (generateCodeMap && indexingResult && indexingResult.success) {
683
+ try {
684
+ const mapGenerator = new CodeMapGenerator();
685
+ const mapOptions = {
686
+ includeComplexity: true,
687
+ includeDocumentation: true,
688
+ includeDependencies: true,
689
+ includeUsage: false,
690
+ groupByDirectory: true,
691
+ maxDepth: 8, // Slightly smaller for health check
692
+ showUnusedImports: true,
693
+ minComplexity: 7,
694
+ };
695
+ // Generate documentation metrics
696
+ let documentation = undefined;
697
+ try {
698
+ const files = Object.keys(auditResult.metadata.fileToFunctionsMap || {});
699
+ if (files.length > 0) {
700
+ const docResult = await analyzeDocumentation(files);
701
+ documentation = docResult.metrics;
702
+ }
703
+ }
704
+ catch (docError) {
705
+ console.error(chalk.yellow('[WARN]'), 'Failed to analyze documentation:', docError);
706
+ }
707
+ // Use paginated code map generation for health check too
708
+ const paginatedResult = await mapGenerator.generatePaginatedCodeMap(auditPath, {
709
+ ...mapOptions,
710
+ includeDocumentation: !!documentation
711
+ });
712
+ codeMapResult = {
713
+ success: true,
714
+ mapId: paginatedResult.mapId,
715
+ summary: paginatedResult.summary,
716
+ quickPreview: paginatedResult.quickPreview,
717
+ sections: paginatedResult.summary.sectionsAvailable,
718
+ documentationCoverage: documentation?.coverageScore
719
+ };
720
+ console.error(chalk.blue('[INFO]'), `Generated paginated code map: ${paginatedResult.summary.stats.totalFiles} files, ${paginatedResult.summary.totalSections} sections`);
721
+ }
722
+ catch (error) {
723
+ console.error(chalk.yellow('[WARN]'), 'Failed to generate code map:', error);
724
+ codeMapResult = {
725
+ success: false,
726
+ error: error instanceof Error ? error.message : 'Failed to generate code map'
727
+ };
728
+ }
729
+ }
730
+ return {
731
+ healthScore,
732
+ threshold,
733
+ passed: healthScore >= threshold,
734
+ status: healthScore >= threshold ? 'healthy' : 'needs-attention',
735
+ metrics: {
736
+ filesAnalyzed: auditResult.metadata.filesAnalyzed,
737
+ totalViolations: auditResult.summary.totalViolations,
738
+ criticalViolations: auditResult.summary.criticalIssues,
739
+ warningViolations: auditResult.summary.warnings,
740
+ },
741
+ recommendation: ToolHandlers.getHealthRecommendation(healthScore, auditResult),
742
+ ...(indexingResult && { functionIndexing: indexingResult }),
743
+ ...(codeMapResult && { codeMap: codeMapResult }),
744
+ };
745
+ }
746
+ // Add all other tool handlers here following the same pattern...
747
+ // (I'll include key ones for brevity)
748
+ static async handleSearchCode(args) {
749
+ const query = args.query;
750
+ const filters = args.filters;
751
+ const limit = args.limit || 50;
752
+ const offset = args.offset || 0;
753
+ if (query !== undefined && typeof query !== 'string') {
754
+ throw new Error('query must be a string');
755
+ }
756
+ return await searchFunctions({
757
+ query,
758
+ filters,
759
+ limit,
760
+ offset
761
+ });
762
+ }
763
+ static async handleFindDefinition(args) {
764
+ const name = args.name;
765
+ const filePath = args.filePath;
766
+ if (!name || typeof name !== 'string') {
767
+ throw new Error('name must be a non-empty string');
768
+ }
769
+ const definition = await findDefinition(name, filePath);
770
+ return definition || { error: 'Function not found' };
771
+ }
772
+ // Schema Management Tool Handlers
773
+ static async handleGenerateSchemaDiscoverySQL(args) {
774
+ const databaseType = args.databaseType;
775
+ const includeIndexes = args.includeIndexes !== false;
776
+ const includeConstraints = args.includeConstraints !== false;
777
+ const specificTables = args.specificTables;
778
+ if (!databaseType || typeof databaseType !== 'string') {
779
+ throw new Error('databaseType must be a non-empty string');
780
+ }
781
+ const sqlQueries = ToolHandlers.generateSchemaDiscoveryQueries(databaseType, includeIndexes, includeConstraints, specificTables);
782
+ return {
783
+ databaseType,
784
+ queries: sqlQueries,
785
+ instructions: [
786
+ "Execute these SQL queries against your database",
787
+ "Copy the results and use create_schema_from_sql_result to import them",
788
+ "Each query discovers different aspects of your database schema",
789
+ "Run them in order and collect all results"
790
+ ],
791
+ nextStep: "Use create_schema_from_sql_result with the query results"
792
+ };
793
+ }
794
+ static async handleCreateSchemaFromSqlResult(args) {
795
+ const schemaName = args.schemaName;
796
+ const databaseType = args.databaseType;
797
+ const tablesData = args.tablesData;
798
+ const columnsData = args.columnsData;
799
+ const constraintsData = args.constraintsData;
800
+ if (!schemaName || !databaseType || !tablesData || !columnsData) {
801
+ throw new Error('schemaName, databaseType, tablesData, and columnsData are required');
802
+ }
803
+ try {
804
+ const schema = ToolHandlers.buildSchemaFromSqlData(schemaName, databaseType, tablesData, columnsData, constraintsData);
805
+ const db = CodeIndexDB.getInstance();
806
+ await db.initialize();
807
+ const schemaId = await db.storeSchema(schema);
808
+ return {
809
+ success: true,
810
+ schemaId,
811
+ schemaName: schema.name,
812
+ stats: {
813
+ databaseCount: schema.databases.length,
814
+ tableCount: schema.databases.reduce((acc, db) => acc + db.tables.length, 0),
815
+ columnCount: schema.databases.reduce((acc, db) => acc + db.tables.reduce((tAcc, table) => tAcc + table.columns.length, 0), 0)
816
+ },
817
+ message: `Schema '${schema.name}' created from SQL results`
818
+ };
819
+ }
820
+ catch (error) {
821
+ return {
822
+ success: false,
823
+ error: `Failed to create schema from SQL results: ${error instanceof Error ? error.message : 'Unknown error'}`
824
+ };
825
+ }
826
+ }
827
+ static async handleAddTableManually(args) {
828
+ const schemaName = args.schemaName;
829
+ const tableName = args.tableName;
830
+ const columns = args.columns;
831
+ const databaseType = args.databaseType || 'postgresql';
832
+ if (!schemaName || !tableName || !columns || !Array.isArray(columns)) {
833
+ throw new Error('schemaName, tableName, and columns array are required');
834
+ }
835
+ try {
836
+ const db = CodeIndexDB.getInstance();
837
+ await db.initialize();
838
+ // Get existing schema or create new one
839
+ const existingSchemas = await db.getAllSchemas();
840
+ let schema = existingSchemas.find(s => s.schema.name === schemaName)?.schema;
841
+ if (!schema) {
842
+ // Create new schema
843
+ schema = {
844
+ version: "1.0.0",
845
+ name: schemaName,
846
+ description: `Manually created schema: ${schemaName}`,
847
+ databases: [{
848
+ name: 'default',
849
+ type: databaseType,
850
+ tables: []
851
+ }]
852
+ };
853
+ }
854
+ // Add the table
855
+ const newTable = {
856
+ name: tableName,
857
+ type: 'table',
858
+ columns: columns.map(col => ({
859
+ name: col.name,
860
+ type: col.type,
861
+ nullable: col.nullable !== false,
862
+ primaryKey: col.primaryKey || false,
863
+ unique: col.unique || false,
864
+ indexed: col.indexed || false,
865
+ description: col.description
866
+ })),
867
+ references: [],
868
+ indexes: []
869
+ };
870
+ schema.databases[0].tables.push(newTable);
871
+ // Store updated schema
872
+ const schemaId = await db.storeSchema(schema);
873
+ return {
874
+ success: true,
875
+ schemaId,
876
+ tableName,
877
+ columnCount: columns.length,
878
+ message: `Table '${tableName}' added to schema '${schemaName}'`
879
+ };
880
+ }
881
+ catch (error) {
882
+ return {
883
+ success: false,
884
+ error: `Failed to add table: ${error instanceof Error ? error.message : 'Unknown error'}`
885
+ };
886
+ }
887
+ }
888
+ static async handleListSchemas(args) {
889
+ try {
890
+ const db = CodeIndexDB.getInstance();
891
+ await db.initialize();
892
+ const schemas = await db.getAllSchemas();
893
+ const stats = await db.getSchemaStats();
894
+ return {
895
+ schemas: schemas.map(s => ({
896
+ schemaId: s.schemaId,
897
+ name: s.schema.name,
898
+ description: s.schema.description,
899
+ databaseCount: s.schema.databases.length,
900
+ tableCount: s.metadata.tableCount,
901
+ relationshipCount: s.metadata.relationshipCount,
902
+ indexedAt: s.metadata.indexedAt
903
+ })),
904
+ totalStats: stats
905
+ };
906
+ }
907
+ catch (error) {
908
+ return {
909
+ success: false,
910
+ error: `Failed to list schemas: ${error instanceof Error ? error.message : 'Unknown error'}`
911
+ };
912
+ }
913
+ }
914
+ static async handleSearchSchemaElements(args) {
915
+ const query = args.query;
916
+ const elementType = args.elementType || 'all';
917
+ if (!query || typeof query !== 'string') {
918
+ throw new Error('query must be a non-empty string');
919
+ }
920
+ try {
921
+ const db = CodeIndexDB.getInstance();
922
+ await db.initialize();
923
+ const schemas = (await db.getAllSchemas()).map(s => s.schema);
924
+ const results = [];
925
+ const queryLower = query.toLowerCase();
926
+ for (const schema of schemas) {
927
+ for (const database of schema.databases) {
928
+ for (const table of database.tables) {
929
+ // Search tables
930
+ if ((elementType === 'all' || elementType === 'tables') &&
931
+ (table.name.toLowerCase().includes(queryLower) ||
932
+ table.description?.toLowerCase().includes(queryLower))) {
933
+ results.push({
934
+ type: 'table',
935
+ tableName: table.name,
936
+ databaseName: database.name,
937
+ schemaName: schema.name,
938
+ description: table.description,
939
+ columnCount: table.columns.length,
940
+ tags: table.tags
941
+ });
942
+ }
943
+ // Search columns
944
+ if (elementType === 'all' || elementType === 'columns') {
945
+ for (const column of table.columns) {
946
+ if (column.name.toLowerCase().includes(queryLower) ||
947
+ column.description?.toLowerCase().includes(queryLower) ||
948
+ column.type.toLowerCase().includes(queryLower)) {
949
+ results.push({
950
+ type: 'column',
951
+ columnName: column.name,
952
+ tableName: table.name,
953
+ databaseName: database.name,
954
+ schemaName: schema.name,
955
+ columnType: column.type,
956
+ description: column.description,
957
+ nullable: column.nullable,
958
+ primaryKey: column.primaryKey
959
+ });
960
+ }
961
+ }
962
+ }
963
+ }
964
+ }
965
+ }
966
+ return {
967
+ query,
968
+ elementType,
969
+ resultCount: results.length,
970
+ results
971
+ };
972
+ }
973
+ catch (error) {
974
+ return {
975
+ success: false,
976
+ error: `Failed to search schema elements: ${error instanceof Error ? error.message : 'Unknown error'}`
977
+ };
978
+ }
979
+ }
980
+ // Schema Helper Functions
981
+ static generateSchemaDiscoveryQueries(databaseType, includeIndexes, includeConstraints, specificTables) {
982
+ const queries = [];
983
+ const tableFilter = specificTables && specificTables.length > 0
984
+ ? `WHERE table_name IN (${specificTables.map(t => `'${t}'`).join(', ')})`
985
+ : '';
986
+ switch (databaseType.toLowerCase()) {
987
+ case 'postgresql':
988
+ queries.push({
989
+ name: 'tables',
990
+ sql: `SELECT table_name, table_type, table_schema
991
+ FROM information_schema.tables
992
+ WHERE table_schema NOT IN ('information_schema', 'pg_catalog') ${tableFilter}
993
+ ORDER BY table_schema, table_name;`,
994
+ description: 'Get all tables and views'
995
+ });
996
+ queries.push({
997
+ name: 'columns',
998
+ sql: `SELECT table_name, column_name, data_type, is_nullable, column_default,
999
+ character_maximum_length, numeric_precision, numeric_scale
1000
+ FROM information_schema.columns
1001
+ WHERE table_schema NOT IN ('information_schema', 'pg_catalog') ${tableFilter}
1002
+ ORDER BY table_name, ordinal_position;`,
1003
+ description: 'Get all columns with types and constraints'
1004
+ });
1005
+ if (includeConstraints) {
1006
+ queries.push({
1007
+ name: 'foreign_keys',
1008
+ sql: `SELECT tc.table_name, kcu.column_name, ccu.table_name AS foreign_table_name,
1009
+ ccu.column_name AS foreign_column_name, rc.delete_rule, rc.update_rule
1010
+ FROM information_schema.table_constraints AS tc
1011
+ JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
1012
+ JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
1013
+ JOIN information_schema.referential_constraints AS rc ON tc.constraint_name = rc.constraint_name
1014
+ WHERE tc.constraint_type = 'FOREIGN KEY' ${tableFilter.replace('table_name', 'tc.table_name')}
1015
+ ORDER BY tc.table_name, kcu.column_name;`,
1016
+ description: 'Get foreign key relationships'
1017
+ });
1018
+ }
1019
+ if (includeIndexes) {
1020
+ queries.push({
1021
+ name: 'indexes',
1022
+ sql: `SELECT tablename, indexname, indexdef
1023
+ FROM pg_indexes
1024
+ WHERE schemaname NOT IN ('information_schema', 'pg_catalog') ${tableFilter.replace('table_name', 'tablename')}
1025
+ ORDER BY tablename, indexname;`,
1026
+ description: 'Get all indexes'
1027
+ });
1028
+ }
1029
+ break;
1030
+ case 'mysql':
1031
+ queries.push({
1032
+ name: 'tables',
1033
+ sql: `SELECT table_name, table_type, table_schema
1034
+ FROM information_schema.tables
1035
+ WHERE table_schema = DATABASE() ${tableFilter}
1036
+ ORDER BY table_name;`,
1037
+ description: 'Get all tables and views'
1038
+ });
1039
+ queries.push({
1040
+ name: 'columns',
1041
+ sql: `SELECT table_name, column_name, data_type, is_nullable, column_default,
1042
+ character_maximum_length, numeric_precision, numeric_scale,
1043
+ column_key, extra
1044
+ FROM information_schema.columns
1045
+ WHERE table_schema = DATABASE() ${tableFilter}
1046
+ ORDER BY table_name, ordinal_position;`,
1047
+ description: 'Get all columns with types and constraints'
1048
+ });
1049
+ if (includeConstraints) {
1050
+ queries.push({
1051
+ name: 'foreign_keys',
1052
+ sql: `SELECT table_name, column_name, referenced_table_name, referenced_column_name,
1053
+ delete_rule, update_rule
1054
+ FROM information_schema.key_column_usage
1055
+ WHERE table_schema = DATABASE() AND referenced_table_name IS NOT NULL ${tableFilter}
1056
+ ORDER BY table_name, column_name;`,
1057
+ description: 'Get foreign key relationships'
1058
+ });
1059
+ }
1060
+ break;
1061
+ case 'sqlite':
1062
+ queries.push({
1063
+ name: 'tables',
1064
+ sql: `SELECT name as table_name, type as table_type
1065
+ FROM sqlite_master
1066
+ WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
1067
+ ORDER BY name;`,
1068
+ description: 'Get all tables and views'
1069
+ });
1070
+ queries.push({
1071
+ name: 'table_info',
1072
+ sql: `-- Run this for each table: PRAGMA table_info(table_name);
1073
+ -- This will give you column information for each table`,
1074
+ description: 'Get column information (run PRAGMA table_info for each table)'
1075
+ });
1076
+ break;
1077
+ }
1078
+ return queries;
1079
+ }
1080
+ static buildSchemaFromSqlData(schemaName, databaseType, tablesData, columnsData, constraintsData) {
1081
+ // Build schema from SQL results
1082
+ const tables = [];
1083
+ const tableMap = new Map();
1084
+ // Process tables
1085
+ const tableRows = Array.isArray(tablesData) ? tablesData : tablesData.rows || [];
1086
+ for (const row of tableRows) {
1087
+ const tableName = row.table_name || row.TABLE_NAME;
1088
+ if (!tableMap.has(tableName)) {
1089
+ tableMap.set(tableName, {
1090
+ name: tableName,
1091
+ type: 'table',
1092
+ columns: [],
1093
+ references: [],
1094
+ indexes: []
1095
+ });
1096
+ }
1097
+ }
1098
+ // Process columns
1099
+ const columnRows = Array.isArray(columnsData) ? columnsData : columnsData.rows || [];
1100
+ for (const row of columnRows) {
1101
+ const tableName = row.table_name || row.TABLE_NAME;
1102
+ const table = tableMap.get(tableName);
1103
+ if (table) {
1104
+ table.columns.push({
1105
+ name: row.column_name || row.COLUMN_NAME,
1106
+ type: row.data_type || row.DATA_TYPE,
1107
+ nullable: (row.is_nullable || row.IS_NULLABLE) === 'YES',
1108
+ primaryKey: (row.column_key || row.COLUMN_KEY) === 'PRI',
1109
+ defaultValue: row.column_default || row.COLUMN_DEFAULT,
1110
+ length: row.character_maximum_length || row.CHARACTER_MAXIMUM_LENGTH,
1111
+ precision: row.numeric_precision || row.NUMERIC_PRECISION,
1112
+ scale: row.numeric_scale || row.NUMERIC_SCALE
1113
+ });
1114
+ }
1115
+ }
1116
+ // Process constraints if provided
1117
+ if (constraintsData) {
1118
+ const constraintRows = Array.isArray(constraintsData) ? constraintsData : constraintsData.rows || [];
1119
+ for (const row of constraintRows) {
1120
+ const tableName = row.table_name || row.TABLE_NAME;
1121
+ const table = tableMap.get(tableName);
1122
+ if (table) {
1123
+ table.references.push({
1124
+ foreignKey: row.column_name || row.COLUMN_NAME,
1125
+ referencedTable: row.foreign_table_name || row.REFERENCED_TABLE_NAME,
1126
+ referencedColumn: row.foreign_column_name || row.REFERENCED_COLUMN_NAME,
1127
+ onDelete: row.delete_rule || row.DELETE_RULE,
1128
+ onUpdate: row.update_rule || row.UPDATE_RULE
1129
+ });
1130
+ }
1131
+ }
1132
+ }
1133
+ return {
1134
+ version: "1.0.0",
1135
+ name: schemaName,
1136
+ description: `Schema discovered from ${databaseType} database`,
1137
+ databases: [{
1138
+ name: 'main',
1139
+ type: databaseType,
1140
+ tables: Array.from(tableMap.values())
1141
+ }],
1142
+ metadata: {
1143
+ createdAt: new Date().toISOString(),
1144
+ source: 'sql-discovery'
1145
+ }
1146
+ };
1147
+ }
1148
+ // Helper functions
1149
+ static getAllViolations(result) {
1150
+ const violations = [];
1151
+ for (const [analyzerName, analyzerResult] of Object.entries(result.analyzerResults)) {
1152
+ for (const violation of analyzerResult.violations) {
1153
+ violations.push({
1154
+ ...violation,
1155
+ analyzer: analyzerName,
1156
+ });
1157
+ }
1158
+ }
1159
+ return violations;
1160
+ }
1161
+ static calculateHealthScore(result) {
1162
+ const filesAnalyzed = result.metadata?.filesAnalyzed || 1;
1163
+ const critical = result.summary.criticalIssues || 0;
1164
+ const warnings = result.summary.warnings || 0;
1165
+ const suggestions = result.summary.suggestions || 0;
1166
+ const weights = {
1167
+ critical: 10,
1168
+ warning: 3,
1169
+ suggestion: 0.5
1170
+ };
1171
+ const weightedViolations = (critical * weights.critical) +
1172
+ (warnings * weights.warning) +
1173
+ (suggestions * weights.suggestion);
1174
+ const violationsPerFile = weightedViolations / filesAnalyzed;
1175
+ let score = 100 - (violationsPerFile * 2);
1176
+ return Math.max(0, Math.round(Math.min(100, score)));
1177
+ }
1178
+ static getHealthRecommendation(score, result) {
1179
+ if (score >= 90)
1180
+ return 'Excellent code health!';
1181
+ if (score >= 70)
1182
+ return 'Good code health with room for improvement';
1183
+ if (result.summary.criticalIssues > 0) {
1184
+ return `Fix ${result.summary.criticalIssues} critical violations first`;
1185
+ }
1186
+ return 'Code health needs attention - run detailed audit';
1187
+ }
1188
+ static async hasFilesWithExtensions(dirPath, extensions) {
1189
+ try {
1190
+ console.error(chalk.yellow('[DEBUG]'), `Checking path: ${dirPath} for extensions: ${extensions.join(', ')}`);
1191
+ const stats = await fs.stat(dirPath);
1192
+ if (stats.isFile()) {
1193
+ const ext = path.extname(dirPath).toLowerCase();
1194
+ console.error(chalk.yellow('[DEBUG]'), `File: ${dirPath}, ext: ${ext}, match: ${extensions.includes(ext)}`);
1195
+ return extensions.includes(ext);
1196
+ }
1197
+ // Use simple readdir and walk manually to avoid recursive option issues
1198
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
1199
+ console.error(chalk.yellow('[DEBUG]'), `Directory: ${dirPath}, entries: ${entries.length}`);
1200
+ for (const entry of entries) {
1201
+ if (entry.isFile()) {
1202
+ const ext = path.extname(entry.name).toLowerCase();
1203
+ console.error(chalk.yellow('[DEBUG]'), `File: ${entry.name}, ext: ${ext}, match: ${extensions.includes(ext)}`);
1204
+ if (extensions.includes(ext)) {
1205
+ console.error(chalk.green('[DEBUG]'), `Found matching file: ${entry.name}`);
1206
+ return true;
1207
+ }
1208
+ }
1209
+ else if (entry.isDirectory()) {
1210
+ // Recursively check subdirectories
1211
+ const subDirPath = path.join(dirPath, entry.name);
1212
+ console.error(chalk.yellow('[DEBUG]'), `Checking subdirectory: ${subDirPath}`);
1213
+ const hasInSubdir = await ToolHandlers.hasFilesWithExtensions(subDirPath, extensions);
1214
+ if (hasInSubdir) {
1215
+ console.error(chalk.green('[DEBUG]'), `Found matching files in subdirectory: ${subDirPath}`);
1216
+ return true;
1217
+ }
1218
+ }
1219
+ }
1220
+ console.error(chalk.red('[DEBUG]'), `No matching files found in: ${dirPath}`);
1221
+ return false;
1222
+ }
1223
+ catch (error) {
1224
+ console.error(chalk.red('[DEBUG]'), `Error checking path ${dirPath}:`, error);
1225
+ return false;
1226
+ }
1227
+ }
1228
+ static convertPolyglotToAuditResult(polyglotResult, auditPath) {
1229
+ // Convert polyglot result to legacy AuditResult format
1230
+ const violations = polyglotResult.violations || [];
1231
+ const criticalIssues = violations.filter((v) => v.severity === 'critical').length;
1232
+ const warnings = violations.filter((v) => v.severity === 'warning').length;
1233
+ const suggestions = violations.filter((v) => v.severity === 'suggestion').length;
1234
+ return {
1235
+ timestamp: new Date(),
1236
+ summary: {
1237
+ totalViolations: violations.length,
1238
+ criticalIssues,
1239
+ warnings,
1240
+ suggestions,
1241
+ totalFiles: polyglotResult.metrics?.totalFiles || 0,
1242
+ violationsByCategory: {},
1243
+ topIssues: violations.slice(0, 5)
1244
+ },
1245
+ analyzerResults: {
1246
+ solid: {
1247
+ violations: violations.filter((v) => v.analyzer === 'solid'),
1248
+ filesProcessed: polyglotResult.metrics?.totalFiles || 0,
1249
+ executionTime: polyglotResult.metrics?.executionTime || 0
1250
+ },
1251
+ dry: {
1252
+ violations: violations.filter((v) => v.analyzer === 'dry'),
1253
+ filesProcessed: polyglotResult.metrics?.totalFiles || 0,
1254
+ executionTime: polyglotResult.metrics?.executionTime || 0
1255
+ },
1256
+ go: {
1257
+ violations: violations.filter((v) => v.analyzer === 'go'),
1258
+ filesProcessed: polyglotResult.metrics?.totalFiles || 0,
1259
+ executionTime: polyglotResult.metrics?.executionTime || 0
1260
+ }
1261
+ },
1262
+ recommendations: [],
1263
+ metadata: {
1264
+ auditDuration: polyglotResult.metrics?.executionTime || 0,
1265
+ filesAnalyzed: polyglotResult.metrics?.totalFiles || 0,
1266
+ analyzersRun: polyglotResult.metrics?.analyzersRun || ['solid', 'dry'],
1267
+ fileToFunctionsMap: polyglotResult.indexEntries ? ToolHandlers.createFileToFunctionsMap(polyglotResult.indexEntries) : {}
1268
+ }
1269
+ };
1270
+ }
1271
+ static createFileToFunctionsMap(indexEntries) {
1272
+ const fileMap = {};
1273
+ for (const entry of indexEntries) {
1274
+ if (!fileMap[entry.file]) {
1275
+ fileMap[entry.file] = [];
1276
+ }
1277
+ fileMap[entry.file].push(entry);
1278
+ }
1279
+ return fileMap;
1280
+ }
1281
+ }
1282
+ //# sourceMappingURL=mcp-tools-shared.js.map