@sfdxy/mule-lint 1.7.1 → 1.8.2

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 (159) hide show
  1. package/README.md +43 -7
  2. package/dist/bin/mule-lint-mcp.d.ts +3 -0
  3. package/dist/bin/mule-lint-mcp.d.ts.map +1 -0
  4. package/dist/bin/mule-lint-mcp.js +13 -0
  5. package/dist/bin/mule-lint-mcp.js.map +1 -0
  6. package/dist/package.json +6 -3
  7. package/dist/src/core/ComplexityCalculator.d.ts.map +1 -1
  8. package/dist/src/core/ComplexityCalculator.js +8 -8
  9. package/dist/src/core/ComplexityCalculator.js.map +1 -1
  10. package/dist/src/core/FileScanner.d.ts.map +1 -1
  11. package/dist/src/core/FileScanner.js +11 -12
  12. package/dist/src/core/FileScanner.js.map +1 -1
  13. package/dist/src/core/XPathHelper.d.ts.map +1 -1
  14. package/dist/src/core/XPathHelper.js +23 -23
  15. package/dist/src/core/XPathHelper.js.map +1 -1
  16. package/dist/src/core/XmlParser.js +1 -1
  17. package/dist/src/core/XmlParser.js.map +1 -1
  18. package/dist/src/core/YamlParser.d.ts.map +1 -1
  19. package/dist/src/core/YamlParser.js +1 -1
  20. package/dist/src/core/YamlParser.js.map +1 -1
  21. package/dist/src/engine/LintEngine.d.ts.map +1 -1
  22. package/dist/src/engine/LintEngine.js +6 -4
  23. package/dist/src/engine/LintEngine.js.map +1 -1
  24. package/dist/src/formatters/CsvFormatter.d.ts.map +1 -1
  25. package/dist/src/formatters/CsvFormatter.js +6 -4
  26. package/dist/src/formatters/CsvFormatter.js.map +1 -1
  27. package/dist/src/formatters/HtmlFormatter.d.ts.map +1 -1
  28. package/dist/src/formatters/HtmlFormatter.js +12 -7
  29. package/dist/src/formatters/HtmlFormatter.js.map +1 -1
  30. package/dist/src/formatters/SarifFormatter.d.ts.map +1 -1
  31. package/dist/src/formatters/SarifFormatter.js +20 -10
  32. package/dist/src/formatters/SarifFormatter.js.map +1 -1
  33. package/dist/src/formatters/TableFormatter.d.ts.map +1 -1
  34. package/dist/src/formatters/TableFormatter.js +1 -1
  35. package/dist/src/formatters/TableFormatter.js.map +1 -1
  36. package/dist/src/mcp/index.d.ts +13 -0
  37. package/dist/src/mcp/index.d.ts.map +1 -0
  38. package/dist/src/mcp/index.js +375 -0
  39. package/dist/src/mcp/index.js.map +1 -0
  40. package/dist/src/rules/api-led/ApiLedRules.d.ts.map +1 -1
  41. package/dist/src/rules/api-led/ApiLedRules.js +3 -1
  42. package/dist/src/rules/api-led/ApiLedRules.js.map +1 -1
  43. package/dist/src/rules/base/BaseRule.d.ts.map +1 -1
  44. package/dist/src/rules/base/BaseRule.js +1 -1
  45. package/dist/src/rules/base/BaseRule.js.map +1 -1
  46. package/dist/src/rules/complexity/FlowComplexityRule.d.ts.map +1 -1
  47. package/dist/src/rules/complexity/FlowComplexityRule.js +3 -5
  48. package/dist/src/rules/complexity/FlowComplexityRule.js.map +1 -1
  49. package/dist/src/rules/dataweave/DataWeaveRules.d.ts.map +1 -1
  50. package/dist/src/rules/dataweave/DataWeaveRules.js +5 -5
  51. package/dist/src/rules/dataweave/DataWeaveRules.js.map +1 -1
  52. package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.d.ts +21 -0
  53. package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.d.ts.map +1 -0
  54. package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.js +162 -0
  55. package/dist/src/rules/dataweave/Java17DWErrorHandlingRule.js.map +1 -0
  56. package/dist/src/rules/documentation/FlowDescriptionRule.d.ts.map +1 -1
  57. package/dist/src/rules/documentation/FlowDescriptionRule.js +1 -1
  58. package/dist/src/rules/documentation/FlowDescriptionRule.js.map +1 -1
  59. package/dist/src/rules/documentation/MissingDocNameRule.d.ts.map +1 -1
  60. package/dist/src/rules/documentation/MissingDocNameRule.js +1 -1
  61. package/dist/src/rules/documentation/MissingDocNameRule.js.map +1 -1
  62. package/dist/src/rules/error-handling/CorrelationIdRule.d.ts.map +1 -1
  63. package/dist/src/rules/error-handling/CorrelationIdRule.js +1 -1
  64. package/dist/src/rules/error-handling/CorrelationIdRule.js.map +1 -1
  65. package/dist/src/rules/error-handling/GenericErrorRule.d.ts.map +1 -1
  66. package/dist/src/rules/error-handling/GenericErrorRule.js +2 -5
  67. package/dist/src/rules/error-handling/GenericErrorRule.js.map +1 -1
  68. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts +1 -1
  69. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.d.ts.map +1 -1
  70. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js +2 -2
  71. package/dist/src/rules/error-handling/GlobalErrorHandlerRule.js.map +1 -1
  72. package/dist/src/rules/error-handling/HttpStatusRule.d.ts.map +1 -1
  73. package/dist/src/rules/error-handling/HttpStatusRule.js +1 -1
  74. package/dist/src/rules/error-handling/HttpStatusRule.js.map +1 -1
  75. package/dist/src/rules/error-handling/MissingErrorHandlerRule.d.ts.map +1 -1
  76. package/dist/src/rules/error-handling/MissingErrorHandlerRule.js +9 -1
  77. package/dist/src/rules/error-handling/MissingErrorHandlerRule.js.map +1 -1
  78. package/dist/src/rules/experimental/ExperimentalRules.d.ts.map +1 -1
  79. package/dist/src/rules/experimental/ExperimentalRules.js +1 -1
  80. package/dist/src/rules/experimental/ExperimentalRules.js.map +1 -1
  81. package/dist/src/rules/http/HttpContentTypeRule.d.ts.map +1 -1
  82. package/dist/src/rules/http/HttpContentTypeRule.js +1 -1
  83. package/dist/src/rules/http/HttpContentTypeRule.js.map +1 -1
  84. package/dist/src/rules/http/HttpTimeoutRule.d.ts.map +1 -1
  85. package/dist/src/rules/http/HttpTimeoutRule.js +1 -1
  86. package/dist/src/rules/http/HttpTimeoutRule.js.map +1 -1
  87. package/dist/src/rules/http/HttpUserAgentRule.d.ts.map +1 -1
  88. package/dist/src/rules/http/HttpUserAgentRule.js +1 -1
  89. package/dist/src/rules/http/HttpUserAgentRule.js.map +1 -1
  90. package/dist/src/rules/index.d.ts +1 -1
  91. package/dist/src/rules/index.d.ts.map +1 -1
  92. package/dist/src/rules/index.js +7 -5
  93. package/dist/src/rules/index.js.map +1 -1
  94. package/dist/src/rules/logging/LoggerCategoryRule.js +2 -2
  95. package/dist/src/rules/logging/LoggerCategoryRule.js.map +1 -1
  96. package/dist/src/rules/logging/LoggerInUntilSuccessfulRule.js +1 -1
  97. package/dist/src/rules/logging/LoggerInUntilSuccessfulRule.js.map +1 -1
  98. package/dist/src/rules/logging/LoggerPayloadRule.d.ts.map +1 -1
  99. package/dist/src/rules/logging/LoggerPayloadRule.js +3 -3
  100. package/dist/src/rules/logging/LoggerPayloadRule.js.map +1 -1
  101. package/dist/src/rules/naming/FlowCasingRule.d.ts +1 -0
  102. package/dist/src/rules/naming/FlowCasingRule.d.ts.map +1 -1
  103. package/dist/src/rules/naming/FlowCasingRule.js +7 -1
  104. package/dist/src/rules/naming/FlowCasingRule.js.map +1 -1
  105. package/dist/src/rules/naming/FlowNamingRule.d.ts.map +1 -1
  106. package/dist/src/rules/naming/FlowNamingRule.js +12 -1
  107. package/dist/src/rules/naming/FlowNamingRule.js.map +1 -1
  108. package/dist/src/rules/naming/VariableNamingRule.d.ts.map +1 -1
  109. package/dist/src/rules/naming/VariableNamingRule.js +3 -3
  110. package/dist/src/rules/naming/VariableNamingRule.js.map +1 -1
  111. package/dist/src/rules/performance/AsyncErrorHandlerRule.d.ts.map +1 -1
  112. package/dist/src/rules/performance/AsyncErrorHandlerRule.js +1 -1
  113. package/dist/src/rules/performance/AsyncErrorHandlerRule.js.map +1 -1
  114. package/dist/src/rules/performance/LargeChoiceBlockRule.d.ts.map +1 -1
  115. package/dist/src/rules/performance/LargeChoiceBlockRule.js +1 -1
  116. package/dist/src/rules/performance/LargeChoiceBlockRule.js.map +1 -1
  117. package/dist/src/rules/performance/ScatterGatherRoutesRule.d.ts.map +1 -1
  118. package/dist/src/rules/performance/ScatterGatherRoutesRule.js +1 -1
  119. package/dist/src/rules/performance/ScatterGatherRoutesRule.js.map +1 -1
  120. package/dist/src/rules/security/HardcodedCredentialsRule.d.ts.map +1 -1
  121. package/dist/src/rules/security/HardcodedCredentialsRule.js +6 -6
  122. package/dist/src/rules/security/HardcodedCredentialsRule.js.map +1 -1
  123. package/dist/src/rules/security/HardcodedHttpRule.d.ts.map +1 -1
  124. package/dist/src/rules/security/HardcodedHttpRule.js +2 -6
  125. package/dist/src/rules/security/HardcodedHttpRule.js.map +1 -1
  126. package/dist/src/rules/security/InsecureTlsRule.d.ts.map +1 -1
  127. package/dist/src/rules/security/InsecureTlsRule.js +2 -2
  128. package/dist/src/rules/security/InsecureTlsRule.js.map +1 -1
  129. package/dist/src/rules/standards/ChoiceAntiPatternRule.d.ts.map +1 -1
  130. package/dist/src/rules/standards/ChoiceAntiPatternRule.js +8 -2
  131. package/dist/src/rules/standards/ChoiceAntiPatternRule.js.map +1 -1
  132. package/dist/src/rules/standards/DeprecatedComponentRule.d.ts.map +1 -1
  133. package/dist/src/rules/standards/DeprecatedComponentRule.js +1 -1
  134. package/dist/src/rules/standards/DeprecatedComponentRule.js.map +1 -1
  135. package/dist/src/rules/standards/DwlStandardsRule.d.ts.map +1 -1
  136. package/dist/src/rules/standards/DwlStandardsRule.js +1 -1
  137. package/dist/src/rules/standards/DwlStandardsRule.js.map +1 -1
  138. package/dist/src/rules/structure/StructureRules.d.ts.map +1 -1
  139. package/dist/src/rules/structure/StructureRules.js +6 -9
  140. package/dist/src/rules/structure/StructureRules.js.map +1 -1
  141. package/dist/src/rules/yaml/YamlRules.d.ts.map +1 -1
  142. package/dist/src/rules/yaml/YamlRules.js +19 -12
  143. package/dist/src/rules/yaml/YamlRules.js.map +1 -1
  144. package/dist/src/types/Config.d.ts.map +1 -1
  145. package/dist/src/types/Config.js +1 -5
  146. package/dist/src/types/Config.js.map +1 -1
  147. package/docs/README.md +95 -0
  148. package/docs/best-practices/documentation-standards.md +82 -0
  149. package/docs/best-practices/folder-structure.md +61 -0
  150. package/docs/best-practices/mulesoft-best-practices.md +902 -0
  151. package/docs/best-practices/rules-catalog.md +736 -0
  152. package/docs/linter/architecture.md +294 -0
  153. package/docs/linter/extending.md +426 -0
  154. package/docs/linter/folder-structure.md +251 -0
  155. package/docs/linter/images/html-report-dashboard.png +0 -0
  156. package/docs/linter/naming-conventions.md +300 -0
  157. package/docs/linter/rule-engine.md +532 -0
  158. package/docs/mcp-design.md +80 -0
  159. package/package.json +6 -3
