@mcpmake/core 0.2.4 → 0.3.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 (184) hide show
  1. package/dist/analyzer/dom-parser.d.ts +35 -0
  2. package/dist/analyzer/dom-parser.d.ts.map +1 -1
  3. package/dist/analyzer/dom-parser.js +107 -6
  4. package/dist/analyzer/dom-parser.js.map +1 -1
  5. package/dist/analyzer/goal-crawler.d.ts +2 -1
  6. package/dist/analyzer/goal-crawler.d.ts.map +1 -1
  7. package/dist/analyzer/goal-crawler.js +66 -17
  8. package/dist/analyzer/goal-crawler.js.map +1 -1
  9. package/dist/analyzer/semantic-analyzer.d.ts +1 -1
  10. package/dist/analyzer/semantic-analyzer.d.ts.map +1 -1
  11. package/dist/analyzer/semantic-analyzer.js +15 -16
  12. package/dist/analyzer/semantic-analyzer.js.map +1 -1
  13. package/dist/analyzer/site-crawler.d.ts.map +1 -1
  14. package/dist/analyzer/site-crawler.js +122 -16
  15. package/dist/analyzer/site-crawler.js.map +1 -1
  16. package/dist/config/mcpmake-config.d.ts.map +1 -1
  17. package/dist/config/mcpmake-config.js +5 -0
  18. package/dist/config/mcpmake-config.js.map +1 -1
  19. package/dist/emitter/code-writer.d.ts +1 -0
  20. package/dist/emitter/code-writer.d.ts.map +1 -1
  21. package/dist/emitter/code-writer.js +79 -12
  22. package/dist/emitter/code-writer.js.map +1 -1
  23. package/dist/emitter/index.d.ts +8 -0
  24. package/dist/emitter/index.d.ts.map +1 -1
  25. package/dist/emitter/index.js +113 -6
  26. package/dist/emitter/index.js.map +1 -1
  27. package/dist/emitter/project-scaffolder.d.ts +13 -0
  28. package/dist/emitter/project-scaffolder.d.ts.map +1 -1
  29. package/dist/emitter/project-scaffolder.js +29 -0
  30. package/dist/emitter/project-scaffolder.js.map +1 -1
  31. package/dist/emitter/python-template-loader.d.ts.map +1 -1
  32. package/dist/emitter/python-template-loader.js +10 -0
  33. package/dist/emitter/python-template-loader.js.map +1 -1
  34. package/dist/emitter/python-templates/dockerfile.hbs +4 -1
  35. package/dist/emitter/python-templates/server.py.hbs +82 -8
  36. package/dist/emitter/site-scaffolder.d.ts.map +1 -1
  37. package/dist/emitter/site-scaffolder.js +28 -1
  38. package/dist/emitter/site-scaffolder.js.map +1 -1
  39. package/dist/emitter/site-templates/browser-manager.ts.hbs +104 -34
  40. package/dist/emitter/site-templates/dockerfile.hbs +13 -4
  41. package/dist/emitter/site-templates/env.example.hbs +8 -0
  42. package/dist/emitter/site-templates/server-main-http.ts.hbs +77 -6
  43. package/dist/emitter/site-templates/telemetry.ts.hbs +117 -0
  44. package/dist/emitter/site-templates/tool-handler-form.ts.hbs +10 -8
  45. package/dist/emitter/template-loader.d.ts.map +1 -1
  46. package/dist/emitter/template-loader.js +9 -0
  47. package/dist/emitter/template-loader.js.map +1 -1
  48. package/dist/emitter/templates/auth-provider.ts.hbs +86 -21
  49. package/dist/emitter/templates/config.ts.hbs +29 -0
  50. package/dist/emitter/templates/dockerfile.hbs +12 -4
  51. package/dist/emitter/templates/env.example.hbs +6 -0
  52. package/dist/emitter/templates/http-executor.ts.hbs +150 -7
  53. package/dist/emitter/templates/oauth.ts.hbs +19 -65
  54. package/dist/emitter/templates/server-main-http.ts.hbs +93 -12
  55. package/dist/emitter/templates/task-handlers.ts.hbs +40 -1
  56. package/dist/emitter/templates/tool-handler.ts.hbs +37 -2
  57. package/dist/emitter/templates/tool-test.ts.hbs +9 -1
  58. package/dist/emitter/worker-templates/config.ts.hbs +7 -0
  59. package/dist/emitter/worker-templates/tool-handler.ts.hbs +37 -2
  60. package/dist/emitter/worker-templates/worker.ts.hbs +119 -25
  61. package/dist/generator/spec-generator.d.ts.map +1 -1
  62. package/dist/generator/spec-generator.js +9 -54
  63. package/dist/generator/spec-generator.js.map +1 -1
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +1 -0
  67. package/dist/index.js.map +1 -1
  68. package/dist/llm/anthropic-provider.d.ts +14 -0
  69. package/dist/llm/anthropic-provider.d.ts.map +1 -0
  70. package/dist/llm/anthropic-provider.js +50 -0
  71. package/dist/llm/anthropic-provider.js.map +1 -0
  72. package/dist/llm/index.d.ts +19 -0
  73. package/dist/llm/index.d.ts.map +1 -0
  74. package/dist/llm/index.js +73 -0
  75. package/dist/llm/index.js.map +1 -0
  76. package/dist/llm/openai-provider.d.ts +46 -0
  77. package/dist/llm/openai-provider.d.ts.map +1 -0
  78. package/dist/llm/openai-provider.js +221 -0
  79. package/dist/llm/openai-provider.js.map +1 -0
  80. package/dist/llm/types.d.ts +61 -0
  81. package/dist/llm/types.d.ts.map +1 -0
  82. package/dist/llm/types.js +16 -0
  83. package/dist/llm/types.js.map +1 -0
  84. package/dist/parser/har-filter.d.ts +7 -0
  85. package/dist/parser/har-filter.d.ts.map +1 -1
  86. package/dist/parser/har-filter.js +107 -1
  87. package/dist/parser/har-filter.js.map +1 -1
  88. package/dist/parser/index.d.ts +1 -1
  89. package/dist/parser/index.d.ts.map +1 -1
  90. package/dist/parser/index.js +1 -1
  91. package/dist/parser/index.js.map +1 -1
  92. package/dist/parser/openapi-loader.d.ts.map +1 -1
  93. package/dist/parser/openapi-loader.js +36 -2
  94. package/dist/parser/openapi-loader.js.map +1 -1
  95. package/dist/parser/operation-extractor.d.ts.map +1 -1
  96. package/dist/parser/operation-extractor.js +12 -1
  97. package/dist/parser/operation-extractor.js.map +1 -1
  98. package/dist/parser/overlay-loader.d.ts.map +1 -1
  99. package/dist/parser/overlay-loader.js +11 -2
  100. package/dist/parser/overlay-loader.js.map +1 -1
  101. package/dist/parser/postman-loader.d.ts.map +1 -1
  102. package/dist/parser/postman-loader.js +6 -1
  103. package/dist/parser/postman-loader.js.map +1 -1
  104. package/dist/parser/schema-converter.d.ts +13 -2
  105. package/dist/parser/schema-converter.d.ts.map +1 -1
  106. package/dist/parser/schema-converter.js +64 -42
  107. package/dist/parser/schema-converter.js.map +1 -1
  108. package/dist/plugins/loader.d.ts +5 -1
  109. package/dist/plugins/loader.d.ts.map +1 -1
  110. package/dist/plugins/loader.js +39 -10
  111. package/dist/plugins/loader.js.map +1 -1
  112. package/dist/pricing.d.ts +5 -1
  113. package/dist/pricing.d.ts.map +1 -1
  114. package/dist/pricing.js +7 -3
  115. package/dist/pricing.js.map +1 -1
  116. package/dist/providers/index.d.ts.map +1 -1
  117. package/dist/providers/index.js +6 -21
  118. package/dist/providers/index.js.map +1 -1
  119. package/dist/recorder/browser-recorder.d.ts.map +1 -1
  120. package/dist/recorder/browser-recorder.js +89 -12
  121. package/dist/recorder/browser-recorder.js.map +1 -1
  122. package/dist/rescan/rescan-scheduler.d.ts.map +1 -1
  123. package/dist/rescan/rescan-scheduler.js +22 -7
  124. package/dist/rescan/rescan-scheduler.js.map +1 -1
  125. package/dist/site-transformer/selector-healer.d.ts +1 -1
  126. package/dist/site-transformer/selector-healer.d.ts.map +1 -1
  127. package/dist/site-transformer/selector-healer.js +17 -18
  128. package/dist/site-transformer/selector-healer.js.map +1 -1
  129. package/dist/site-transformer/tool-generator.d.ts.map +1 -1
  130. package/dist/site-transformer/tool-generator.js +90 -22
  131. package/dist/site-transformer/tool-generator.js.map +1 -1
  132. package/dist/transformer/auth-detector.d.ts.map +1 -1
  133. package/dist/transformer/auth-detector.js +24 -5
  134. package/dist/transformer/auth-detector.js.map +1 -1
  135. package/dist/transformer/har-schema-inferrer.d.ts.map +1 -1
  136. package/dist/transformer/har-schema-inferrer.js +2 -0
  137. package/dist/transformer/har-schema-inferrer.js.map +1 -1
  138. package/dist/transformer/har-to-operations.d.ts +1 -0
  139. package/dist/transformer/har-to-operations.d.ts.map +1 -1
  140. package/dist/transformer/har-to-operations.js +42 -10
  141. package/dist/transformer/har-to-operations.js.map +1 -1
  142. package/dist/transformer/llm-namer.d.ts.map +1 -1
  143. package/dist/transformer/llm-namer.js +10 -13
  144. package/dist/transformer/llm-namer.js.map +1 -1
  145. package/dist/transformer/naming.d.ts.map +1 -1
  146. package/dist/transformer/naming.js +16 -8
  147. package/dist/transformer/naming.js.map +1 -1
  148. package/dist/transformer/resource-builder.d.ts.map +1 -1
  149. package/dist/transformer/resource-builder.js +13 -6
  150. package/dist/transformer/resource-builder.js.map +1 -1
  151. package/dist/transformer/stainless-translator.d.ts +6 -0
  152. package/dist/transformer/stainless-translator.d.ts.map +1 -1
  153. package/dist/transformer/stainless-translator.js +18 -1
  154. package/dist/transformer/stainless-translator.js.map +1 -1
  155. package/dist/transformer/tool-builder.d.ts.map +1 -1
  156. package/dist/transformer/tool-builder.js +141 -14
  157. package/dist/transformer/tool-builder.js.map +1 -1
  158. package/dist/types/index.d.ts +79 -0
  159. package/dist/types/index.d.ts.map +1 -1
  160. package/dist/types/site.d.ts +19 -1
  161. package/dist/types/site.d.ts.map +1 -1
  162. package/dist/utils/fail.d.ts.map +1 -1
  163. package/dist/utils/fail.js +22 -4
  164. package/dist/utils/fail.js.map +1 -1
  165. package/dist/utils/json-extract.d.ts +21 -0
  166. package/dist/utils/json-extract.d.ts.map +1 -0
  167. package/dist/utils/json-extract.js +67 -0
  168. package/dist/utils/json-extract.js.map +1 -0
  169. package/dist/utils/model-resolver.d.ts.map +1 -1
  170. package/dist/utils/model-resolver.js +6 -0
  171. package/dist/utils/model-resolver.js.map +1 -1
  172. package/dist/utils/sanitize.d.ts +63 -1
  173. package/dist/utils/sanitize.d.ts.map +1 -1
  174. package/dist/utils/sanitize.js +147 -2
  175. package/dist/utils/sanitize.js.map +1 -1
  176. package/dist/utils/ssrf-guard.d.ts +10 -0
  177. package/dist/utils/ssrf-guard.d.ts.map +1 -0
  178. package/dist/utils/ssrf-guard.js +130 -0
  179. package/dist/utils/ssrf-guard.js.map +1 -0
  180. package/dist/utils/watcher.d.ts +17 -1
  181. package/dist/utils/watcher.d.ts.map +1 -1
  182. package/dist/utils/watcher.js +53 -21
  183. package/dist/utils/watcher.js.map +1 -1
  184. package/package.json +2 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-extract.js","sourceRoot":"","sources":["../../src/utils/json-extract.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,SAAS,eAAe,CAAC,IAAY,EAAE,IAAY,EAAE,KAAa;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,OAAO;gBAAE,OAAO,GAAG,KAAK,CAAC;iBACxB,IAAI,EAAE,KAAK,IAAI;gBAAE,OAAO,GAAG,IAAI,CAAC;iBAChC,IAAI,EAAE,KAAK,GAAG;gBAAE,QAAQ,GAAG,KAAK,CAAC;QACxC,CAAC;aAAM,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACtB,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;aAAM,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YACvB,KAAK,EAAE,CAAC;QACV,CAAC;aAAM,IAAI,EAAE,KAAK,KAAK,EAAE,CAAC;YACxB,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAEnC,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI,QAAQ,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,kEAAkE;IAClE,MAAM,QAAQ,GAAG,QAAQ,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,CAAC,IAAI,QAAQ,GAAG,QAAQ,CAAC,CAAC;IAE7E,OAAO,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AACtF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"model-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/model-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAG/C;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAqB5C;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,SAAS,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAsCjB"}
