easyeda-mcp-pro 0.3.2 → 0.5.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 (173) hide show
  1. package/README.md +369 -52
  2. package/dist/bom-quality/adapter.d.ts +90 -0
  3. package/dist/bom-quality/adapter.js +295 -0
  4. package/dist/bom-quality/adapter.js.map +1 -0
  5. package/dist/bom-quality/index.d.ts +10 -0
  6. package/dist/bom-quality/index.js +9 -0
  7. package/dist/bom-quality/index.js.map +1 -0
  8. package/dist/bom-quality/quality.d.ts +28 -0
  9. package/dist/bom-quality/quality.js +179 -0
  10. package/dist/bom-quality/quality.js.map +1 -0
  11. package/dist/bom-quality/types.d.ts +94 -0
  12. package/dist/bom-quality/types.js +12 -0
  13. package/dist/bom-quality/types.js.map +1 -0
  14. package/dist/bridge/manager.d.ts +41 -0
  15. package/dist/bridge/manager.js +334 -47
  16. package/dist/bridge/manager.js.map +1 -1
  17. package/dist/bridge/protocol.d.ts +48 -1
  18. package/dist/bridge/protocol.js +43 -1
  19. package/dist/bridge/protocol.js.map +1 -1
  20. package/dist/bridge/types.d.ts +6 -0
  21. package/dist/bridge/types.js +6 -0
  22. package/dist/bridge/types.js.map +1 -1
  23. package/dist/catalog/errors.d.ts +49 -0
  24. package/dist/catalog/errors.js +60 -0
  25. package/dist/catalog/errors.js.map +1 -0
  26. package/dist/catalog/index.d.ts +15 -0
  27. package/dist/catalog/index.js +17 -0
  28. package/dist/catalog/index.js.map +1 -0
  29. package/dist/catalog/schema.d.ts +268 -0
  30. package/dist/catalog/schema.js +245 -0
  31. package/dist/catalog/schema.js.map +1 -0
  32. package/dist/catalog/starter.d.ts +159 -0
  33. package/dist/catalog/starter.js +579 -0
  34. package/dist/catalog/starter.js.map +1 -0
  35. package/dist/catalog/validation.d.ts +48 -0
  36. package/dist/catalog/validation.js +263 -0
  37. package/dist/catalog/validation.js.map +1 -0
  38. package/dist/circuit/circuit-ir.d.ts +460 -0
  39. package/dist/circuit/circuit-ir.js +292 -0
  40. package/dist/circuit/circuit-ir.js.map +1 -0
  41. package/dist/circuit/compiler.d.ts +55 -0
  42. package/dist/circuit/compiler.js +197 -0
  43. package/dist/circuit/compiler.js.map +1 -0
  44. package/dist/circuit/design-intent.d.ts +94 -0
  45. package/dist/circuit/design-intent.js +118 -0
  46. package/dist/circuit/design-intent.js.map +1 -0
  47. package/dist/circuit/errors.d.ts +50 -0
  48. package/dist/circuit/errors.js +61 -0
  49. package/dist/circuit/errors.js.map +1 -0
  50. package/dist/circuit/index.d.ts +17 -0
  51. package/dist/circuit/index.js +18 -0
  52. package/dist/circuit/index.js.map +1 -0
  53. package/dist/circuit/types.d.ts +186 -0
  54. package/dist/circuit/types.js +123 -0
  55. package/dist/circuit/types.js.map +1 -0
  56. package/dist/cli/auto-setup.d.ts +5 -0
  57. package/dist/cli/auto-setup.js +355 -0
  58. package/dist/cli/auto-setup.js.map +1 -0
  59. package/dist/cli/client-definitions.d.ts +34 -0
  60. package/dist/cli/client-definitions.js +194 -0
  61. package/dist/cli/client-definitions.js.map +1 -0
  62. package/dist/cli/local-setup.d.ts +5 -1
  63. package/dist/cli/local-setup.js +40 -6
  64. package/dist/cli/local-setup.js.map +1 -1
  65. package/dist/config/env.d.ts +6 -1
  66. package/dist/config/env.js +93 -5
  67. package/dist/config/env.js.map +1 -1
  68. package/dist/config/profiles.js +4 -4
  69. package/dist/config/version.d.ts +1 -1
  70. package/dist/config/version.js +1 -1
  71. package/dist/export-manifest/errors.d.ts +55 -0
  72. package/dist/export-manifest/errors.js +69 -0
  73. package/dist/export-manifest/errors.js.map +1 -0
  74. package/dist/export-manifest/index.d.ts +10 -0
  75. package/dist/export-manifest/index.js +9 -0
  76. package/dist/export-manifest/index.js.map +1 -0
  77. package/dist/export-manifest/types.d.ts +170 -0
  78. package/dist/export-manifest/types.js +36 -0
  79. package/dist/export-manifest/types.js.map +1 -0
  80. package/dist/export-manifest/validation.d.ts +39 -0
  81. package/dist/export-manifest/validation.js +298 -0
  82. package/dist/export-manifest/validation.js.map +1 -0
  83. package/dist/index.js +20 -0
  84. package/dist/index.js.map +1 -1
  85. package/dist/net-validation/errors.d.ts +89 -0
  86. package/dist/net-validation/errors.js +69 -0
  87. package/dist/net-validation/errors.js.map +1 -0
  88. package/dist/net-validation/fixtures.d.ts +38 -0
  89. package/dist/net-validation/fixtures.js +215 -0
  90. package/dist/net-validation/fixtures.js.map +1 -0
  91. package/dist/net-validation/index.d.ts +10 -0
  92. package/dist/net-validation/index.js +9 -0
  93. package/dist/net-validation/index.js.map +1 -0
  94. package/dist/net-validation/schema.d.ts +118 -0
  95. package/dist/net-validation/schema.js +140 -0
  96. package/dist/net-validation/schema.js.map +1 -0
  97. package/dist/net-validation/validation.d.ts +45 -0
  98. package/dist/net-validation/validation.js +369 -0
  99. package/dist/net-validation/validation.js.map +1 -0
  100. package/dist/pcb-constraints/errors.d.ts +39 -0
  101. package/dist/pcb-constraints/errors.js +133 -0
  102. package/dist/pcb-constraints/errors.js.map +1 -0
  103. package/dist/pcb-constraints/index.d.ts +9 -0
  104. package/dist/pcb-constraints/index.js +9 -0
  105. package/dist/pcb-constraints/index.js.map +1 -0
  106. package/dist/pcb-constraints/types.d.ts +102 -0
  107. package/dist/pcb-constraints/types.js +7 -0
  108. package/dist/pcb-constraints/types.js.map +1 -0
  109. package/dist/pcb-constraints/validation.d.ts +70 -0
  110. package/dist/pcb-constraints/validation.js +309 -0
  111. package/dist/pcb-constraints/validation.js.map +1 -0
  112. package/dist/server/factory.js +2 -0
  113. package/dist/server/factory.js.map +1 -1
  114. package/dist/server/transports/http.d.ts +20 -1
  115. package/dist/server/transports/http.js +156 -34
  116. package/dist/server/transports/http.js.map +1 -1
  117. package/dist/storage/database.d.ts +25 -0
  118. package/dist/storage/database.js +126 -51
  119. package/dist/storage/database.js.map +1 -1
  120. package/dist/tools/{L0_diagnostics.d.ts → L0_diagnostics_api.d.ts} +2 -2
  121. package/dist/tools/L0_diagnostics_api.js +152 -0
  122. package/dist/tools/L0_diagnostics_api.js.map +1 -0
  123. package/dist/tools/L0_diagnostics_core.d.ts +6 -0
  124. package/dist/tools/{L0_diagnostics.js → L0_diagnostics_core.js} +50 -124
  125. package/dist/tools/L0_diagnostics_core.js.map +1 -0
  126. package/dist/tools/L1_board.js +20 -4
  127. package/dist/tools/L1_board.js.map +1 -1
  128. package/dist/tools/{L1_bom.d.ts → L1_bom_core.d.ts} +2 -2
  129. package/dist/tools/{L1_bom.js → L1_bom_core.js} +21 -101
  130. package/dist/tools/L1_bom_core.js.map +1 -0
  131. package/dist/tools/{L1_schematic.d.ts → L1_bom_sourcing.d.ts} +2 -2
  132. package/dist/tools/L1_bom_sourcing.js +265 -0
  133. package/dist/tools/L1_bom_sourcing.js.map +1 -0
  134. package/dist/tools/L1_drc_erc.js +15 -3
  135. package/dist/tools/L1_drc_erc.js.map +1 -1
  136. package/dist/tools/L1_export.js +20 -4
  137. package/dist/tools/L1_export.js.map +1 -1
  138. package/dist/tools/L1_pcb_constraints.d.ts +6 -0
  139. package/dist/tools/L1_pcb_constraints.js +234 -0
  140. package/dist/tools/L1_pcb_constraints.js.map +1 -0
  141. package/dist/tools/L1_pcb_write.js +26 -12
  142. package/dist/tools/L1_pcb_write.js.map +1 -1
  143. package/dist/tools/L1_schematic_read.d.ts +6 -0
  144. package/dist/tools/{L1_schematic.js → L1_schematic_read.js} +125 -190
  145. package/dist/tools/L1_schematic_read.js.map +1 -0
  146. package/dist/tools/L1_schematic_write.d.ts +6 -0
  147. package/dist/tools/L1_schematic_write.js +516 -0
  148. package/dist/tools/L1_schematic_write.js.map +1 -0
  149. package/dist/tools/register.js +15 -7
  150. package/dist/tools/register.js.map +1 -1
  151. package/dist/tools/registry.d.ts +41 -7
  152. package/dist/tools/registry.js +111 -12
  153. package/dist/tools/registry.js.map +1 -1
  154. package/dist/tools/types.d.ts +16 -0
  155. package/dist/utils/logger.d.ts +1 -1
  156. package/dist/utils/logger.js +1 -1
  157. package/dist/utils/logger.js.map +1 -1
  158. package/dist/vendors/base-http-client.d.ts +48 -0
  159. package/dist/vendors/base-http-client.js +75 -0
  160. package/dist/vendors/base-http-client.js.map +1 -0
  161. package/dist/vendors/digikey/client.js +10 -19
  162. package/dist/vendors/digikey/client.js.map +1 -1
  163. package/dist/vendors/jlcpcb/client.js +41 -70
  164. package/dist/vendors/jlcpcb/client.js.map +1 -1
  165. package/dist/vendors/lcsc/client.js +5 -18
  166. package/dist/vendors/lcsc/client.js.map +1 -1
  167. package/dist/vendors/mouser/client.js +4 -12
  168. package/dist/vendors/mouser/client.js.map +1 -1
  169. package/easyeda-bridge-extension.eext +0 -0
  170. package/package.json +41 -26
  171. package/dist/tools/L0_diagnostics.js.map +0 -1
  172. package/dist/tools/L1_bom.js.map +0 -1
  173. package/dist/tools/L1_schematic.js.map +0 -1