@@ -0,0 +1,532 @@
1
+ # Rule Engine Documentation
2
+
3
+ > **Version:** 1.0.0
4
+ > **Purpose:** Comprehensive guide to the mule-lint rule engine internals, extensibility, and best practices.
5
+
6
+ ---
7
+
8
+ ## Overview
9
+
10
+ The Rule Engine is the heart of mule-lint. It orchestrates:
11
+
12
+ 1. **Discovery** - Finding XML files to analyze
13
+ 2. **Parsing** - Converting XML to queryable DOM
14
+ 3. **Validation** - Running rules against the DOM
15
+ 4. **Reporting** - Aggregating and formatting results
16
+
17
+ ---
18
+
19
+ ## Core Interfaces
20
+
21
+ ### Rule Interface
22
+
23
+ Every rule must implement this contract:
24
+
25
+ ```typescript
26
+ import { Document } from '@xmldom/xmldom';
27
+
28
+ export type Severity = 'error' | 'warning' | 'info';
29
+
30
+ export type RuleCategory =
31
+ | 'naming'
32
+ | 'error-handling'
33
+ | 'security'
34
+ | 'logging'
35
+ | 'standards'
36
+ | 'performance'
37
+ | 'documentation';
38
+
39
+ export interface Issue {
40
+ line: number;
41
+ column?: number;
42
+ message: string;
43
+ ruleId: string;
44
+ severity: Severity;
45
+ suggestion?: string; // Optional fix suggestion
46
+ codeSnippet?: string; // Relevant code context
47
+ }
48
+
49
+ export interface ValidationContext {
50
+ filePath: string; // Absolute path to file
51
+ relativePath: string; // Path relative to project root
52
+ projectRoot: string; // Project root directory
53
+ config: RuleConfig; // Rule-specific configuration
54
+ }
55
+
56
+ export interface Rule {
57
+ // Unique identifier (e.g., "MULE-001")
58
+ id: string;
59
+
60
+ // Human-readable name
61
+ name: string;
62
+
63
+ // Detailed description for documentation
64
+ description: string;
65
+
66
+ // Default severity (can be overridden by config)
67
+ severity: Severity;
68
+
69
+ // Category for grouping in reports
70
+ category: RuleCategory;
71
+
72
+ // Optional: documentation URL
73
+ docsUrl?: string;
74
+
75
+ // The validation function
76
+ validate(doc: Document, context: ValidationContext): Issue[];
77
+ }
78
+ ```
79
+
80
+ ### RuleConfig Interface
81
+
82
+ Per-rule configuration:
83
+
84
+ ```typescript
85
+ export interface RuleConfig {
86
+ enabled: boolean;
87
+ severity?: Severity; // Override default
88
+ options?: Record<string, unknown>; // Rule-specific options
89
+ }
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Base Rule Class
95
+
96
+ All rules should extend `BaseRule` for common utilities:
97
+
98
+ ```typescript
99
+ import { Document, Node } from '@xmldom/xmldom';
100
+ import { Rule, Issue, Severity, RuleCategory, ValidationContext } from '@types';
101
+ import { XPathHelper } from '@core/XPathHelper';
102
+
103
+ export abstract class BaseRule implements Rule {
104
+ abstract id: string;
105
+ abstract name: string;
106
+ abstract description: string;
107
+ abstract severity: Severity;
108
+ abstract category: RuleCategory;
109
+
110
+ docsUrl?: string;
111
+
112
+ protected xpath = XPathHelper.getInstance();
113
+
114
+ abstract validate(doc: Document, context: ValidationContext): Issue[];
115
+
116
+ // --- Utility Methods ---
117
+
118
+ /**
119
+ * Execute XPath and return matching nodes
120
+ */
121
+ protected select(expression: string, doc: Document): Node[] {
122
+ return this.xpath.select(expression, doc);
123
+ }
124
+
125
+ /**
126
+ * Create an issue with consistent formatting
127
+ */
128
+ protected createIssue(
129
+ node: Node,
130
+ message: string,
131
+ options?: {
132
+ suggestion?: string;
133
+ severity?: Severity;
134
+ }
135
+ ): Issue {
136
+ return {
137
+ line: this.getLineNumber(node),
138
+ column: this.getColumnNumber(node),
139
+ message,
140
+ ruleId: this.id,
141
+ severity: options?.severity ?? this.severity,
142
+ suggestion: options?.suggestion,
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Get line number from node (xmldom stores this)
148
+ */
149
+ protected getLineNumber(node: Node): number {
150
+ // xmldom stores line info in columnNumber/lineNumber
151
+ return (node as any).lineNumber ?? 1;
152
+ }
153
+
154
+ /**
155
+ * Get column number from node
156
+ */
157
+ protected getColumnNumber(node: Node): number | undefined {
158
+ return (node as any).columnNumber;
159
+ }
160
+
161
+ /**
162
+ * Check if a node has a specific attribute
163
+ */
164
+ protected hasAttribute(node: Node, attrName: string): boolean {
165
+ const element = node as Element;
166
+ return element.hasAttribute?.(attrName) ?? false;
167
+ }
168
+
169
+ /**
170
+ * Get attribute value from node
171
+ */
172
+ protected getAttribute(node: Node, attrName: string): string | null {
173
+ const element = node as Element;
174
+ return element.getAttribute?.(attrName) ?? null;
175
+ }
176
+ }
177
+ ```
178
+
179
+ ---
180
+
181
+ ## XPath Reference for MuleSoft
182
+
183
+ ### Namespace Map
184
+
185
+ ```typescript
186
+ const MULE_NAMESPACES = {
187
+ 'mule': 'http://www.mulesoft.org/schema/mule/core',
188
+ 'http': 'http://www.mulesoft.org/schema/mule/http',
189
+ 'https': 'http://www.mulesoft.org/schema/mule/https',
190
+ 'ee': 'http://www.mulesoft.org/schema/mule/ee/core',
191
+ 'doc': 'http://www.mulesoft.org/schema/mule/documentation',
192
+ 'tls': 'http://www.mulesoft.org/schema/mule/tls',
193
+ 'db': 'http://www.mulesoft.org/schema/mule/db',
194
+ 'file': 'http://www.mulesoft.org/schema/mule/file',
195
+ 'sftp': 'http://www.mulesoft.org/schema/mule/sftp',
196
+ 'vm': 'http://www.mulesoft.org/schema/mule/vm',
197
+ 'jms': 'http://www.mulesoft.org/schema/mule/jms',
198
+ 'apikit': 'http://www.mulesoft.org/schema/mule/mule-apikit',
199
+ 'api-gateway': 'http://www.mulesoft.org/schema/mule/api-gateway',
200
+ 'secure-properties': 'http://www.mulesoft.org/schema/mule/secure-properties',
201
+ 'os': 'http://www.mulesoft.org/schema/mule/os',
202
+ 'batch': 'http://www.mulesoft.org/schema/mule/batch',
203
+ };
204
+ ```
205
+
206
+ ### Common XPath Patterns
207
+
208
+ | Purpose | XPath Expression |
209
+ |---------|------------------|
210
+ | All flows | `//mule:flow` |
211
+ | All sub-flows | `//mule:sub-flow` |
212
+ | Flow by name | `//mule:flow[@name='my-flow']` |
213
+ | All loggers | `//mule:logger` |
214
+ | Loggers without category | `//mule:logger[not(@category)]` |
215
+ | All error handlers | `//mule:error-handler` |
216
+ | All on-error blocks | `//mule:on-error-continue \| //mule:on-error-propagate` |
217
+ | All HTTP listeners | `//http:listener` |
218
+ | All HTTP requests | `//http:request` |
219
+ | Choice blocks | `//mule:choice` |
220
+ | DataWeave transforms | `//ee:transform` |
221
+ | Set-variable | `//mule:set-variable` |
222
+ | Flows without error handler | `//mule:flow[not(mule:error-handler)]` |
223
+ | Attributes starting with http | `//*[@*[starts-with(., 'http:')]]` |
224
+
225
+ ---
226
+
227
+ ## Rule Implementation Examples
228
+
229
+ ### Example 1: Flow Naming Rule
230
+
231
+ ```typescript
232
+ import { Document, Node } from '@xmldom/xmldom';
233
+ import { BaseRule, Issue, ValidationContext } from '@types';
234
+
235
+ export class FlowNamingRule extends BaseRule {
236
+ id = 'MULE-002';
237
+ name = 'Flow Naming Convention';
238
+ description = 'Flows must end with "-flow", sub-flows with "-subflow"';
239
+ severity = 'warning' as const;
240
+ category = 'naming' as const;
241
+
242
+ validate(doc: Document, context: ValidationContext): Issue[] {
243
+ const issues: Issue[] = [];
244
+
245
+ // Check flows
246
+ const flows = this.select('//mule:flow', doc);
247
+ for (const flow of flows) {
248
+ const name = this.getAttribute(flow, 'name');
249
+ if (name && !name.endsWith('-flow')) {
250
+ issues.push(this.createIssue(
251
+ flow,
252
+ `Flow "${name}" should end with "-flow"`,
253
+ { suggestion: `Rename to "${name}-flow"` }
254
+ ));
255
+ }
256
+ }
257
+
258
+ // Check sub-flows
259
+ const subflows = this.select('//mule:sub-flow', doc);
260
+ for (const subflow of subflows) {
261
+ const name = this.getAttribute(subflow, 'name');
262
+ if (name && !name.endsWith('-subflow')) {
263
+ issues.push(this.createIssue(
264
+ subflow,
265
+ `Sub-flow "${name}" should end with "-subflow"`,
266
+ { suggestion: `Rename to "${name}-subflow"` }
267
+ ));
268
+ }
269
+ }
270
+
271
+ return issues;
272
+ }
273
+ }
274
+ ```
275
+
276
+ ### Example 2: Logger Category Rule
277
+
278
+ ```typescript
279
+ import { Document, Node } from '@xmldom/xmldom';
280
+ import { BaseRule, Issue, ValidationContext } from '@types';
281
+
282
+ export class LoggerCategoryRule extends BaseRule {
283
+ id = 'MULE-006';
284
+ name = 'Logger Category Required';
285
+ description = 'All loggers must have a category attribute for proper log filtering';
286
+ severity = 'warning' as const;
287
+ category = 'logging' as const;
288
+
289
+ validate(doc: Document, context: ValidationContext): Issue[] {
290
+ const issues: Issue[] = [];
291
+
292
+ const loggers = this.select('//mule:logger[not(@category)]', doc);
293
+
294
+ for (const logger of loggers) {
295
+ const message = this.getAttribute(logger, 'message') || 'unknown';
296
+ issues.push(this.createIssue(
297
+ logger,
298
+ `Logger is missing 'category' attribute`,
299
+ {
300
+ suggestion: `Add category="com.myorg.${context.relativePath.replace(/\//g, '.')}"`
301
+ }
302
+ ));
303
+ }
304
+
305
+ return issues;
306
+ }
307
+ }
308
+ ```
309
+
310
+ ### Example 3: Hardcoded HTTP Rule (Security)
311
+
312
+ ```typescript
313
+ import { Document, Node, Element } from '@xmldom/xmldom';
314
+ import { BaseRule, Issue, ValidationContext } from '@types';
315
+
316
+ export class HardcodedHttpRule extends BaseRule {
317
+ id = 'MULE-004';
318
+ name = 'Hardcoded HTTP URLs';
319
+ description = 'HTTP/HTTPS URLs should use properties, not hardcoded values';
320
+ severity = 'error' as const;
321
+ category = 'security' as const;
322
+
323
+ private readonly URL_PATTERN = /^https?:\/\//i;
324
+ private readonly ALLOWED_PATTERNS = [
325
+ /\$\{[^}]+\}/, // Property placeholders ${...}
326
+ /\#\[[^\]]+\]/, // DataWeave expressions #[...]
327
+ ];
328
+
329
+ validate(doc: Document, context: ValidationContext): Issue[] {
330
+ const issues: Issue[] = [];
331
+
332
+ // Find all elements with attributes containing http:// or https://
333
+ const allElements = doc.getElementsByTagName('*');
334
+
335
+ for (let i = 0; i < allElements.length; i++) {
336
+ const element = allElements[i];
337
+ const attrs = element.attributes;
338
+
339
+ for (let j = 0; j < attrs.length; j++) {
340
+ const attr = attrs[j];
341
+ const value = attr.value;
342
+
343
+ if (this.URL_PATTERN.test(value) && !this.isAllowedPattern(value)) {
344
+ issues.push(this.createIssue(
345
+ element,
346
+ `Hardcoded URL "${this.truncate(value)}" found in attribute "${attr.name}"`,
347
+ {
348
+ suggestion: 'Use property placeholder: ${http.url}'
349
+ }
350
+ ));
351
+ }
352
+ }
353
+ }
354
+
355
+ return issues;
356
+ }
357
+
358
+ private isAllowedPattern(value: string): boolean {
359
+ return this.ALLOWED_PATTERNS.some(pattern => pattern.test(value));
360
+ }
361
+
362
+ private truncate(value: string, maxLen = 50): string {
363
+ return value.length > maxLen ? value.substring(0, maxLen) + '...' : value;
364
+ }
365
+ }
366
+ ```
367
+
368
+ ---
369
+
370
+ ## Rule Registration
371
+
372
+ Rules are registered in `src/rules/index.ts`:
373
+
374
+ ```typescript
375
+ import { Rule } from '@types';
376
+
377
+ // Import all rules
378
+ import { FlowNamingRule } from './naming/FlowNamingRule';
379
+ import { GlobalErrorHandlerRule } from './error-handling/GlobalErrorHandlerRule';
380
+ import { MissingErrorHandlerRule } from './error-handling/MissingErrorHandlerRule';
381
+ import { HardcodedHttpRule } from './security/HardcodedHttpRule';
382
+ import { HttpStatusRule } from './error-handling/HttpStatusRule';
383
+ import { LoggerCategoryRule } from './logging/LoggerCategoryRule';
384
+ import { CorrelationIdRule } from './error-handling/CorrelationIdRule';
385
+ import { ChoiceAntiPatternRule } from './standards/ChoiceAntiPatternRule';
386
+ import { GenericErrorRule } from './error-handling/GenericErrorRule';
387
+ import { DwlStandardsRule } from './standards/DwlStandardsRule';
388
+
389
+ // Rule registry
390
+ export const RULES: Rule[] = [
391
+ new FlowNamingRule(),
392
+ new GlobalErrorHandlerRule(),
393
+ new MissingErrorHandlerRule(),
394
+ new HardcodedHttpRule(),
395
+ new HttpStatusRule(),
396
+ new LoggerCategoryRule(),
397
+ new CorrelationIdRule(),
398
+ new ChoiceAntiPatternRule(),
399
+ new GenericErrorRule(),
400
+ new DwlStandardsRule(),
401
+ ];
402
+
403
+ // Export for external use
404
+ export * from './naming/FlowNamingRule';
405
+ export * from './error-handling/GlobalErrorHandlerRule';
406
+ // ... etc
407
+ ```
408
+
409
+ ---
410
+
411
+ ## Engine Processing Flow
412
+
413
+ ```mermaid
414
+ flowchart TD
415
+ START[Start Scan] --> CONFIG[Load Configuration]
416
+ CONFIG --> DISCOVER[Discover XML Files]
417
+ DISCOVER --> PARSE[Parse XML to DOM]
418
+ PARSE --> CHECK{Parse Error?}
419
+ CHECK -- Yes --> CRITICAL[Report Critical Error]
420
+ CHECK -- No --> RULES[Run All Rules]
421
+ RULES --> COLLECT[Collect Issues]
422
+ COLLECT --> AGGREGATE[Aggregate Results]
423
+ AGGREGATE --> FORMAT[Format Output]
424
+ FORMAT --> EXIT[Set Exit Code]
425
+ CRITICAL --> COLLECT
426
+ ```
427
+
428
+ ---
429
+
430
+ ## Configuration
431
+
432
+ ### `.mulelintrc.json` Schema
433
+
434
+ ```json
435
+ {
436
+ "$schema": "./node_modules/mule-lint/config-schema.json",
437
+ "rules": {
438
+ "MULE-001": { "enabled": true, "severity": "error" },
439
+ "MULE-002": { "enabled": true, "severity": "warning" },
440
+ "MULE-003": {
441
+ "enabled": true,
442
+ "options": {
443
+ "excludePatterns": ["*-api-main"]
444
+ }
445
+ }
446
+ },
447
+ "include": ["src/main/mule/**/*.xml"],
448
+ "exclude": ["**/test/**", "**/*.munit.xml"],
449
+ "formatters": {
450
+ "default": "table",
451
+ "ci": "sarif"
452
+ }
453
+ }
454
+ ```
455
+
456
+ ### Rule-Specific Options
457
+
458
+ Some rules support additional options:
459
+
460
+ | Rule | Option | Type | Default | Description |
461
+ |------|--------|------|---------|-------------|
462
+ | MULE-002 | `flowSuffix` | string | `-flow` | Expected flow name suffix |
463
+ | MULE-002 | `subflowSuffix` | string | `-subflow` | Expected sub-flow suffix |
464
+ | MULE-003 | `excludePatterns` | string[] | `[]` | Flow name patterns to exclude |
465
+ | MULE-006 | `requirePrefix` | string | `null` | Required category prefix |
466
+
467
+ ---
468
+
469
+ ## Testing Rules
470
+
471
+ ### Test Structure
472
+
473
+ ```typescript
474
+ import { FlowNamingRule } from '../rules/naming/FlowNamingRule';
475
+ import { parseXml } from '../core/XmlParser';
476
+
477
+ describe('FlowNamingRule', () => {
478
+ const rule = new FlowNamingRule();
479
+
480
+ it('should pass for correctly named flow', () => {
481
+ const xml = `
482
+ <mule xmlns="http://www.mulesoft.org/schema/mule/core">
483
+ <flow name="my-process-flow">
484
+ <logger message="test"/>
485
+ </flow>
486
+ </mule>
487
+ `;
488
+ const doc = parseXml(xml);
489
+ const issues = rule.validate(doc, mockContext);
490
+
491
+ expect(issues).toHaveLength(0);
492
+ });
493
+
494
+ it('should fail for incorrectly named flow', () => {
495
+ const xml = `
496
+ <mule xmlns="http://www.mulesoft.org/schema/mule/core">
497
+ <flow name="myProcess">
498
+ <logger message="test"/>
499
+ </flow>
500
+ </mule>
501
+ `;
502
+ const doc = parseXml(xml);
503
+ const issues = rule.validate(doc, mockContext);
504
+
505
+ expect(issues).toHaveLength(1);
506
+ expect(issues[0].ruleId).toBe('MULE-002');
507
+ expect(issues[0].message).toContain('myProcess');
508
+ });
509
+ });
510
+ ```
511
+
512
+ ---
513
+
514
+ ## Performance Specifications
515
+
516
+ | Metric | Target |
517
+ |--------|--------|
518
+ | Files per second | > 100 |
519
+ | Memory per file | < 10MB |
520
+ | Rule execution | < 50ms per rule |
521
+ | Total for 100 files | < 5 seconds |
522
+
523
+ ---
524
+
525
+ ## Exit Codes
526
+
527
+ | Code | Meaning |
528
+ |------|---------|
529
+ | 0 | No errors or warnings |
530
+ | 1 | At least one error found |
531
+ | 2 | Configuration error |
532
+ | 3 | Critical error (parse failure) |
@@ -0,0 +1,80 @@
1
+ # Mule Lint MCP Server Design
2
+
3
+ ## Executive Summary
4
+ This document outlines the strategy for exposing `mule-lint` capabilities via the Model Context Protocol (MCP). By wrapping the linter in an MCP server, we enable AI agents (like Claude, IDE assistants, etc.) to autonomously discover linting rules, validate MuleSoft projects, and retrieve detailed rule documentation without needing to shell out or parse CLI text output.
5
+
6
+ ## Architecture Decision: Monorepo vs. Separate Repo
7
+ **Recommendation: Same Repo (Monorepo)**
8
+
9
+ We should implement the MCP server directly in the `mule-lint` repository, likely under `src/mcp` or as a separate package if this was a workspace.
10
+ * **Pros**: Direct access to `src/core`, `src/rules`, and types without publishing/installing packages. Easier to keep rule definitions and agent-exposed descriptions in sync.
11
+ * **Cons**: Adds a dependency on `@modelcontextprotocol/sdk` to the main repo (or requires a build split).
12
+
13
+ ## Features & Capabilities
14
+
15
+ ### 1. Tools
16
+ Tools allow the agent to perform actions.
17
+
18
+ | Tool Name | Arguments | Description |
19
+ | :--- | :--- | :--- |
20
+ | `run_lint_analysis` | `projectPath` (string) | Runs the scanning engine on a specified directory. Returns a JSON summary of errors, warnings, and code references. |
21
+ | `validate_snippet` | `code` (string), `type` (xml/dwl) | quickly validates a small chunk of code without a full project structure. Useful for agents generating code on the fly. |
22
+ | `get_rule_details` | `ruleId` (string) | Returns the full documentation, examples, and rationale for a specific rule (e.g., `MULE-001`). |
23
+
24
+ ### 2. Resources
25
+ Resources allow the agent to read context.
26
+
27
+ | Resource URI | Description |
28
+ | :--- | :--- |
29
+ | `mule-lint://rules` | A JSON list of all registered rules, their categories, and severity levels. |
30
+ | `mule-lint://config/schema` | The JSON schema for `.mule-lintrc` to help agents author valid configurations. |
31
+ | `mule-lint://docs/{slug}` | Access internal documentation (e.g., `best-practices`, `architecture`, `naming`). |
32
+
33
+ ### 3. Prompts
34
+ Pre-defined prompts to help users interacting with the agent.
35
+
36
+ | Prompt Name | Description |
37
+ | :--- | :--- |
38
+ | `analyze_current_project` | "Run a comprehensive analysis on this project and summarize the top 3 critical issues." |
39
+ | `explain_violation` | "Here is an error I found: {{ErrorString}}. Explain why this is bad and how to fix it using `get_rule_details`." |
40
+
41
+ ## Implementation Phases
42
+
43
+ ### Phase 1: Foundation (The "Reader" Agent)
44
+ > [!NOTE]
45
+ > **Status**: Completed. Available on NPM as `@sfdxy/mule-lint`.
46
+
47
+ *Goal: Allow an agent to see what rules exist and run a scan.*
48
+ - [x] Install `@modelcontextprotocol/sdk`.
49
+ - [x] Create `McpServer` class in `src/mcp/index.ts`.
50
+ - [x] Implement `mule-lint://rules` resource.
51
+ - [x] Implement `run_lint_analysis` tool (wrapping `LintEngine`).
52
+ - [x] Add `stdio` transport for local running.
53
+
54
+ ### Phase 2: Interactive Context (The "Helper" Agent)
55
+ > [!NOTE]
56
+ > **Status**: Completed.
57
+
58
+ *Goal: Allow the agent to understand *why* things failed.*
59
+ - [x] Implement `get_rule_details` tool.
60
+ - [x] Expose internal documentation of rules via MCP.
61
+ - [x] Add `validate_snippet` for real-time code generation checks.
62
+
63
+ ### Phase 3: Remediation (The "Fixer" Agent)
64
+ > [!NOTE]
65
+ > **Status**: Partially Completed. `apply_fix` deferred. Enhanced reporting added.
66
+
67
+ *Goal: Allow the agent to automatically fix issues.*
68
+ - [ ] Implement `apply_fix` tool (Deferred: requires AST write support).
69
+ - [x] Enhanced error reporting with precise range/location data (Added column/suggestion).
70
+
71
+ ## Libraries & Dependencies
72
+ * **Core**: `@modelcontextprotocol/sdk`
73
+ * **Transport**: Stdio (standard input/output) for local CLI integration.
74
+ * **Runtime**: Node.js (uses existing project runtime).
75
+
76
+ ## Agent Workflow Example
77
+ 1. **Discovery**: Agent connects and reads `mule-lint://rules` to know what it is looking for.
78
+ 2. **Action**: User asks "Check my code". Agent calls `run_lint_analysis(cwd)`.
79
+ 3. **Context**: Agent sees error `DW-004`. Agent calls `get_rule_details("DW-004")` to read the "Java 17 DataWeave" docs.
80
+ 4. **Result**: Agent explains the error to the user with specific context from the official rule definitions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sfdxy/mule-lint",
3
- "version": "1.7.1",
3
+ "version": "1.8.2",
4
4
  "description": "Static analysis tool for MuleSoft applications - supports humans, AI agents, and CI/CD pipelines",
5
5
  "author": "Avinava",
6
6
  "license": "MIT",
@@ -14,14 +14,16 @@
14
14
  "cli"
15
15
  ],
16
16
  "bin": {
17
- "mule-lint": "./dist/bin/mule-lint.js"
17
+ "mule-lint": "./dist/bin/mule-lint.js",
18
+ "mule-lint-mcp": "./dist/bin/mule-lint-mcp.js"
18
19
  },
19
20
  "main": "./dist/index.js",
20
21
  "types": "./dist/index.d.ts",
21
22
  "files": [
22
23
  "dist",
23
24
  "README.md",
24
- "LICENSE"
25
+ "LICENSE",
26
+ "docs"
25
27
  ],
26
28
  "scripts": {
27
29
  "build": "tsc",
@@ -37,6 +39,7 @@
37
39
  "release": "git tag v$(node -p 'require(\"./package.json\").version') && git push origin v$(node -p 'require(\"./package.json\").version')"
38
40
  },
39
41
  "dependencies": {
42
+ "@modelcontextprotocol/sdk": "^1.25.2",
40
43
  "@xmldom/xmldom": "^0.8.10",
41
44
  "chalk": "^4.1.2",
42
45
  "commander": "^11.1.0",