1
+ {"version":3,"file":"model-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/model-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAG/C;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAqB5C;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,SAAS,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CA4CjB"}
@@ -56,8 +56,14 @@ export async function resolveModel(client, tier, override) {
56
56
  }
57
57
  }
58
58
  catch (err) {
59
+ // Transient Models API failure: return the fallback for this call WITHOUT
60
+ // caching it, so a later call retries once the API recovers (a poisoned
61
+ // cache would pin the whole process to the fallback alias).
59
62
  logger.warn(`Could not list models (${err instanceof Error ? err.message : err}); using "${preferred}"`);
63
+ return preferred;
60
64
  }
65
+ // Only successful resolutions are cached — the served-model list rarely
66
+ // changes mid-run, so one API round-trip per tier is enough.
61
67
  cache.set(tier, choice);
62
68
  return choice;
63
69
  }
@@ -1 +1 @@
1
- {"version":3,"file":"model-resolver.js","sourceRoot":"","sources":["../../src/utils/model-resolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AASrC;;;;GAIG;AACH,MAAM,SAAS,GAA8B;IAC3C,QAAQ,EAAE,mBAAmB;IAC7B,IAAI,EAAE,kBAAkB;CACzB,CAAC;AAEF,MAAM,YAAY,GAA8B;IAC9C,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,QAAQ;CACf,CAAC;AAEF,2EAA2E;AAC3E,wEAAwE;AACxE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAiB,EACjB,IAAe,EACf,QAAiB;IAEjB,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,MAAM,GAAG,SAAS,CAAC;IAEvB,IAAI,CAAC;QACH,MAAM,MAAM,GAA+D,EAAE,CAAC;QAC9E,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;QACpF,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC;YAC5C,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YACnC,MAAM,MAAM,GAAG,MAAM;iBAClB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;gBAClE,yEAAyE;iBACxE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/D,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,CAAC,IAAI,CAAC,oBAAoB,SAAS,yBAAyB,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;gBAChF,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CACT,oBAAoB,SAAS,wBAAwB,IAAI,gCAAgC,CAC1F,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CACT,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,aAAa,SAAS,GAAG,CAC5F,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxB,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"model-resolver.js","sourceRoot":"","sources":["../../src/utils/model-resolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AASrC;;;;GAIG;AACH,MAAM,SAAS,GAA8B;IAC3C,QAAQ,EAAE,mBAAmB;IAC7B,IAAI,EAAE,kBAAkB;CACzB,CAAC;AAEF,MAAM,YAAY,GAA8B;IAC9C,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,QAAQ;CACf,CAAC;AAEF,2EAA2E;AAC3E,wEAAwE;AACxE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAiB,EACjB,IAAe,EACf,QAAiB;IAEjB,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,MAAM,GAAG,SAAS,CAAC;IAEvB,IAAI,CAAC;QACH,MAAM,MAAM,GAA+D,EAAE,CAAC;QAC9E,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;QACpF,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC;YAC5C,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YACnC,MAAM,MAAM,GAAG,MAAM;iBAClB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;gBAClE,yEAAyE;iBACxE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/D,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,CAAC,IAAI,CAAC,oBAAoB,SAAS,yBAAyB,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;gBAChF,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CACT,oBAAoB,SAAS,wBAAwB,IAAI,gCAAgC,CAC1F,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,0EAA0E;QAC1E,wEAAwE;QACxE,4DAA4D;QAC5D,MAAM,CAAC,IAAI,CACT,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,aAAa,SAAS,GAAG,CAC5F,CAAC;QACF,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,wEAAwE;IACxE,6DAA6D;IAC7D,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxB,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Sanitize a string for safe embedding in a JavaScript/TypeScript single-quoted string literal.
3
- * Escapes backslashes, single quotes, and newlines.
3
+ * Escapes backslashes, single quotes, and CR/LF.
4
4
  */
5
5
  export declare function escapeStringLiteral(str: string): string;
6
6
  /**
@@ -13,6 +13,68 @@ export declare function escapeTemplateLiteral(str: string): string;
13
13
  * Strips anything that isn't alphanumeric, underscore, or hyphen.
14
14
  */
15
15
  export declare function sanitizeIdentifier(str: string): string;
16
+ /**
17
+ * Sanitize an HTTP header name (e.g. an apiKey scheme `name`) for safe embedding
18
+ * in a string literal. Real header names are RFC 7230 tokens; in practice API
19
+ * key headers are `[A-Za-z0-9-]` (plus `_`). Stripping everything else removes
20
+ * quotes/backticks/newlines that could break out of the generated literal.
21
+ */
22
+ export declare function sanitizeHeaderName(str: string): string;
23
+ /**
24
+ * Sanitize an environment-variable name. It is emitted both as a quoted string
25
+ * and as a bare member access (`process.env.<NAME>` / `env.<NAME>`), so it must
26
+ * be a valid JS/POSIX identifier. Strips invalid chars and prefixes `_` when the
27
+ * result would start with a digit or be empty.
28
+ */
29
+ export declare function sanitizeEnvVarName(str: string): string;
30
+ /**
31
+ * Resolve OpenAPI server-variable placeholders (`{var}`) in a server URL from
32
+ * their declared defaults, then validate the result is a real http(s) URL.
33
+ *
34
+ * OpenAPI server URLs may carry variables — `https://{region}.api.test/{version}`
35
+ * — with a `variables` map giving each a `default`. Substituting them here keeps
36
+ * the URL semantically intact instead of stripping the braces (which would corrupt
37
+ * the path, e.g. `{version}` → empty). Variables without a known default are left
38
+ * as-is; the subsequent per-sink escaper neutralizes any residual unsafe chars.
39
+ */
40
+ export declare function resolveServerUrl(url: string, variables?: Record<string, {
41
+ default?: string;
42
+ } | undefined>): string;
43
+ /**
44
+ * Escape a base URL for safe embedding in a string literal across multiple
45
+ * target languages without mutating its semantics. Unlike a strip-based
46
+ * approach, this preserves every character that is legal in a URL (including
47
+ * `$`, `{`, `}`, `~`, etc. that appear in real paths such as `/v1/$metadata`)
48
+ * and only neutralizes the characters that could break out of, or inject into,
49
+ * the literal it is embedded in:
50
+ * - backslash, single/double quote, backtick → backslash-escaped
51
+ * - `${` (TS template-literal interpolation) → broken with a backslash
52
+ * - CR/LF (could inject extra dotenv/TOML lines) → backslash-escaped
53
+ * The same escaped form is safe in a TS single/double-quote string, a TS
54
+ * backtick literal, a Python double-quote string, a TOML basic string, and a
55
+ * single-line dotenv value. Validation/variable-resolution happens upstream in
56
+ * the emitter via {@link resolveServerUrl}.
57
+ */
58
+ export declare function sanitizeUrlLiteral(str: string): string;
59
+ /**
60
+ * Validate a request-body media type against the RFC media-type grammar
61
+ * (`type/subtype`, optional parameters). The OpenAPI `content` map key is
62
+ * attacker-influenced and is interpolated into a string literal, so a value
63
+ * that is not a well-formed media type is replaced with `application/json`
64
+ * (the universal safe default) rather than trusted. The returned value is
65
+ * still escaped at the literal sink by {@link escapeStringLiteral}.
66
+ */
67
+ export declare function sanitizeMediaType(str: string): string;
68
+ /**
69
+ * Escape a string for safe embedding in a double-quoted Python string literal.
70
+ * Escapes backslashes, double quotes, and newlines.
71
+ */
72
+ export declare function escapePyString(str: string): string;
73
+ /**
74
+ * Convert an arbitrary parameter name into a valid, non-keyword Python
75
+ * identifier for use as a function argument or local variable name.
76
+ */
77
+ export declare function sanitizePyIdentifier(str: string): string;
16
78
  /**
17
79
  * Validate that a URL path template contains only safe characters.
18
80
  * Allows: alphanumeric, /, {, }, -, _, .
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKpD;AAID;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD"}
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC,GAC3D,MAAM,CAUR;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAStD;AAMD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMlD;AA0CD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKpD;AAID;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD"}
@@ -1,9 +1,13 @@
1
1
  /**
2
2
  * Sanitize a string for safe embedding in a JavaScript/TypeScript single-quoted string literal.
3
- * Escapes backslashes, single quotes, and newlines.
3
+ * Escapes backslashes, single quotes, and CR/LF.
4
4
  */
5
5
  export function escapeStringLiteral(str) {
6
- return str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n').replace(/\r/g, '');
6
+ return str
7
+ .replace(/\\/g, '\\\\')
8
+ .replace(/'/g, "\\'")
9
+ .replace(/\n/g, '\\n')
10
+ .replace(/\r/g, '\\r');
7
11
  }
8
12
  /**
9
13
  * Sanitize a string for safe embedding in a JavaScript/TypeScript template literal.
@@ -19,6 +23,147 @@ export function escapeTemplateLiteral(str) {
19
23
  export function sanitizeIdentifier(str) {
20
24
  return str.replace(/[^a-zA-Z0-9_\-]/g, '');
21
25
  }
26
+ /**
27
+ * Sanitize an HTTP header name (e.g. an apiKey scheme `name`) for safe embedding
28
+ * in a string literal. Real header names are RFC 7230 tokens; in practice API
29
+ * key headers are `[A-Za-z0-9-]` (plus `_`). Stripping everything else removes
30
+ * quotes/backticks/newlines that could break out of the generated literal.
31
+ */
32
+ export function sanitizeHeaderName(str) {
33
+ return str.replace(/[^A-Za-z0-9_\-]/g, '');
34
+ }
35
+ /**
36
+ * Sanitize an environment-variable name. It is emitted both as a quoted string
37
+ * and as a bare member access (`process.env.<NAME>` / `env.<NAME>`), so it must
38
+ * be a valid JS/POSIX identifier. Strips invalid chars and prefixes `_` when the
39
+ * result would start with a digit or be empty.
40
+ */
41
+ export function sanitizeEnvVarName(str) {
42
+ const cleaned = str.replace(/[^A-Za-z0-9_]/g, '');
43
+ return /^[A-Za-z_]/.test(cleaned) ? cleaned : `_${cleaned}`;
44
+ }
45
+ /**
46
+ * Resolve OpenAPI server-variable placeholders (`{var}`) in a server URL from
47
+ * their declared defaults, then validate the result is a real http(s) URL.
48
+ *
49
+ * OpenAPI server URLs may carry variables — `https://{region}.api.test/{version}`
50
+ * — with a `variables` map giving each a `default`. Substituting them here keeps
51
+ * the URL semantically intact instead of stripping the braces (which would corrupt
52
+ * the path, e.g. `{version}` → empty). Variables without a known default are left
53
+ * as-is; the subsequent per-sink escaper neutralizes any residual unsafe chars.
54
+ */
55
+ export function resolveServerUrl(url, variables) {
56
+ if (!url)
57
+ return url;
58
+ let resolved = url;
59
+ if (variables) {
60
+ resolved = resolved.replace(/\{([^{}]+)\}/g, (match, name) => {
61
+ const def = variables[name]?.default;
62
+ return typeof def === 'string' ? def : match;
63
+ });
64
+ }
65
+ return resolved;
66
+ }
67
+ /**
68
+ * Escape a base URL for safe embedding in a string literal across multiple
69
+ * target languages without mutating its semantics. Unlike a strip-based
70
+ * approach, this preserves every character that is legal in a URL (including
71
+ * `$`, `{`, `}`, `~`, etc. that appear in real paths such as `/v1/$metadata`)
72
+ * and only neutralizes the characters that could break out of, or inject into,
73
+ * the literal it is embedded in:
74
+ * - backslash, single/double quote, backtick → backslash-escaped
75
+ * - `${` (TS template-literal interpolation) → broken with a backslash
76
+ * - CR/LF (could inject extra dotenv/TOML lines) → backslash-escaped
77
+ * The same escaped form is safe in a TS single/double-quote string, a TS
78
+ * backtick literal, a Python double-quote string, a TOML basic string, and a
79
+ * single-line dotenv value. Validation/variable-resolution happens upstream in
80
+ * the emitter via {@link resolveServerUrl}.
81
+ */
82
+ export function sanitizeUrlLiteral(str) {
83
+ return str
84
+ .replace(/\\/g, '\\\\')
85
+ .replace(/`/g, '\\`')
86
+ .replace(/'/g, "\\'")
87
+ .replace(/"/g, '\\"')
88
+ .replace(/\$\{/g, '\\${')
89
+ .replace(/\r/g, '\\r')
90
+ .replace(/\n/g, '\\n');
91
+ }
92
+ /** RFC 6838 / RFC 7231 media-type token: `type/subtype` with optional params. */
93
+ const MEDIA_TYPE_RE = /^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*\/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*(\s*;\s*[^\s;]+)*$/;
94
+ /**
95
+ * Validate a request-body media type against the RFC media-type grammar
96
+ * (`type/subtype`, optional parameters). The OpenAPI `content` map key is
97
+ * attacker-influenced and is interpolated into a string literal, so a value
98
+ * that is not a well-formed media type is replaced with `application/json`
99
+ * (the universal safe default) rather than trusted. The returned value is
100
+ * still escaped at the literal sink by {@link escapeStringLiteral}.
101
+ */
102
+ export function sanitizeMediaType(str) {
103
+ return MEDIA_TYPE_RE.test(str.trim()) ? str.trim() : 'application/json';
104
+ }
105
+ /**
106
+ * Escape a string for safe embedding in a double-quoted Python string literal.
107
+ * Escapes backslashes, double quotes, and newlines.
108
+ */
109
+ export function escapePyString(str) {
110
+ return str
111
+ .replace(/\\/g, '\\\\')
112
+ .replace(/"/g, '\\"')
113
+ .replace(/\n/g, '\\n')
114
+ .replace(/\r/g, '\\r');
115
+ }
116
+ const PY_KEYWORDS = new Set([
117
+ 'False',
118
+ 'None',
119
+ 'True',
120
+ 'and',
121
+ 'as',
122
+ 'assert',
123
+ 'async',
124
+ 'await',
125
+ 'break',
126
+ 'class',
127
+ 'continue',
128
+ 'def',
129
+ 'del',
130
+ 'elif',
131
+ 'else',
132
+ 'except',
133
+ 'finally',
134
+ 'for',
135
+ 'from',
136
+ 'global',
137
+ 'if',
138
+ 'import',
139
+ 'in',
140
+ 'is',
141
+ 'lambda',
142
+ 'nonlocal',
143
+ 'not',
144
+ 'or',
145
+ 'pass',
146
+ 'raise',
147
+ 'return',
148
+ 'try',
149
+ 'while',
150
+ 'with',
151
+ 'yield',
152
+ 'match',
153
+ 'case',
154
+ ]);
155
+ /**
156
+ * Convert an arbitrary parameter name into a valid, non-keyword Python
157
+ * identifier for use as a function argument or local variable name.
158
+ */
159
+ export function sanitizePyIdentifier(str) {
160
+ let id = str.replace(/[^A-Za-z0-9_]/g, '_');
161
+ if (!/^[A-Za-z_]/.test(id))
162
+ id = `_${id}`;
163
+ if (PY_KEYWORDS.has(id))
164
+ id = `${id}_`;
165
+ return id || '_param';
166
+ }
22
167
  /**
23
168
  * Validate that a URL path template contains only safe characters.
24
169
  * Allows: alphanumeric, /, {, }, -, _, .
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAClG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAClF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,OAAO,GAAG,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,OAAO,GAAG,CAAC,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,GAAG;SACP,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;SACpB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC;AAE1E;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,OAAO,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC"}
1
+ {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export declare function privateHostsAllowed(): boolean;
2
+ /** Whether `ip` (a literal IPv4 or IPv6 address) is private, loopback, or reserved. */
3
+ export declare function isPrivateOrReservedIp(ip: string): boolean;
4
+ /**
5
+ * Throw if `urlString` is not a public http(s) URL. Resolves the host via DNS and
6
+ * rejects when any resolved address is private/loopback/link-local/reserved.
7
+ * Honors the `MCPMAKE_ALLOW_PRIVATE_HOSTS` escape hatch.
8
+ */
9
+ export declare function assertPublicUrl(urlString: string): Promise<void>;
10
+ //# sourceMappingURL=ssrf-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssrf-guard.d.ts","sourceRoot":"","sources":["../../src/utils/ssrf-guard.ts"],"names":[],"mappings":"AAyBA,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AA0CD,uFAAuF;AACvF,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAKzD;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CtE"}
@@ -0,0 +1,130 @@
1
+ import { isIP } from 'node:net';
2
+ import { lookup } from 'node:dns/promises';
3
+ /**
4
+ * SSRF guard for outbound requests driven by untrusted input — crawled links,
5
+ * recorded navigations, and remote OpenAPI spec/`$ref` URLs.
6
+ *
7
+ * The browser crawler/recorder and the spec loader all fetch URLs that originate
8
+ * from user input or from an LLM/page following links. Without a host check they
9
+ * can be steered at `localhost`, RFC1918 intranets, or the cloud metadata
10
+ * endpoint (`169.254.169.254`). This module resolves a URL's host and rejects
11
+ * any that points at a private, loopback, link-local, or otherwise reserved
12
+ * address.
13
+ *
14
+ * Residual limitation (documented, not closed here): this checks at request time
15
+ * but does not pin DNS at the socket, so a TOCTOU/DNS-rebinding attacker who
16
+ * flips a record between this lookup and the browser's own connect can still slip
17
+ * through. Full DNS-pinning would require intercepting at the socket layer.
18
+ *
19
+ * Escape hatch: set `MCPMAKE_ALLOW_PRIVATE_HOSTS=1` to allow private/loopback
20
+ * targets (local development against `localhost` test servers).
21
+ */
22
+ const ALLOW_ENV = 'MCPMAKE_ALLOW_PRIVATE_HOSTS';
23
+ export function privateHostsAllowed() {
24
+ return process.env[ALLOW_ENV] === '1' || process.env[ALLOW_ENV] === 'true';
25
+ }
26
+ /** Parse a dotted-quad IPv4 string into its four octets, or null if malformed. */
27
+ function ipv4Octets(ip) {
28
+ const parts = ip.split('.');
29
+ if (parts.length !== 4)
30
+ return null;
31
+ const nums = parts.map((p) => Number(p));
32
+ if (nums.some((n) => !Number.isInteger(n) || n < 0 || n > 255))
33
+ return null;
34
+ return nums;
35
+ }
36
+ function isPrivateIpv4(ip) {
37
+ const o = ipv4Octets(ip);
38
+ if (!o)
39
+ return false;
40
+ const [a, b] = o;
41
+ if (a === 0)
42
+ return true; // 0.0.0.0/8 "this network"
43
+ if (a === 10)
44
+ return true; // 10.0.0.0/8 private
45
+ if (a === 127)
46
+ return true; // 127.0.0.0/8 loopback
47
+ if (a === 169 && b === 254)
48
+ return true; // 169.254.0.0/16 link-local (cloud metadata)
49
+ if (a === 172 && b >= 16 && b <= 31)
50
+ return true; // 172.16.0.0/12 private
51
+ if (a === 192 && b === 168)
52
+ return true; // 192.168.0.0/16 private
53
+ if (a === 100 && b >= 64 && b <= 127)
54
+ return true; // 100.64.0.0/10 CGNAT
55
+ if (a === 192 && b === 0 && o[2] === 0)
56
+ return true; // 192.0.0.0/24 IETF protocol
57
+ if (a >= 224)
58
+ return true; // 224.0.0.0/4 multicast + 240.0.0.0/4 reserved + 255.* broadcast
59
+ return false;
60
+ }
61
+ function isPrivateIpv6(raw) {
62
+ const ip = raw.toLowerCase();
63
+ if (ip === '::1' || ip === '::')
64
+ return true; // loopback / unspecified
65
+ // IPv4-mapped (::ffff:a.b.c.d) and IPv4-compatible / NAT64 — re-check embedded v4.
66
+ const mapped = ip.match(/(?:::ffff:|::ffff:0:|64:ff9b::)(\d+\.\d+\.\d+\.\d+)$/);
67
+ if (mapped)
68
+ return isPrivateIpv4(mapped[1]);
69
+ if (ip.startsWith('fe8') || ip.startsWith('fe9') || ip.startsWith('fea') || ip.startsWith('feb'))
70
+ return true; // fe80::/10 link-local
71
+ if (ip.startsWith('fc') || ip.startsWith('fd'))
72
+ return true; // fc00::/7 unique-local
73
+ if (ip.startsWith('fec') || ip.startsWith('fed') || ip.startsWith('fee') || ip.startsWith('fef'))
74
+ return true; // fec0::/10 deprecated site-local
75
+ if (ip.startsWith('ff'))
76
+ return true; // ff00::/8 multicast
77
+ return false;
78
+ }
79
+ /** Whether `ip` (a literal IPv4 or IPv6 address) is private, loopback, or reserved. */
80
+ export function isPrivateOrReservedIp(ip) {
81
+ const kind = isIP(ip);
82
+ if (kind === 4)
83
+ return isPrivateIpv4(ip);
84
+ if (kind === 6)
85
+ return isPrivateIpv6(ip);
86
+ return false; // not an IP literal
87
+ }
88
+ /**
89
+ * Throw if `urlString` is not a public http(s) URL. Resolves the host via DNS and
90
+ * rejects when any resolved address is private/loopback/link-local/reserved.
91
+ * Honors the `MCPMAKE_ALLOW_PRIVATE_HOSTS` escape hatch.
92
+ */
93
+ export async function assertPublicUrl(urlString) {
94
+ let url;
95
+ try {
96
+ url = new URL(urlString);
97
+ }
98
+ catch {
99
+ throw new Error(`Invalid URL: ${urlString}`);
100
+ }
101
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
102
+ throw new Error(`Refusing non-http(s) URL: ${urlString}`);
103
+ }
104
+ if (privateHostsAllowed())
105
+ return;
106
+ // Strip IPv6 brackets from the hostname for literal checks.
107
+ const host = url.hostname.replace(/^\[/, '').replace(/\]$/, '');
108
+ // Literal IP host — check directly, no DNS needed.
109
+ if (isIP(host)) {
110
+ if (isPrivateOrReservedIp(host)) {
111
+ throw new Error(`Refusing to access private/reserved address ${host} (set ${ALLOW_ENV}=1 to allow).`);
112
+ }
113
+ return;
114
+ }
115
+ // Hostname — resolve every address it maps to and reject if ANY is private.
116
+ let addresses;
117
+ try {
118
+ addresses = await lookup(host, { all: true });
119
+ }
120
+ catch {
121
+ throw new Error(`Cannot resolve host "${host}" to verify it is public; refusing request.`);
122
+ }
123
+ for (const { address } of addresses) {
124
+ if (isPrivateOrReservedIp(address)) {
125
+ throw new Error(`Refusing request: host "${host}" resolves to private/reserved address ${address} ` +
126
+ `(set ${ALLOW_ENV}=1 to allow).`);
127
+ }
128
+ }
129
+ }
130
+ //# sourceMappingURL=ssrf-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssrf-guard.js","sourceRoot":"","sources":["../../src/utils/ssrf-guard.ts"],"names":[],"mappings":"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"}
@@ -4,9 +4,25 @@ export interface WatchOptions {
4
4
  onChange: () => Promise<void>;
5
5
  debounceMs?: number;
6
6
  }
7
+ /**
8
+ * Debounce + coalesce change notifications into regeneration runs.
9
+ *
10
+ * - Rapid bursts before a run are debounced into one run.
11
+ * - A change that arrives *while* a run is in flight sets a single `pending`
12
+ * flag, so exactly one trailing run fires afterwards. The flag (not a queue)
13
+ * caps the backlog at one regardless of how many events land mid-run, which
14
+ * avoids an unbounded backlog / memory growth.
15
+ *
16
+ * Exposed (and exported) separately from the fs binding so the coalescing logic
17
+ * is testable without relying on platform fs.watch event timing.
18
+ */
19
+ export declare function createChangeHandler(onChange: () => Promise<void>, debounceMs: number): {
20
+ notify: () => void;
21
+ };
7
22
  /**
8
23
  * Watch a file for changes and invoke a callback.
9
- * Debounces rapid successive changes (e.g., editor save + format).
24
+ * Debounces rapid successive changes (e.g., editor save + format) and coalesces
25
+ * changes that arrive during a regeneration into a single trailing run.
10
26
  */
11
27
  export declare function watchFile(options: WatchOptions): FSWatcher;
12
28
  //# sourceMappingURL=watcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/utils/watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAGhD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,CA4B1D"}
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/utils/watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAGhD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC7B,UAAU,EAAE,MAAM,GACjB;IAAE,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,CAmCxB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,CAa1D"}
@@ -1,34 +1,66 @@
1
1
  import { watch } from 'node:fs';
2
2
  import { logger } from './logger.js';
3
+ /**
4
+ * Debounce + coalesce change notifications into regeneration runs.
5
+ *
6
+ * - Rapid bursts before a run are debounced into one run.
7
+ * - A change that arrives *while* a run is in flight sets a single `pending`
8
+ * flag, so exactly one trailing run fires afterwards. The flag (not a queue)
9
+ * caps the backlog at one regardless of how many events land mid-run, which
10
+ * avoids an unbounded backlog / memory growth.
11
+ *
12
+ * Exposed (and exported) separately from the fs binding so the coalescing logic
13
+ * is testable without relying on platform fs.watch event timing.
14
+ */
15
+ export function createChangeHandler(onChange, debounceMs) {
16
+ let timer;
17
+ let running = false;
18
+ let pending = false;
19
+ async function regenerate() {
20
+ running = true;
21
+ try {
22
+ do {
23
+ pending = false;
24
+ logger.info('Spec file changed, regenerating...');
25
+ try {
26
+ await onChange();
27
+ logger.success('Regeneration complete');
28
+ }
29
+ catch (err) {
30
+ logger.error(`Regeneration failed: ${err instanceof Error ? err.message : err}`);
31
+ }
32
+ } while (pending);
33
+ }
34
+ finally {
35
+ running = false;
36
+ }
37
+ }
38
+ return {
39
+ notify() {
40
+ if (running) {
41
+ pending = true;
42
+ return;
43
+ }
44
+ if (timer)
45
+ clearTimeout(timer);
46
+ timer = setTimeout(() => {
47
+ void regenerate();
48
+ }, debounceMs);
49
+ },
50
+ };
51
+ }
3
52
  /**
4
53
  * Watch a file for changes and invoke a callback.
5
- * Debounces rapid successive changes (e.g., editor save + format).
54
+ * Debounces rapid successive changes (e.g., editor save + format) and coalesces
55
+ * changes that arrive during a regeneration into a single trailing run.
6
56
  */
7
57
  export function watchFile(options) {
8
58
  const debounceMs = options.debounceMs ?? 500;
9
- let timer;
10
- let running = false;
59
+ const handler = createChangeHandler(options.onChange, debounceMs);
11
60
  const watcher = watch(options.filePath, (eventType) => {
12
61
  if (eventType !== 'change')
13
62
  return;
14
- if (running)
15
- return;
16
- if (timer)
17
- clearTimeout(timer);
18
- timer = setTimeout(async () => {
19
- running = true;
20
- logger.info('Spec file changed, regenerating...');
21
- try {
22
- await options.onChange();
23
- logger.success('Regeneration complete');
24
- }
25
- catch (err) {
26
- logger.error(`Regeneration failed: ${err instanceof Error ? err.message : err}`);
27
- }
28
- finally {
29
- running = false;
30
- }
31
- }, debounceMs);
63
+ handler.notify();
32
64
  });
33
65
  logger.info(`Watching for changes: ${options.filePath}`);
34
66
  logger.info('Press Ctrl+C to stop');
@@ -1 +1 @@
1
- {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/utils/watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAkB,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,OAAqB;IAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC;IAC7C,IAAI,KAAgD,CAAC;IACrD,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,EAAE;QACpD,IAAI,SAAS,KAAK,QAAQ;YAAE,OAAO;QACnC,IAAI,OAAO;YAAE,OAAO;QAEpB,IAAI,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAC/B,KAAK,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;YAC5B,OAAO,GAAG,IAAI,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YAClD,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACzB,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;YAC1C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,KAAK,CAAC,wBAAwB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACnF,CAAC;oBAAS,CAAC;gBACT,OAAO,GAAG,KAAK,CAAC;YAClB,CAAC;QACH,CAAC,EAAE,UAAU,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,yBAAyB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzD,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAEpC,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/utils/watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAkB,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAA6B,EAC7B,UAAkB;IAElB,IAAI,KAAgD,CAAC;IACrD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,UAAU,UAAU;QACvB,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,CAAC;YACH,GAAG,CAAC;gBACF,OAAO,GAAG,KAAK,CAAC;gBAChB,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;gBAClD,IAAI,CAAC;oBACH,MAAM,QAAQ,EAAE,CAAC;oBACjB,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;gBAC1C,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,KAAK,CAAC,wBAAwB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;gBACnF,CAAC;YACH,CAAC,QAAQ,OAAO,EAAE;QACpB,CAAC;gBAAS,CAAC;YACT,OAAO,GAAG,KAAK,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM;YACJ,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,GAAG,IAAI,CAAC;gBACf,OAAO;YACT,CAAC;YACD,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YAC/B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBACtB,KAAK,UAAU,EAAE,CAAC;YACpB,CAAC,EAAE,UAAU,CAAC,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,OAAqB;IAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC;IAC7C,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAElE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,EAAE;QACpD,IAAI,SAAS,KAAK,QAAQ;YAAE,OAAO;QACnC,OAAO,CAAC,MAAM,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,yBAAyB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzD,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAEpC,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcpmake/core",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "Shared generation library for mcpmake — parse APIs (OpenAPI/HAR/Postman/websites) and emit MCP servers.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -35,6 +35,7 @@
35
35
  "handlebars": "^4.7.8",
36
36
  "json-schema-to-zod": "^2.4.1",
37
37
  "marked": "^12.0.2",
38
+ "openai": "^6.44.0",
38
39
  "openapi-types": "^12.1.3",
39
40
  "playwright": "^1.59.1",
40
41
  "yaml": "^2.7.0"