@@ -0,0 +1,309 @@
1
+ /**
2
+ * PCB constraint validation rules.
3
+ *
4
+ * Implements constraint checks that operate on {@link PcbConstraintInput}
5
+ * and produce structured {@link PcbConstraintIssue}s.
6
+ *
7
+ * Rules:
8
+ * 1. Missing board outline (error)
9
+ * 2. Board outline too small (warning)
10
+ * 3. Missing layer stackup (warning)
11
+ * 4. Layer count mismatch between intent and board (error)
12
+ * 5. Missing mounting holes (warning)
13
+ * 6. Missing net classes (warning)
14
+ * 7. Invalid clearance / creepage (error)
15
+ * 8. Keepout area violations (error)
16
+ * 9. Missing placement zones (warning)
17
+ * 10. Missing manufacturing constraints (warning)
18
+ * 11. Fiducials recommended for SMT (warning)
19
+ * 12. High-voltage creepage/clearance (error)
20
+ *
21
+ * @module
22
+ */
23
+ import { pcbError, pcbWarning } from './errors.js';
24
+ // ── Rule: missing board outline ────────────────────────────────────────────
25
+ function checkMissingOutline(input) {
26
+ const issues = [];
27
+ if (!input.hasOutline) {
28
+ issues.push(pcbError('PCB_MISSING_OUTLINE', 'Board outline is not defined', {
29
+ remediationHint: 'Define a board outline with at least 3 polygon points in the PCB intent or board dimensions',
30
+ }));
31
+ }
32
+ return issues;
33
+ }
34
+ // ── Rule: board outline too small ──────────────────────────────────────────
35
+ const MIN_BOARD_DIMENSION_MM = 5;
36
+ function checkOutlineTooSmall(input) {
37
+ const issues = [];
38
+ if (input.widthMm && input.widthMm < MIN_BOARD_DIMENSION_MM) {
39
+ issues.push(pcbWarning('PCB_OUTLINE_TOO_SMALL', `Board width (${input.widthMm}mm) is below recommended minimum of ${MIN_BOARD_DIMENSION_MM}mm`, {
40
+ remediationHint: 'Verify the board dimensions are correct. Very small boards may be difficult to manufacture.',
41
+ }));
42
+ }
43
+ if (input.heightMm && input.heightMm < MIN_BOARD_DIMENSION_MM) {
44
+ issues.push(pcbWarning('PCB_OUTLINE_TOO_SMALL', `Board height (${input.heightMm}mm) is below recommended minimum of ${MIN_BOARD_DIMENSION_MM}mm`, {
45
+ remediationHint: 'Verify the board dimensions are correct. Very small boards may be difficult to manufacture.',
46
+ }));
47
+ }
48
+ return issues;
49
+ }
50
+ // ── Rule: missing layer stackup ────────────────────────────────────────────
51
+ function checkMissingStackup(input) {
52
+ const issues = [];
53
+ if (!input.hasLayerStack) {
54
+ issues.push(pcbWarning('PCB_MISSING_STACKUP', 'Detailed layer stackup is not defined', {
55
+ remediationHint: 'Define a layer stackup with thickness, material, and copper weight for each layer to ensure manufacturability',
56
+ details: { layerCount: input.layerCount },
57
+ }));
58
+ }
59
+ return issues;
60
+ }
61
+ // ── Rule: layer count mismatch ─────────────────────────────────────────────
62
+ const MAX_LAYERS_WITHOUT_STACKUP = 2;
63
+ function checkLayerCountMismatch(input) {
64
+ const issues = [];
65
+ if (input.layerCount && input.layerCount > MAX_LAYERS_WITHOUT_STACKUP && !input.hasLayerStack) {
66
+ issues.push(pcbError('PCB_LAYER_COUNT_MISMATCH', `Board has ${input.layerCount} layers but no detailed stackup is defined`, {
67
+ remediationHint: 'For boards with more than 2 layers, a complete layer stackup is required for fabrication',
68
+ details: { layerCount: input.layerCount },
69
+ }));
70
+ }
71
+ return issues;
72
+ }
73
+ // ── Rule: missing mounting holes ───────────────────────────────────────────
74
+ function checkMissingMountingHoles(input) {
75
+ const issues = [];
76
+ if (input.mountingHoleCount !== undefined && input.mountingHoleCount === 0) {
77
+ issues.push(pcbWarning('PCB_MISSING_MOUNTING_HOLES', 'No mounting holes defined on the board', {
78
+ remediationHint: 'Add at least 4 mounting holes (one near each corner) for mechanical stability and assembly',
79
+ }));
80
+ }
81
+ return issues;
82
+ }
83
+ // ── Rule: missing net classes ──────────────────────────────────────────────
84
+ function checkMissingNetClasses(input) {
85
+ const issues = [];
86
+ if (!input.hasNetClasses) {
87
+ issues.push(pcbWarning('PCB_MISSING_NET_CLASSES', 'No net classes with routing rules are defined', {
88
+ remediationHint: 'Define net classes for power, signal, and high-speed nets to enforce trace width and clearance rules',
89
+ }));
90
+ }
91
+ return issues;
92
+ }
93
+ // ── Rule: invalid clearance ────────────────────────────────────────────────
94
+ function checkInvalidClearance(input) {
95
+ const issues = [];
96
+ if (input.hasNetClasses && !input.hasClearanceRules) {
97
+ issues.push(pcbWarning('PCB_INVALID_CLEARANCE', 'Net classes are defined but no clearance rules between them', {
98
+ remediationHint: 'Add clearance rules between each pair of net classes (e.g., power-to-signal, high-voltage-to-signal)',
99
+ }));
100
+ }
101
+ return issues;
102
+ }
103
+ // ── Rule: keepout violations ───────────────────────────────────────────────
104
+ function checkKeepoutAreas(input) {
105
+ const issues = [];
106
+ if (!input.hasKeepoutAreas && input.hasPlacementZones) {
107
+ issues.push(pcbWarning('PCB_KEEPOUT_VIOLATION', 'Placement zones are defined but no keepout/restricted areas', {
108
+ remediationHint: 'Consider adding keepout areas to prevent component or track placement in restricted zones',
109
+ }));
110
+ }
111
+ return issues;
112
+ }
113
+ // ── Rule: missing placement zones ──────────────────────────────────────────
114
+ function checkMissingPlacementZones(input) {
115
+ const issues = [];
116
+ if (!input.hasPlacementZones && input.layerCount && input.layerCount > 1) {
117
+ issues.push(pcbWarning('PCB_MISSING_PLACEMENT_ZONES', 'No placement zones defined for component grouping', {
118
+ remediationHint: 'Define placement zones (power, analog, digital, etc.) to guide component placement and improve signal integrity',
119
+ }));
120
+ }
121
+ return issues;
122
+ }
123
+ // ── Rule: missing manufacturing constraints ────────────────────────────────
124
+ function checkMissingManufacturingConstraints(input) {
125
+ const issues = [];
126
+ if (!input.manufacturingProcess) {
127
+ issues.push(pcbWarning('PCB_MISSING_MANUFACTURING_CONSTRAINTS', 'Manufacturing process (lead-free/lead-based) is not specified', {
128
+ remediationHint: 'Specify the manufacturing process to ensure correct solder mask, pad finish, and assembly requirements',
129
+ }));
130
+ }
131
+ if (!input.hasQuantity) {
132
+ issues.push(pcbWarning('PCB_MISSING_MANUFACTURING_CONSTRAINTS', 'Production quantity is not specified', {
133
+ remediationHint: 'Specify the target production quantity to select the appropriate manufacturing process',
134
+ }));
135
+ }
136
+ return issues;
137
+ }
138
+ // ── Rule: fiducials recommended ────────────────────────────────────────────
139
+ function checkFiducials(input) {
140
+ const issues = [];
141
+ if (!input.hasFiducials && input.mountingHoleCount && input.mountingHoleCount > 0) {
142
+ issues.push(pcbWarning('PCB_FIDUCIAL_REQUIRED', 'No fiducial marks defined for SMT assembly', {
143
+ remediationHint: 'Add at least 3 fiducial marks (global, near board corners) for pick-and-place machine alignment',
144
+ }));
145
+ }
146
+ return issues;
147
+ }
148
+ // ── Rule: high-voltage creepage/clearance ──────────────────────────────────
149
+ function checkHighVoltageClearance(input) {
150
+ const issues = [];
151
+ if (input.hasHighVoltage && (!input.hasClearanceRules || !input.hasNetClasses)) {
152
+ issues.push(pcbError('PCB_HIGH_VOLTAGE_CLEARANCE', 'High-voltage domain detected but no clearance rules defined', {
153
+ remediationHint: 'Define explicit creepage and clearance distances for high-voltage nets. Minimum 4mm clearance for 250V AC is recommended.',
154
+ }));
155
+ }
156
+ return issues;
157
+ }
158
+ const RULES = [
159
+ checkMissingOutline,
160
+ checkOutlineTooSmall,
161
+ checkMissingStackup,
162
+ checkLayerCountMismatch,
163
+ checkMissingMountingHoles,
164
+ checkMissingNetClasses,
165
+ checkInvalidClearance,
166
+ checkKeepoutAreas,
167
+ checkMissingPlacementZones,
168
+ checkMissingManufacturingConstraints,
169
+ checkFiducials,
170
+ checkHighVoltageClearance,
171
+ ];
172
+ // ── Main entry point ───────────────────────────────────────────────────────
173
+ /**
174
+ * Run all PCB constraint validation rules against board data.
175
+ *
176
+ * Orchestrates 12 rules:
177
+ * 1. Missing outline — error if board outline is not defined
178
+ * 2. Outline too small — warning if dimensions are below 5mm
179
+ * 3. Missing stackup — warning if no detailed layer stack
180
+ * 4. Layer count mismatch — error if >2 layers without stackup
181
+ * 5. Missing mounting holes — warning if zero mounting holes
182
+ * 6. Missing net classes — warning if no routing rules
183
+ * 7. Invalid clearance — warning if net classes but no clearance rules
184
+ * 8. Keepout violations — warning if zones but no keepouts
185
+ * 9. Missing placement zones — warning if multi-layer without zones
186
+ * 10. Missing mfg constraints — warning if process/quantity unspecified
187
+ * 11. Fiducials recommended — warning if SMT board without fiducials
188
+ * 12. High-voltage clearance — error if HV domain without proper clearance
189
+ */
190
+ export function validatePcbConstraints(input) {
191
+ const errors = [];
192
+ const warnings = [];
193
+ let passed = 0;
194
+ for (const rule of RULES) {
195
+ const issues = rule(input);
196
+ if (issues.length === 0) {
197
+ passed++;
198
+ }
199
+ else {
200
+ for (const issue of issues) {
201
+ if (issue.severity === 'error') {
202
+ errors.push(issue);
203
+ }
204
+ else {
205
+ warnings.push(issue);
206
+ }
207
+ }
208
+ }
209
+ }
210
+ const totalChecks = RULES.length;
211
+ const failed = errors.length + warnings.length;
212
+ const notApplicable = Math.max(0, totalChecks - passed - failed);
213
+ return {
214
+ valid: errors.length === 0,
215
+ errors,
216
+ warnings,
217
+ summary: {
218
+ totalChecks,
219
+ passed,
220
+ failed,
221
+ notApplicable,
222
+ },
223
+ };
224
+ }
225
+ /**
226
+ * Build a human-readable constraint report explaining which constraints
227
+ * were applied and which require manual review.
228
+ */
229
+ export function buildConstraintReport(input, result) {
230
+ const checked = [];
231
+ const manualReviewRequired = [];
232
+ // Build checked list from validation results
233
+ const areaMap = {
234
+ 'Board outline': 'Board outline defines the physical board shape for fabrication',
235
+ 'Board dimensions': 'Minimum dimension check ensures manufacturability',
236
+ 'Layer stackup': 'Layer stackup defines material, thickness, and copper weight per layer',
237
+ 'Layer count': 'Layer count must match between intent and stackup',
238
+ 'Mounting holes': 'Mounting holes provide mechanical mounting and assembly alignment',
239
+ 'Net classes': 'Net classes enforce trace width and clearance for signal integrity',
240
+ 'Clearance rules': 'Clearance rules prevent shorts between different net classes',
241
+ 'Keepout areas': 'Keepout areas prevent component/track placement in restricted zones',
242
+ 'Placement zones': 'Placement zones guide component grouping for optimal layout',
243
+ 'Manufacturing constraints': 'Manufacturing constraints ensure correct fabrication process',
244
+ Fiducials: 'Fiducial marks enable pick-and-place machine alignment',
245
+ 'High-voltage clearance': 'High-voltage clearance ensures safety against arcing',
246
+ };
247
+ // Determine which rules actually produced output
248
+ const hasErrorsOrWarnings = result.errors.length > 0 || result.warnings.length > 0;
249
+ if (!input.hasOutline && !hasErrorsOrWarnings) {
250
+ checked.push({
251
+ area: 'Board outline',
252
+ status: 'failed',
253
+ details: areaMap['Board outline'] ?? '',
254
+ });
255
+ }
256
+ // Add manual review items for things that can't be auto-checked
257
+ manualReviewRequired.push({
258
+ area: 'Component placement',
259
+ reason: 'AI-guided component placement requires human review before applying to PCB layout',
260
+ });
261
+ manualReviewRequired.push({
262
+ area: 'Trace routing',
263
+ reason: 'Automated trace routing may require manual optimization for signal integrity',
264
+ });
265
+ manualReviewRequired.push({
266
+ area: 'Thermal management',
267
+ reason: 'Thermal vias and copper pour areas should be verified against thermal simulation',
268
+ });
269
+ // Overall verdict
270
+ let verdict;
271
+ if (result.errors.length > 0) {
272
+ verdict = 'rejected';
273
+ }
274
+ else if (result.warnings.length > 0 || manualReviewRequired.length > 0) {
275
+ verdict = 'needs-review';
276
+ }
277
+ else {
278
+ verdict = 'approved';
279
+ }
280
+ return {
281
+ checked,
282
+ manualReviewRequired,
283
+ verdict,
284
+ };
285
+ }
286
+ /**
287
+ * Create PcbConstraintInput from a CircuitIR PCB intent object.
288
+ */
289
+ export function fromPcbIntent(pcb) {
290
+ return {
291
+ widthMm: pcb.widthMm,
292
+ heightMm: pcb.heightMm,
293
+ layerCount: pcb.layerCount,
294
+ hasOutline: !!(pcb.boardOutline && (Array.isArray(pcb.boardOutline) ? pcb.boardOutline.length > 0 : true)),
295
+ mountingHoleCount: pcb.mountingHoles?.length ?? 0,
296
+ hasLayerStack: !!(pcb.layerStack && pcb.layerStack.length > 0),
297
+ hasNetClasses: !!(pcb.netClasses && pcb.netClasses.length > 0),
298
+ hasClearanceRules: !!(pcb.clearanceRules && pcb.clearanceRules.length > 0),
299
+ hasKeepoutAreas: !!(pcb.keepoutAreas && pcb.keepoutAreas.length > 0),
300
+ hasPlacementZones: !!(pcb.placementZones && pcb.placementZones.length > 0),
301
+ hasFiducials: !!(pcb.fiducials && pcb.fiducials.length > 0),
302
+ hasTestPads: !!(pcb.testPads && pcb.testPads.length > 0),
303
+ hasHighVoltage: pcb.highVoltage ?? false,
304
+ manufacturingProcess: pcb.manufacturingProcess ?? pcb.manufacturing?.process,
305
+ hasQuantity: !!(pcb.quantity && pcb.quantity > 0) ||
306
+ !!(pcb.manufacturing?.quantity && pcb.manufacturing.quantity > 0),
307
+ };
308
+ }
309
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/pcb-constraints/validation.ts"],"names":[],"mappings":"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"}
@@ -55,6 +55,8 @@ export async function createServer(config) {
55
55
  config: {
56
56
  bridgeTimeoutMs: config.BRIDGE_TIMEOUT_MS,
57
57
  artifactDir: config.ARTIFACT_DIR,
58
+ bridgeHost: config.BRIDGE_HOST,
59
+ bridgePort: config.BRIDGE_PORT,
58
60
  },
59
61
  vendors: {
60
62
  lcsc: lcscClient,
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/server/factory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/server/factory.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { type Express } from 'express';
1
+ import { type Express, type Request, type Response, type NextFunction } from 'express';
2
2
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
3
  import { type EnvConfig } from '../../config/env.js';
4
4
  export interface HttpTransportInstance {
@@ -7,4 +7,23 @@ export interface HttpTransportInstance {
7
7
  start: () => Promise<void>;
8
8
  close: () => Promise<void>;
9
9
  }
10
+ /** Check whether the given host is a loopback address. */
11
+ export declare function isLoopback(host: string): boolean;
12
+ /**
13
+ * Parse a comma-separated origin allowlist string into a Set.
14
+ * Empty string returns an empty set. '*' returns a set containing '*' only.
15
+ */
16
+ export declare function parseOriginAllowlist(raw: string): Set<string>;
17
+ /**
18
+ * Create middleware for origin validation and CORS handling.
19
+ *
20
+ * - DNS rebinding protection: validates the Host header matches the configured
21
+ * HTTP_HOST when running on a non-loopback address.
22
+ * - Origin validation: rejects requests whose `Origin` is not in the allowlist.
23
+ * - CORS preflight: responds with appropriate headers for OPTIONS requests.
24
+ * - No-origin requests (non-browser clients) are always allowed.
25
+ */
26
+ export declare function createOriginValidator(config: EnvConfig): (req: Request, res: Response, next: NextFunction) => void;
27
+ /** CORS preflight handler for OPTIONS requests. */
28
+ export declare function handleCorsPreflight(req: Request, res: Response, next: NextFunction): void;
10
29
  export declare function createHttpTransport(config: EnvConfig): HttpTransportInstance;
@@ -1,8 +1,11 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/express.js';
3
3
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
4
+ import { createRemoteJWKSet, jwtVerify } from 'jose';
4
5
  import { SERVER_VERSION } from '../../config/version.js';
5
6
  import { getLogger } from '../../utils/logger.js';
7
+ /** Known loopback host values used for default origin policy. */
8
+ const LOOPBACK_ORIGINS = new Set(['http://127.0.0.1', 'http://localhost', 'http://::1', 'null']);
6
9
  function createRateLimiter(windowMs, maxRequests) {
7
10
  const store = new Map();
8
11
  const CLEANUP_THRESHOLD = 1000;
@@ -31,56 +34,184 @@ function createRateLimiter(windowMs, maxRequests) {
31
34
  next();
32
35
  };
33
36
  }
37
+ /**
38
+ * Supported token type hints (typ claim).
39
+ * We accept JWT by default and only reject tokens with an explicit non-JWT typ.
40
+ */
41
+ const SUPPORTED_TOKEN_TYPES = new Set(['JWT', undefined]);
42
+ /** Structured error responses for token validation failures. */
43
+ function tokenError(res, message, code = 'invalid_token') {
44
+ res.status(401).json({ error: message, code });
45
+ }
34
46
  function validateOAuthToken(config) {
35
47
  if (!config.OAUTH_ENABLED) {
36
48
  return (_req, _res, next) => next();
37
49
  }
38
- let JWKSet = null;
50
+ // The config validator already ensures OAUTH_JWKS_URI is present when OAuth is enabled.
51
+ // We cache the JWKSet for the lifetime of the server.
52
+ const JWKSet = createRemoteJWKSet(new URL(config.OAUTH_JWKS_URI));
39
53
  return (req, res, next) => {
54
+ // ── 1. Extract Bearer token ───────────────────────────────────────────
40
55
  const auth = req.headers.authorization;
41
56
  if (!auth || !auth.startsWith('Bearer ')) {
42
- res.status(401).json({ error: 'Missing or invalid Authorization header' });
57
+ tokenError(res, 'Missing or invalid Authorization header', 'missing_auth');
43
58
  return;
44
59
  }
45
60
  const token = auth.slice(7);
46
61
  if (!token) {
47
- res.status(401).json({ error: 'Empty Bearer token' });
62
+ tokenError(res, 'Empty Bearer token', 'empty_token');
48
63
  return;
49
64
  }
50
- if (!config.OAUTH_JWKS_URI) {
51
- const configured = config.OAUTH_REQUIRED_SCOPES || 'easyeda:read';
52
- if (token.length < 8) {
53
- res.status(401).json({ error: 'Invalid token format' });
65
+ // ── 2. Verify JWT ─────────────────────────────────────────────────────
66
+ jwtVerify(token, JWKSet, {
67
+ issuer: config.OAUTH_ISSUER || undefined,
68
+ audience: config.OAUTH_AUDIENCE || undefined,
69
+ algorithms: ['RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512'],
70
+ })
71
+ .then(({ payload, protectedHeader }) => {
72
+ // Validate token type (typ) — it lives in the protected header per JWT spec
73
+ const typ = protectedHeader.typ;
74
+ if (typ !== undefined && !SUPPORTED_TOKEN_TYPES.has(typ)) {
75
+ tokenError(res, `Unsupported token type: ${typ}`, 'unsupported_token_type');
54
76
  return;
55
77
  }
56
- res.locals.scope = configured;
78
+ res.locals.claims = payload;
79
+ next();
80
+ })
81
+ .catch((err) => {
82
+ const msg = err instanceof Error ? err.message : String(err);
83
+ getLogger().warn({ err: msg }, 'OAuth token validation failed');
84
+ // Map jose errors to user-friendly messages
85
+ // jose error examples:
86
+ // '"exp" claim timestamp check failed' → token_expired
87
+ // '"iss" claim mismatch' → invalid_issuer
88
+ // '"aud" claim mismatch' → invalid_audience
89
+ // 'signature verification failed' → invalid_signature
90
+ if (/["']exp["']/.test(msg) || /expired|timestamp/i.test(msg)) {
91
+ tokenError(res, 'Token has expired', 'token_expired');
92
+ }
93
+ else if (/["']iss["']/.test(msg) || /issuer/i.test(msg)) {
94
+ tokenError(res, 'Invalid token issuer', 'invalid_issuer');
95
+ }
96
+ else if (/["']aud["']/.test(msg) || /audience/i.test(msg)) {
97
+ tokenError(res, 'Invalid token audience', 'invalid_audience');
98
+ }
99
+ else if (/signature|key|JWT|JWS|malformed|parse/i.test(msg)) {
100
+ tokenError(res, 'Invalid token signature', 'invalid_signature');
101
+ }
102
+ else {
103
+ tokenError(res, 'Token validation failed', 'token_validation_failed');
104
+ }
105
+ });
106
+ };
107
+ }
108
+ /** Check whether the given host is a loopback address. */
109
+ export function isLoopback(host) {
110
+ return host === '127.0.0.1' || host === 'localhost' || host === '::1';
111
+ }
112
+ /**
113
+ * Parse a comma-separated origin allowlist string into a Set.
114
+ * Empty string returns an empty set. '*' returns a set containing '*' only.
115
+ */
116
+ export function parseOriginAllowlist(raw) {
117
+ if (!raw)
118
+ return new Set();
119
+ const origins = raw
120
+ .split(',')
121
+ .map((s) => s.trim())
122
+ .filter(Boolean);
123
+ return new Set(origins);
124
+ }
125
+ /**
126
+ * Create middleware for origin validation and CORS handling.
127
+ *
128
+ * - DNS rebinding protection: validates the Host header matches the configured
129
+ * HTTP_HOST when running on a non-loopback address.
130
+ * - Origin validation: rejects requests whose `Origin` is not in the allowlist.
131
+ * - CORS preflight: responds with appropriate headers for OPTIONS requests.
132
+ * - No-origin requests (non-browser clients) are always allowed.
133
+ */
134
+ export function createOriginValidator(config) {
135
+ const allowedOrigins = parseOriginAllowlist(config.ALLOWED_ORIGINS);
136
+ const hasExplicitAllowlist = allowedOrigins.size > 0;
137
+ const allowAll = allowedOrigins.has('*');
138
+ const loopbackMode = isLoopback(config.HTTP_HOST);
139
+ return (req, res, next) => {
140
+ // ── 1. DNS rebinding protection ───────────────────────────────────────
141
+ // On non-loopback, verify the Host header matches our configured bind address.
142
+ // This prevents an attacker from pointing a DNS name at our IP and bypassing
143
+ // browser same-origin policy.
144
+ if (!loopbackMode) {
145
+ const host = req.headers.host;
146
+ if (host) {
147
+ const expectedHost = `${config.HTTP_HOST}:${config.HTTP_PORT}`;
148
+ const expectedHostWithoutPort = config.HTTP_HOST;
149
+ if (host !== expectedHost &&
150
+ host !== expectedHostWithoutPort &&
151
+ !host.startsWith('127.0.0.1') &&
152
+ !host.startsWith('localhost')) {
153
+ res.status(400).json({
154
+ error: 'Invalid Host header',
155
+ code: 'host_mismatch',
156
+ });
157
+ return;
158
+ }
159
+ }
160
+ }
161
+ // ── 2. Origin validation ──────────────────────────────────────────────
162
+ const origin = req.headers.origin;
163
+ // Non-browser requests without an Origin header are always allowed.
164
+ if (!origin) {
57
165
  next();
58
166
  return;
59
167
  }
60
- Promise.resolve()
61
- .then(async () => {
62
- const { createRemoteJWKSet, jwtVerify } = await import('jose');
63
- if (!JWKSet) {
64
- JWKSet = createRemoteJWKSet(new URL(config.OAUTH_JWKS_URI));
168
+ // Wildcard allows everything.
169
+ if (allowAll) {
170
+ res.setHeader('Access-Control-Allow-Origin', origin);
171
+ next();
172
+ return;
173
+ }
174
+ // On loopback, accept known localhost origins and the legacy CORS_ORIGIN.
175
+ if (loopbackMode) {
176
+ const legacyMatch = config.CORS_ORIGIN && origin === config.CORS_ORIGIN;
177
+ if (LOOPBACK_ORIGINS.has(origin) || legacyMatch) {
178
+ res.setHeader('Access-Control-Allow-Origin', origin);
179
+ next();
180
+ return;
65
181
  }
66
- const { payload } = await jwtVerify(token, JWKSet, {
67
- issuer: config.OAUTH_ISSUER || undefined,
68
- audience: config.OAUTH_AUDIENCE || undefined,
69
- });
70
- res.locals.claims = payload;
182
+ }
183
+ // Check the explicit allowlist.
184
+ if (hasExplicitAllowlist && allowedOrigins.has(origin)) {
185
+ res.setHeader('Access-Control-Allow-Origin', origin);
71
186
  next();
72
- })
73
- .catch((err) => {
74
- getLogger().error({ err: err instanceof Error ? err.message : String(err) }, 'Token validation failed');
75
- res.status(403).json({ error: 'Token validation failed' });
187
+ return;
188
+ }
189
+ // ── 3. Reject unknown origins ─────────────────────────────────────────
190
+ res.status(403).json({
191
+ error: 'Origin not allowed',
192
+ code: 'origin_not_allowed',
76
193
  });
77
194
  };
78
195
  }
196
+ /** CORS preflight handler for OPTIONS requests. */
197
+ export function handleCorsPreflight(req, res, next) {
198
+ if (req.method === 'OPTIONS') {
199
+ // Vary: Origin tells caches that the response varies by the Origin header.
200
+ res.setHeader('Vary', 'Origin');
201
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
202
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
203
+ res.setHeader('Access-Control-Max-Age', '86400');
204
+ res.status(204).end();
205
+ return;
206
+ }
207
+ next();
208
+ }
79
209
  function addSecurityHeaders(_req, res, next) {
80
210
  res.setHeader('X-Content-Type-Options', 'nosniff');
81
211
  res.setHeader('X-Frame-Options', 'DENY');
82
212
  res.setHeader('X-XSS-Protection', '0');
83
213
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
214
+ res.setHeader('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none'");
84
215
  next();
85
216
  }
86
217
  export function createHttpTransport(config) {
@@ -92,18 +223,9 @@ export function createHttpTransport(config) {
92
223
  app.use(addSecurityHeaders);
93
224
  app.use(createRateLimiter(60_000, config.HTTP_RATE_LIMIT_MAX));
94
225
  app.use(validateOAuthToken(config));
95
- if (config.CORS_ORIGIN) {
96
- app.use((req, res, next) => {
97
- res.setHeader('Access-Control-Allow-Origin', config.CORS_ORIGIN);
98
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
99
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
100
- if (req.method === 'OPTIONS') {
101
- res.status(204).end();
102
- return;
103
- }
104
- next();
105
- });
106
- }
226
+ // Origin validation and CORS (must come before route handlers).
227
+ app.use(createOriginValidator(config));
228
+ app.use(handleCorsPreflight);
107
229
  app.post('/mcp', (req, res) => {
108
230
  transport.handleRequest(req, res, req.body);
109
231
  });