mcpmake 0.1.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 (344) hide show
  1. package/README.md +691 -0
  2. package/bin/mcpmake.mjs +2 -0
  3. package/dist/analyzer/auth-detector.d.ts +12 -0
  4. package/dist/analyzer/auth-detector.js +142 -0
  5. package/dist/analyzer/dom-parser.d.ts +10 -0
  6. package/dist/analyzer/dom-parser.js +259 -0
  7. package/dist/analyzer/goal-crawler.d.ts +25 -0
  8. package/dist/analyzer/goal-crawler.js +177 -0
  9. package/dist/analyzer/hybrid-detector.d.ts +28 -0
  10. package/dist/analyzer/hybrid-detector.js +96 -0
  11. package/dist/analyzer/index.d.ts +12 -0
  12. package/dist/analyzer/index.js +8 -0
  13. package/dist/analyzer/screenshot-capture.d.ts +29 -0
  14. package/dist/analyzer/screenshot-capture.js +42 -0
  15. package/dist/analyzer/selector-builder.d.ts +19 -0
  16. package/dist/analyzer/selector-builder.js +199 -0
  17. package/dist/analyzer/semantic-analyzer.d.ts +13 -0
  18. package/dist/analyzer/semantic-analyzer.js +145 -0
  19. package/dist/analyzer/site-crawler.d.ts +38 -0
  20. package/dist/analyzer/site-crawler.js +235 -0
  21. package/dist/cloud/billing/billing-engine.d.ts +44 -0
  22. package/dist/cloud/billing/billing-engine.js +81 -0
  23. package/dist/cloud/billing/credit-store.d.ts +64 -0
  24. package/dist/cloud/billing/credit-store.js +168 -0
  25. package/dist/cloud/billing/index.d.ts +4 -0
  26. package/dist/cloud/billing/index.js +2 -0
  27. package/dist/cloud/billing/usage-store.d.ts +42 -0
  28. package/dist/cloud/billing/usage-store.js +85 -0
  29. package/dist/cloud/billing/usage-tracker.d.ts +38 -0
  30. package/dist/cloud/billing/usage-tracker.js +95 -0
  31. package/dist/cloud/build-pipeline.d.ts +39 -0
  32. package/dist/cloud/build-pipeline.js +310 -0
  33. package/dist/cloud/build-queue.d.ts +30 -0
  34. package/dist/cloud/build-queue.js +70 -0
  35. package/dist/cloud/caddy-manager.d.ts +18 -0
  36. package/dist/cloud/caddy-manager.js +97 -0
  37. package/dist/cloud/container-backend.d.ts +62 -0
  38. package/dist/cloud/container-backend.js +59 -0
  39. package/dist/cloud/container-manager.d.ts +64 -0
  40. package/dist/cloud/container-manager.js +301 -0
  41. package/dist/cloud/crypto.d.ts +27 -0
  42. package/dist/cloud/crypto.js +63 -0
  43. package/dist/cloud/db/index.d.ts +27 -0
  44. package/dist/cloud/db/index.js +53 -0
  45. package/dist/cloud/db/migrations.d.ts +12 -0
  46. package/dist/cloud/db/migrations.js +329 -0
  47. package/dist/cloud/db/pg-store.d.ts +45 -0
  48. package/dist/cloud/db/pg-store.js +336 -0
  49. package/dist/cloud/failure-tracker.d.ts +51 -0
  50. package/dist/cloud/failure-tracker.js +102 -0
  51. package/dist/cloud/idle-monitor.d.ts +30 -0
  52. package/dist/cloud/idle-monitor.js +70 -0
  53. package/dist/cloud/mailer.d.ts +21 -0
  54. package/dist/cloud/mailer.js +193 -0
  55. package/dist/cloud/mcp-proxy.d.ts +58 -0
  56. package/dist/cloud/mcp-proxy.js +203 -0
  57. package/dist/cloud/metric-samples.d.ts +43 -0
  58. package/dist/cloud/metric-samples.js +85 -0
  59. package/dist/cloud/metrics.d.ts +26 -0
  60. package/dist/cloud/metrics.js +59 -0
  61. package/dist/cloud/multipart.d.ts +26 -0
  62. package/dist/cloud/multipart.js +132 -0
  63. package/dist/cloud/observability.d.ts +27 -0
  64. package/dist/cloud/observability.js +98 -0
  65. package/dist/cloud/rate-limiter.d.ts +31 -0
  66. package/dist/cloud/rate-limiter.js +58 -0
  67. package/dist/cloud/request-security.d.ts +5 -0
  68. package/dist/cloud/request-security.js +74 -0
  69. package/dist/cloud/resource-monitor.d.ts +69 -0
  70. package/dist/cloud/resource-monitor.js +130 -0
  71. package/dist/cloud/secret-store.d.ts +38 -0
  72. package/dist/cloud/secret-store.js +103 -0
  73. package/dist/cloud/security.d.ts +26 -0
  74. package/dist/cloud/security.js +142 -0
  75. package/dist/cloud/server.d.ts +21 -0
  76. package/dist/cloud/server.js +1079 -0
  77. package/dist/cloud/shared-state.d.ts +72 -0
  78. package/dist/cloud/shared-state.js +159 -0
  79. package/dist/cloud/ssrf.d.ts +43 -0
  80. package/dist/cloud/ssrf.js +150 -0
  81. package/dist/cloud/store.d.ts +41 -0
  82. package/dist/cloud/store.js +75 -0
  83. package/dist/cloud/stripe.d.ts +78 -0
  84. package/dist/cloud/stripe.js +317 -0
  85. package/dist/cloud/telemetry-store.d.ts +53 -0
  86. package/dist/cloud/telemetry-store.js +108 -0
  87. package/dist/cloud/web/auth.d.ts +225 -0
  88. package/dist/cloud/web/auth.js +555 -0
  89. package/dist/cloud/web/charts.d.ts +70 -0
  90. package/dist/cloud/web/charts.js +178 -0
  91. package/dist/cloud/web/csrf.d.ts +14 -0
  92. package/dist/cloud/web/csrf.js +22 -0
  93. package/dist/cloud/web/docs.d.ts +40 -0
  94. package/dist/cloud/web/docs.js +174 -0
  95. package/dist/cloud/web/router.d.ts +25 -0
  96. package/dist/cloud/web/router.js +1921 -0
  97. package/dist/cloud/web/static/alpine.min.js +5 -0
  98. package/dist/cloud/web/static/favicon.svg +4 -0
  99. package/dist/cloud/web/static/htmx-sse.js +290 -0
  100. package/dist/cloud/web/static/htmx.min.js +1 -0
  101. package/dist/cloud/web/static/style.css +2683 -0
  102. package/dist/cloud/web/static-server.d.ts +13 -0
  103. package/dist/cloud/web/static-server.js +73 -0
  104. package/dist/cloud/web/template-engine.d.ts +27 -0
  105. package/dist/cloud/web/template-engine.js +146 -0
  106. package/dist/cloud/web/templates/layouts/admin.hbs +57 -0
  107. package/dist/cloud/web/templates/layouts/auth.hbs +138 -0
  108. package/dist/cloud/web/templates/layouts/base.hbs +16 -0
  109. package/dist/cloud/web/templates/layouts/dashboard.hbs +39 -0
  110. package/dist/cloud/web/templates/layouts/landing.hbs +82 -0
  111. package/dist/cloud/web/templates/pages/admin/overview.hbs +123 -0
  112. package/dist/cloud/web/templates/pages/admin/servers.hbs +129 -0
  113. package/dist/cloud/web/templates/pages/admin/telemetry.hbs +39 -0
  114. package/dist/cloud/web/templates/pages/admin/user-edit.hbs +91 -0
  115. package/dist/cloud/web/templates/pages/admin/users.hbs +179 -0
  116. package/dist/cloud/web/templates/pages/auth/forgot-password.hbs +25 -0
  117. package/dist/cloud/web/templates/pages/auth/login.hbs +33 -0
  118. package/dist/cloud/web/templates/pages/auth/register.hbs +32 -0
  119. package/dist/cloud/web/templates/pages/auth/reset-password.hbs +34 -0
  120. package/dist/cloud/web/templates/pages/dashboard/billing.hbs +140 -0
  121. package/dist/cloud/web/templates/pages/dashboard/create.hbs +173 -0
  122. package/dist/cloud/web/templates/pages/dashboard/index.hbs +8 -0
  123. package/dist/cloud/web/templates/pages/dashboard/server-detail.hbs +280 -0
  124. package/dist/cloud/web/templates/pages/dashboard/server-logs.hbs +35 -0
  125. package/dist/cloud/web/templates/pages/dashboard/server-metrics.hbs +63 -0
  126. package/dist/cloud/web/templates/pages/dashboard/servers-partial.hbs +21 -0
  127. package/dist/cloud/web/templates/pages/dashboard/servers.hbs +44 -0
  128. package/dist/cloud/web/templates/pages/docs/show.hbs +16 -0
  129. package/dist/cloud/web/templates/pages/errors/404.hbs +9 -0
  130. package/dist/cloud/web/templates/pages/errors/500.hbs +8 -0
  131. package/dist/cloud/web/templates/pages/landing/index.hbs +223 -0
  132. package/dist/cloud/web/templates/pages/legal/privacy.hbs +71 -0
  133. package/dist/cloud/web/templates/pages/legal/terms.hbs +73 -0
  134. package/dist/cloud/web/templates/partials/admin-stats.hbs +52 -0
  135. package/dist/cloud/web/templates/partials/flash-message.hbs +6 -0
  136. package/dist/cloud/web/templates/partials/pricing-table.hbs +103 -0
  137. package/dist/cloud/web/templates/partials/server-card.hbs +19 -0
  138. package/dist/cloud/web/templates/partials/status-badge.hbs +1 -0
  139. package/dist/commands/bundle.d.ts +18 -0
  140. package/dist/commands/bundle.js +82 -0
  141. package/dist/commands/ci.d.ts +25 -0
  142. package/dist/commands/ci.js +149 -0
  143. package/dist/commands/deploy.d.ts +24 -0
  144. package/dist/commands/deploy.js +145 -0
  145. package/dist/commands/diff.d.ts +18 -0
  146. package/dist/commands/diff.js +185 -0
  147. package/dist/commands/from/describe.d.ts +65 -0
  148. package/dist/commands/from/describe.js +173 -0
  149. package/dist/commands/from/har.d.ts +81 -0
  150. package/dist/commands/from/har.js +255 -0
  151. package/dist/commands/from/openapi.d.ts +105 -0
  152. package/dist/commands/from/openapi.js +302 -0
  153. package/dist/commands/from/postman.d.ts +51 -0
  154. package/dist/commands/from/postman.js +146 -0
  155. package/dist/commands/from/target-support.d.ts +11 -0
  156. package/dist/commands/from/target-support.js +33 -0
  157. package/dist/commands/from/url.d.ts +75 -0
  158. package/dist/commands/from/url.js +244 -0
  159. package/dist/commands/from/website.d.ts +75 -0
  160. package/dist/commands/from/website.js +284 -0
  161. package/dist/commands/lint.d.ts +24 -0
  162. package/dist/commands/lint.js +184 -0
  163. package/dist/commands/merge.d.ts +18 -0
  164. package/dist/commands/merge.js +161 -0
  165. package/dist/commands/publish.d.ts +27 -0
  166. package/dist/commands/publish.js +334 -0
  167. package/dist/commands/rescan.d.ts +40 -0
  168. package/dist/commands/rescan.js +255 -0
  169. package/dist/commands/update.d.ts +14 -0
  170. package/dist/commands/update.js +87 -0
  171. package/dist/commands/verify.d.ts +14 -0
  172. package/dist/commands/verify.js +71 -0
  173. package/dist/config/configurable-command.d.ts +13 -0
  174. package/dist/config/configurable-command.js +70 -0
  175. package/dist/config/mcpmake-config.d.ts +68 -0
  176. package/dist/config/mcpmake-config.js +207 -0
  177. package/dist/docs/cli.md +400 -0
  178. package/dist/docs/mcp-2026-07-28-migration.md +78 -0
  179. package/dist/docs/migrate-from-stainless.md +94 -0
  180. package/dist/docs/quickstart.md +166 -0
  181. package/dist/docs/show-hn.md +26 -0
  182. package/dist/docs/website-servers.md +169 -0
  183. package/dist/emitter/code-writer.d.ts +8 -0
  184. package/dist/emitter/code-writer.js +25 -0
  185. package/dist/emitter/index.d.ts +32 -0
  186. package/dist/emitter/index.js +280 -0
  187. package/dist/emitter/mcpb-bundler.d.ts +31 -0
  188. package/dist/emitter/mcpb-bundler.js +172 -0
  189. package/dist/emitter/project-scaffolder.d.ts +4 -0
  190. package/dist/emitter/project-scaffolder.js +89 -0
  191. package/dist/emitter/python-template-loader.d.ts +4 -0
  192. package/dist/emitter/python-template-loader.js +30 -0
  193. package/dist/emitter/python-templates/dockerfile.hbs +14 -0
  194. package/dist/emitter/python-templates/env.example.hbs +6 -0
  195. package/dist/emitter/python-templates/requirements.txt.hbs +4 -0
  196. package/dist/emitter/python-templates/server.py.hbs +77 -0
  197. package/dist/emitter/site-scaffolder.d.ts +13 -0
  198. package/dist/emitter/site-scaffolder.js +70 -0
  199. package/dist/emitter/site-template-loader.d.ts +5 -0
  200. package/dist/emitter/site-template-loader.js +47 -0
  201. package/dist/emitter/site-templates/browser-manager.ts.hbs +233 -0
  202. package/dist/emitter/site-templates/config.ts.hbs +28 -0
  203. package/dist/emitter/site-templates/dockerfile.hbs +31 -0
  204. package/dist/emitter/site-templates/env.example.hbs +19 -0
  205. package/dist/emitter/site-templates/package.json.hbs +26 -0
  206. package/dist/emitter/site-templates/server-main-http.ts.hbs +108 -0
  207. package/dist/emitter/site-templates/server-main.ts.hbs +23 -0
  208. package/dist/emitter/site-templates/tool-handler-action.ts.hbs +86 -0
  209. package/dist/emitter/site-templates/tool-handler-form.ts.hbs +116 -0
  210. package/dist/emitter/site-templates/tool-handler-lifecycle.ts.hbs +146 -0
  211. package/dist/emitter/site-templates/tool-index.ts.hbs +11 -0
  212. package/dist/emitter/template-loader.d.ts +1 -0
  213. package/dist/emitter/template-loader.js +27 -0
  214. package/dist/emitter/templates/auth-provider.ts.hbs +57 -0
  215. package/dist/emitter/templates/config.ts.hbs +63 -0
  216. package/dist/emitter/templates/discovery.ts.hbs +301 -0
  217. package/dist/emitter/templates/dockerfile.hbs +34 -0
  218. package/dist/emitter/templates/env.example.hbs +28 -0
  219. package/dist/emitter/templates/gitignore.hbs +5 -0
  220. package/dist/emitter/templates/http-executor.ts.hbs +117 -0
  221. package/dist/emitter/templates/oauth.ts.hbs +188 -0
  222. package/dist/emitter/templates/package.json.hbs +25 -0
  223. package/dist/emitter/templates/prompts.ts.hbs +22 -0
  224. package/dist/emitter/templates/readme.md.hbs +123 -0
  225. package/dist/emitter/templates/resources.ts.hbs +63 -0
  226. package/dist/emitter/templates/server-main-http.ts.hbs +407 -0
  227. package/dist/emitter/templates/server-main.ts.hbs +40 -0
  228. package/dist/emitter/templates/task-handlers.ts.hbs +189 -0
  229. package/dist/emitter/templates/task-manager.ts.hbs +139 -0
  230. package/dist/emitter/templates/task-sse.ts.hbs +105 -0
  231. package/dist/emitter/templates/tool-handler.ts.hbs +124 -0
  232. package/dist/emitter/templates/tool-index.ts.hbs +11 -0
  233. package/dist/emitter/templates/tool-test.ts.hbs +57 -0
  234. package/dist/emitter/templates/trace.ts.hbs +79 -0
  235. package/dist/emitter/templates/tsconfig.json.hbs +16 -0
  236. package/dist/emitter/templates/types.ts.hbs +5 -0
  237. package/dist/emitter/worker-template-loader.d.ts +5 -0
  238. package/dist/emitter/worker-template-loader.js +33 -0
  239. package/dist/emitter/worker-templates/config.ts.hbs +54 -0
  240. package/dist/emitter/worker-templates/dev-vars.example.hbs +10 -0
  241. package/dist/emitter/worker-templates/gitignore.hbs +6 -0
  242. package/dist/emitter/worker-templates/package.json.hbs +24 -0
  243. package/dist/emitter/worker-templates/readme.md.hbs +53 -0
  244. package/dist/emitter/worker-templates/server.test.ts.hbs +20 -0
  245. package/dist/emitter/worker-templates/tool-handler.ts.hbs +85 -0
  246. package/dist/emitter/worker-templates/tool-index.ts.hbs +28 -0
  247. package/dist/emitter/worker-templates/tsconfig.json.hbs +17 -0
  248. package/dist/emitter/worker-templates/worker.ts.hbs +242 -0
  249. package/dist/emitter/worker-templates/wrangler.toml.hbs +19 -0
  250. package/dist/generator/spec-generator.d.ts +6 -0
  251. package/dist/generator/spec-generator.js +50 -0
  252. package/dist/index.d.ts +1 -0
  253. package/dist/index.js +64 -0
  254. package/dist/parser/har-filter.d.ts +8 -0
  255. package/dist/parser/har-filter.js +71 -0
  256. package/dist/parser/har-loader.d.ts +2 -0
  257. package/dist/parser/har-loader.js +14 -0
  258. package/dist/parser/har-normalizer.d.ts +20 -0
  259. package/dist/parser/har-normalizer.js +78 -0
  260. package/dist/parser/index.d.ts +10 -0
  261. package/dist/parser/index.js +6 -0
  262. package/dist/parser/openapi-loader.d.ts +6 -0
  263. package/dist/parser/openapi-loader.js +308 -0
  264. package/dist/parser/operation-extractor.d.ts +13 -0
  265. package/dist/parser/operation-extractor.js +155 -0
  266. package/dist/parser/overlay-loader.d.ts +10 -0
  267. package/dist/parser/overlay-loader.js +184 -0
  268. package/dist/parser/postman-loader.d.ts +9 -0
  269. package/dist/parser/postman-loader.js +106 -0
  270. package/dist/parser/schema-converter.d.ts +12 -0
  271. package/dist/parser/schema-converter.js +117 -0
  272. package/dist/plugins/adapter.d.ts +40 -0
  273. package/dist/plugins/adapter.js +15 -0
  274. package/dist/plugins/loader.d.ts +25 -0
  275. package/dist/plugins/loader.js +58 -0
  276. package/dist/pricing.d.ts +55 -0
  277. package/dist/pricing.js +133 -0
  278. package/dist/providers/index.d.ts +15 -0
  279. package/dist/providers/index.js +56 -0
  280. package/dist/recorder/browser-recorder.d.ts +22 -0
  281. package/dist/recorder/browser-recorder.js +205 -0
  282. package/dist/registry/official-registry.d.ts +90 -0
  283. package/dist/registry/official-registry.js +129 -0
  284. package/dist/rescan/diff-engine.d.ts +5 -0
  285. package/dist/rescan/diff-engine.js +312 -0
  286. package/dist/rescan/index.d.ts +3 -0
  287. package/dist/rescan/index.js +2 -0
  288. package/dist/rescan/rescan-runner.d.ts +42 -0
  289. package/dist/rescan/rescan-runner.js +69 -0
  290. package/dist/rescan/rescan-scheduler.d.ts +41 -0
  291. package/dist/rescan/rescan-scheduler.js +179 -0
  292. package/dist/site-transformer/browser-tools.d.ts +10 -0
  293. package/dist/site-transformer/browser-tools.js +59 -0
  294. package/dist/site-transformer/index.d.ts +2 -0
  295. package/dist/site-transformer/index.js +2 -0
  296. package/dist/site-transformer/selector-healer.d.ts +8 -0
  297. package/dist/site-transformer/selector-healer.js +106 -0
  298. package/dist/site-transformer/tool-generator.d.ts +13 -0
  299. package/dist/site-transformer/tool-generator.js +245 -0
  300. package/dist/transformer/auth-detector.d.ts +13 -0
  301. package/dist/transformer/auth-detector.js +90 -0
  302. package/dist/transformer/catalog-builder.d.ts +18 -0
  303. package/dist/transformer/catalog-builder.js +56 -0
  304. package/dist/transformer/client-compat.d.ts +6 -0
  305. package/dist/transformer/client-compat.js +44 -0
  306. package/dist/transformer/har-clusterer.d.ts +9 -0
  307. package/dist/transformer/har-clusterer.js +27 -0
  308. package/dist/transformer/har-dedup.d.ts +10 -0
  309. package/dist/transformer/har-dedup.js +81 -0
  310. package/dist/transformer/har-schema-inferrer.d.ts +15 -0
  311. package/dist/transformer/har-schema-inferrer.js +90 -0
  312. package/dist/transformer/har-to-operations.d.ts +13 -0
  313. package/dist/transformer/har-to-operations.js +192 -0
  314. package/dist/transformer/index.d.ts +8 -0
  315. package/dist/transformer/index.js +6 -0
  316. package/dist/transformer/llm-namer.d.ts +6 -0
  317. package/dist/transformer/llm-namer.js +59 -0
  318. package/dist/transformer/naming.d.ts +4 -0
  319. package/dist/transformer/naming.js +30 -0
  320. package/dist/transformer/operation-filter.d.ts +13 -0
  321. package/dist/transformer/operation-filter.js +52 -0
  322. package/dist/transformer/resource-builder.d.ts +12 -0
  323. package/dist/transformer/resource-builder.js +80 -0
  324. package/dist/transformer/schema-merger.d.ts +14 -0
  325. package/dist/transformer/schema-merger.js +65 -0
  326. package/dist/transformer/tool-builder.d.ts +3 -0
  327. package/dist/transformer/tool-builder.js +114 -0
  328. package/dist/types/index.d.ts +131 -0
  329. package/dist/types/index.js +1 -0
  330. package/dist/types/site.d.ts +284 -0
  331. package/dist/types/site.js +8 -0
  332. package/dist/utils/fail.d.ts +48 -0
  333. package/dist/utils/fail.js +204 -0
  334. package/dist/utils/fs.d.ts +5 -0
  335. package/dist/utils/fs.js +28 -0
  336. package/dist/utils/interactive.d.ts +6 -0
  337. package/dist/utils/interactive.js +30 -0
  338. package/dist/utils/logger.d.ts +1 -0
  339. package/dist/utils/logger.js +2 -0
  340. package/dist/utils/sanitize.d.ts +28 -0
  341. package/dist/utils/sanitize.js +44 -0
  342. package/dist/utils/watcher.d.ts +11 -0
  343. package/dist/utils/watcher.js +36 -0
  344. package/package.json +65 -0
@@ -0,0 +1,280 @@
1
+ import { writeCodeUnits } from './code-writer.js';
2
+ import { scaffoldProjectFiles, scaffoldSharedModules } from './project-scaffolder.js';
3
+ import { scaffoldSiteProjectFiles, scaffoldSiteSharedModules } from './site-scaffolder.js';
4
+ import { renderTemplate } from './template-loader.js';
5
+ import { renderSiteTemplate } from './site-template-loader.js';
6
+ import { renderPythonTemplate } from './python-template-loader.js';
7
+ import { renderWorkerTemplate } from './worker-template-loader.js';
8
+ import { buildCatalog } from '../transformer/catalog-builder.js';
9
+ import { logger } from '../utils/logger.js';
10
+ const SAFE_VERSION_RE = /^[0-9a-zA-Z._+-]{1,50}$/;
11
+ export async function emitProject(manifest, options) {
12
+ // Cloudflare Workers target uses a separate (stateless Fetch-handler) pipeline.
13
+ if (manifest.target === 'cloudflare') {
14
+ return emitWorkerProject(manifest, options);
15
+ }
16
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(manifest.serverName)) {
17
+ throw new Error(`Invalid server name: "${manifest.serverName}". Must match /^[a-z0-9][a-z0-9._-]*$/`);
18
+ }
19
+ if (!SAFE_VERSION_RE.test(manifest.serverVersion)) {
20
+ logger.warn(`Unsafe server version "${manifest.serverVersion}" sanitized to "0.0.0"`);
21
+ manifest = { ...manifest, serverVersion: '0.0.0' };
22
+ }
23
+ const units = [];
24
+ // Project skeleton
25
+ units.push(...scaffoldProjectFiles(manifest));
26
+ // Shared source modules
27
+ units.push(...scaffoldSharedModules(manifest));
28
+ // Task manager (if any async operations detected)
29
+ const hasAsyncTools = manifest.tools.some((t) => t.isAsync);
30
+ if (hasAsyncTools) {
31
+ units.push({
32
+ filePath: 'src/task-manager.ts',
33
+ content: renderTemplate('task-manager.ts', manifest),
34
+ });
35
+ units.push({
36
+ filePath: 'src/task-handlers.ts',
37
+ content: renderTemplate('task-handlers.ts', manifest),
38
+ });
39
+ units.push({
40
+ filePath: 'src/task-sse.ts',
41
+ content: renderTemplate('task-sse.ts', manifest),
42
+ });
43
+ }
44
+ // OAuth module (if OAuth2 auth detected)
45
+ const hasOAuth = manifest.authSchemes.some((s) => s.type === 'oauth2');
46
+ if (hasOAuth) {
47
+ units.push({
48
+ filePath: 'src/oauth.ts',
49
+ content: renderTemplate('oauth.ts', manifest),
50
+ });
51
+ }
52
+ if (manifest.dynamicDiscovery) {
53
+ // Dynamic discovery mode: emit catalog + discovery meta-tools
54
+ const catalog = buildCatalog(manifest.tools);
55
+ units.push({
56
+ filePath: 'src/tool-catalog.json',
57
+ content: JSON.stringify(catalog, null, 2),
58
+ });
59
+ units.push({
60
+ filePath: 'src/discovery.ts',
61
+ content: renderTemplate('discovery.ts', manifest),
62
+ });
63
+ // Hybrid mode: also register first N tools statically
64
+ const staticCount = manifest.staticToolCount ?? 0;
65
+ const staticTools = manifest.tools.slice(0, staticCount);
66
+ if (staticTools.length > 0) {
67
+ for (const tool of staticTools) {
68
+ units.push({
69
+ filePath: `src/tools/${tool.fileName}.ts`,
70
+ content: renderTemplate('tool-handler.ts', tool),
71
+ });
72
+ }
73
+ units.push({
74
+ filePath: 'src/tools/index.ts',
75
+ content: renderTemplate('tool-index.ts', { tools: staticTools }),
76
+ });
77
+ }
78
+ logger.info(`Dynamic discovery: ${catalog.length} tools in catalog` +
79
+ (staticTools.length > 0 ? `, ${staticTools.length} static` : ''));
80
+ }
81
+ else {
82
+ // Standard mode: emit all tools individually
83
+ for (const tool of manifest.tools) {
84
+ units.push({
85
+ filePath: `src/tools/${tool.fileName}.ts`,
86
+ content: renderTemplate('tool-handler.ts', tool),
87
+ });
88
+ }
89
+ units.push({
90
+ filePath: 'src/tools/index.ts',
91
+ content: renderTemplate('tool-index.ts', { tools: manifest.tools }),
92
+ });
93
+ }
94
+ // Resources file (if any GET list endpoints exist)
95
+ if (manifest.resources && manifest.resources.length > 0) {
96
+ units.push({
97
+ filePath: 'src/resources.ts',
98
+ content: renderTemplate('resources.ts', { resources: manifest.resources }),
99
+ });
100
+ }
101
+ // Prompts file (if any)
102
+ if (manifest.prompts && manifest.prompts.length > 0) {
103
+ units.push({
104
+ filePath: 'src/prompts.ts',
105
+ content: renderTemplate('prompts.ts', { prompts: manifest.prompts }),
106
+ });
107
+ }
108
+ // Test files for each tool
109
+ for (const tool of manifest.tools) {
110
+ units.push({
111
+ filePath: `test/tools/${tool.fileName}.test.ts`,
112
+ content: renderTemplate('tool-test.ts', tool),
113
+ });
114
+ }
115
+ logger.info(`Writing ${units.length} files to ${options.outputDir}`);
116
+ await writeCodeUnits(units, options.outputDir, options);
117
+ }
118
+ /**
119
+ * Emit a Cloudflare Workers MCP server project (the `--target cloudflare` path).
120
+ *
121
+ * Workers is stateless and has no node:http server, so the SDK's
122
+ * StreamableHTTPServerTransport cannot run there. The entry (`src/index.ts`) is a
123
+ * hand-rolled JSON-RPC Fetch handler instead. The runtime-agnostic modules
124
+ * (`http.ts`, `auth.ts`, `trace.ts`) are reused verbatim from the Node templates —
125
+ * they only need `nodejs_compat`, which `wrangler.toml` enables.
126
+ *
127
+ * v1 scope is tools + initialize/ping/server-discover + bearer auth. Resources,
128
+ * prompts, the Tasks extension and OAuth2 outbound auth are not emitted for this
129
+ * target (a warning is logged and the user is pointed at `--target node`).
130
+ */
131
+ export async function emitWorkerProject(manifest, options) {
132
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(manifest.serverName)) {
133
+ throw new Error(`Invalid server name: "${manifest.serverName}". Must match /^[a-z0-9][a-z0-9._-]*$/`);
134
+ }
135
+ if (!SAFE_VERSION_RE.test(manifest.serverVersion)) {
136
+ logger.warn(`Unsafe server version "${manifest.serverVersion}" sanitized to "0.0.0"`);
137
+ manifest = { ...manifest, serverVersion: '0.0.0' };
138
+ }
139
+ // Warn about features the Workers target does not emit (so nothing is silently
140
+ // dropped). Tools — the dominant case — are always fully emitted.
141
+ if ((manifest.resources?.length ?? 0) > 0 || (manifest.prompts?.length ?? 0) > 0) {
142
+ logger.warn(`Cloudflare target emits tools only — ${manifest.resources?.length ?? 0} resource(s) ` +
143
+ `and ${manifest.prompts?.length ?? 0} prompt(s) omitted. Use --target node to include them.`);
144
+ }
145
+ if (manifest.tools.some((t) => t.isAsync)) {
146
+ logger.warn('Cloudflare target: async tools run synchronously within the request (no Tasks extension / background polling).');
147
+ }
148
+ if (manifest.dynamicDiscovery) {
149
+ logger.warn('Cloudflare target does not support --dynamic-discovery; emitting all tools individually.');
150
+ }
151
+ const workerAuthSchemes = manifest.authSchemes.filter((s) => s.type !== 'oauth2');
152
+ if (workerAuthSchemes.length !== manifest.authSchemes.length) {
153
+ logger.warn('Cloudflare target: OAuth2 outbound auth is omitted (apiKey / bearer / basic are supported). Use --target node for OAuth2.');
154
+ }
155
+ const authEnvVars = manifest.envVars.filter((v) => v.name !== 'BASE_URL');
156
+ // The reused auth + config modules must see the filtered (no-oauth2) schemes so
157
+ // they never reference an un-emitted oauth.ts.
158
+ const templateData = {
159
+ ...manifest,
160
+ authSchemes: workerAuthSchemes,
161
+ authEnvVars,
162
+ hasOAuth: false,
163
+ };
164
+ const units = [
165
+ // Worker-specific files.
166
+ { filePath: 'src/index.ts', content: renderWorkerTemplate('worker.ts', templateData) },
167
+ { filePath: 'src/config.ts', content: renderWorkerTemplate('config.ts', templateData) },
168
+ { filePath: 'package.json', content: renderWorkerTemplate('package.json', templateData) },
169
+ { filePath: 'tsconfig.json', content: renderWorkerTemplate('tsconfig.json', templateData) },
170
+ { filePath: 'wrangler.toml', content: renderWorkerTemplate('wrangler.toml', templateData) },
171
+ { filePath: 'README.md', content: renderWorkerTemplate('readme.md', templateData) },
172
+ {
173
+ filePath: '.dev.vars.example',
174
+ content: renderWorkerTemplate('dev-vars.example', templateData),
175
+ },
176
+ { filePath: '.gitignore', content: renderWorkerTemplate('gitignore', templateData) },
177
+ // Runtime-agnostic modules reused from the Node templates (fetch / Buffer /
178
+ // AsyncLocalStorage — all covered by nodejs_compat).
179
+ { filePath: 'src/http.ts', content: renderTemplate('http-executor.ts', templateData) },
180
+ { filePath: 'src/auth.ts', content: renderTemplate('auth-provider.ts', templateData) },
181
+ { filePath: 'src/trace.ts', content: renderTemplate('trace.ts', templateData) },
182
+ ];
183
+ for (const tool of manifest.tools) {
184
+ units.push({
185
+ filePath: `src/tools/${tool.fileName}.ts`,
186
+ content: renderWorkerTemplate('tool-handler.ts', tool),
187
+ });
188
+ }
189
+ units.push({
190
+ filePath: 'src/tools/index.ts',
191
+ content: renderWorkerTemplate('tool-index.ts', { tools: manifest.tools }),
192
+ });
193
+ units.push({
194
+ filePath: 'test/server.test.ts',
195
+ content: renderWorkerTemplate('server.test.ts', templateData),
196
+ });
197
+ logger.info(`Writing ${units.length} Workers files to ${options.outputDir}`);
198
+ await writeCodeUnits(units, options.outputDir, options);
199
+ }
200
+ /**
201
+ * Emit a Playwright-based MCP server project from a SiteProjectManifest.
202
+ * Parallel to emitProject() but uses site-specific templates.
203
+ */
204
+ export async function emitSiteProject(manifest, options) {
205
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(manifest.serverName)) {
206
+ throw new Error(`Invalid server name: "${manifest.serverName}". Must match /^[a-z0-9][a-z0-9._-]*$/`);
207
+ }
208
+ if (!SAFE_VERSION_RE.test(manifest.serverVersion)) {
209
+ logger.warn(`Unsafe server version "${manifest.serverVersion}" sanitized to "0.0.0"`);
210
+ manifest = { ...manifest, serverVersion: '0.0.0' };
211
+ }
212
+ const units = [];
213
+ // Project skeleton (package.json, tsconfig, Dockerfile, .env.example, .gitignore)
214
+ units.push(...scaffoldSiteProjectFiles(manifest));
215
+ // Shared source modules (server entry, config, browser-manager, site-descriptor.json)
216
+ units.push(...scaffoldSiteSharedModules(manifest));
217
+ // Tool files — choose template based on tool type
218
+ for (const tool of manifest.tools) {
219
+ const templateName = getSiteToolTemplate(tool);
220
+ units.push({
221
+ filePath: `src/tools/${tool.fileName}.ts`,
222
+ content: renderSiteTemplate(templateName, tool),
223
+ });
224
+ }
225
+ // Tool index (registers all tools)
226
+ units.push({
227
+ filePath: 'src/tools/index.ts',
228
+ content: renderSiteTemplate('tool-index.ts', { tools: manifest.tools }),
229
+ });
230
+ logger.info(`Writing ${units.length} files to ${options.outputDir}`);
231
+ await writeCodeUnits(units, options.outputDir, options);
232
+ }
233
+ /** Pick the right template for a site tool based on its type. */
234
+ function getSiteToolTemplate(tool) {
235
+ switch (tool.toolType) {
236
+ case 'browser-lifecycle':
237
+ return 'tool-handler-lifecycle.ts';
238
+ case 'page-action':
239
+ return 'tool-handler-form.ts';
240
+ case 'element-action':
241
+ case 'navigation':
242
+ return 'tool-handler-action.ts';
243
+ default:
244
+ return 'tool-handler-action.ts';
245
+ }
246
+ }
247
+ /**
248
+ * Emit a Python MCP server project.
249
+ * Generates a single server.py file with all tools, plus requirements.txt.
250
+ */
251
+ export async function emitPythonProject(manifest, options) {
252
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(manifest.serverName)) {
253
+ throw new Error(`Invalid server name: "${manifest.serverName}". Must match /^[a-z0-9][a-z0-9._-]*$/`);
254
+ }
255
+ const units = [];
256
+ const templateData = {
257
+ ...manifest,
258
+ authEnvVars: manifest.envVars.filter((v) => v.name !== 'BASE_URL'),
259
+ };
260
+ units.push({
261
+ filePath: 'server.py',
262
+ content: renderPythonTemplate('server.py', templateData),
263
+ });
264
+ units.push({
265
+ filePath: 'requirements.txt',
266
+ content: renderPythonTemplate('requirements.txt', manifest),
267
+ });
268
+ units.push({
269
+ filePath: '.env.example',
270
+ content: renderPythonTemplate('env.example', templateData),
271
+ });
272
+ if (manifest.transport === 'http') {
273
+ units.push({
274
+ filePath: 'Dockerfile',
275
+ content: renderPythonTemplate('dockerfile', manifest),
276
+ });
277
+ }
278
+ logger.info(`Writing ${units.length} Python files to ${options.outputDir}`);
279
+ await writeCodeUnits(units, options.outputDir, options);
280
+ }
@@ -0,0 +1,31 @@
1
+ export interface McpbManifest {
2
+ schema_version: string;
3
+ name: string;
4
+ version: string;
5
+ description: string;
6
+ author: string;
7
+ license: string;
8
+ runtime: string;
9
+ entry_point: string;
10
+ tools: {
11
+ name: string;
12
+ description: string;
13
+ }[];
14
+ env_vars: {
15
+ name: string;
16
+ description: string;
17
+ required: boolean;
18
+ }[];
19
+ }
20
+ /**
21
+ * Generate an .mcpb bundle from a built MCP server project.
22
+ *
23
+ * The bundle is a zip file containing:
24
+ * manifest.json - metadata, tool list, config schema
25
+ * server/ - the Node.js server code (package.json, dist/, node_modules/)
26
+ */
27
+ export declare function generateMcpb(opts: {
28
+ projectDir: string;
29
+ outputPath?: string;
30
+ manifest?: Partial<McpbManifest>;
31
+ }): Promise<string>;
@@ -0,0 +1,172 @@
1
+ import { readFile, readdir, mkdtemp, rm, cp, writeFile } from 'node:fs/promises';
2
+ import { resolve, join, basename } from 'node:path';
3
+ import { tmpdir } from 'node:os';
4
+ import { execFile as execFileCb } from 'node:child_process';
5
+ import { promisify } from 'node:util';
6
+ import { logger } from '../utils/logger.js';
7
+ import { pathExists } from '../utils/fs.js';
8
+ const execFile = promisify(execFileCb);
9
+ /**
10
+ * Generate an .mcpb bundle from a built MCP server project.
11
+ *
12
+ * The bundle is a zip file containing:
13
+ * manifest.json - metadata, tool list, config schema
14
+ * server/ - the Node.js server code (package.json, dist/, node_modules/)
15
+ */
16
+ export async function generateMcpb(opts) {
17
+ const { projectDir } = opts;
18
+ // Validate project structure
19
+ const pkgPath = resolve(projectDir, 'package.json');
20
+ if (!(await pathExists(pkgPath))) {
21
+ throw new Error(`Not a valid project directory (no package.json): ${projectDir}`);
22
+ }
23
+ const distDir = resolve(projectDir, 'dist');
24
+ if (!(await pathExists(distDir))) {
25
+ throw new Error(`No dist/ directory found. Build the project first: cd ${projectDir} && npm run build`);
26
+ }
27
+ // Read project metadata
28
+ const pkgJson = JSON.parse(await readFile(pkgPath, 'utf-8'));
29
+ const projectName = pkgJson.name ?? 'mcp-server';
30
+ const projectVersion = pkgJson.version ?? '1.0.0';
31
+ const projectDescription = pkgJson.description ?? '';
32
+ // Extract tool list (reuse same strategies as publish.ts)
33
+ const tools = await extractToolList(projectDir);
34
+ // Extract env vars from .env.example
35
+ const envVars = await extractEnvVars(projectDir);
36
+ // Build the manifest
37
+ const manifest = {
38
+ schema_version: '1.0',
39
+ name: opts.manifest?.name ?? projectName,
40
+ version: opts.manifest?.version ?? projectVersion,
41
+ description: opts.manifest?.description ?? projectDescription,
42
+ author: opts.manifest?.author ?? '',
43
+ license: opts.manifest?.license ?? 'proprietary',
44
+ runtime: opts.manifest?.runtime ?? 'node',
45
+ entry_point: opts.manifest?.entry_point ?? 'server/dist/index.js',
46
+ tools: opts.manifest?.tools ?? tools,
47
+ env_vars: opts.manifest?.env_vars ?? envVars,
48
+ };
49
+ // Create temp staging directory
50
+ const stagingDir = await mkdtemp(join(tmpdir(), 'mcpb-'));
51
+ try {
52
+ // Create server/ subdirectory with the project contents
53
+ const serverDir = join(stagingDir, 'server');
54
+ // Copy dist/
55
+ await cp(resolve(projectDir, 'dist'), join(serverDir, 'dist'), { recursive: true });
56
+ // Copy package.json (stripped to essentials)
57
+ const minimalPkg = {
58
+ name: pkgJson.name,
59
+ version: pkgJson.version,
60
+ description: pkgJson.description,
61
+ type: pkgJson.type,
62
+ main: pkgJson.main,
63
+ dependencies: pkgJson.dependencies,
64
+ };
65
+ await writeFile(join(serverDir, 'package.json'), JSON.stringify(minimalPkg, null, 2), 'utf-8');
66
+ // Copy node_modules/ if present
67
+ const nodeModulesDir = resolve(projectDir, 'node_modules');
68
+ if (await pathExists(nodeModulesDir)) {
69
+ await cp(nodeModulesDir, join(serverDir, 'node_modules'), { recursive: true });
70
+ }
71
+ // Write manifest.json at root of the bundle
72
+ await writeFile(join(stagingDir, 'manifest.json'), JSON.stringify(manifest, null, 2), 'utf-8');
73
+ // Determine output path
74
+ const outputPath = opts.outputPath ?? resolve(projectDir, '..', `${basename(projectDir)}.mcpb`);
75
+ // Create the zip archive using the system zip command
76
+ await execFile('zip', ['-r', '-q', resolve(outputPath), '.'], {
77
+ cwd: stagingDir,
78
+ });
79
+ return outputPath;
80
+ }
81
+ finally {
82
+ // Clean up staging directory
83
+ await rm(stagingDir, { recursive: true, force: true });
84
+ }
85
+ }
86
+ /**
87
+ * Extract tool names and descriptions from a generated project.
88
+ * Tries tool-catalog.json first, then scans src/tools/*.ts files.
89
+ */
90
+ async function extractToolList(projectDir) {
91
+ // Strategy 1: tool-catalog.json (dynamic discovery projects)
92
+ const catalogPath = resolve(projectDir, 'src/tool-catalog.json');
93
+ if (await pathExists(catalogPath)) {
94
+ try {
95
+ const catalog = JSON.parse(await readFile(catalogPath, 'utf-8'));
96
+ if (Array.isArray(catalog)) {
97
+ return catalog.map((entry) => ({
98
+ name: entry.name,
99
+ description: entry.description ?? entry.title ?? '',
100
+ }));
101
+ }
102
+ }
103
+ catch {
104
+ logger.warn('Failed to parse tool-catalog.json, falling back to tool file scanning');
105
+ }
106
+ }
107
+ // Strategy 2: Parse individual tool handler files in src/tools/
108
+ const toolsDir = resolve(projectDir, 'src/tools');
109
+ if (!(await pathExists(toolsDir))) {
110
+ return [];
111
+ }
112
+ const tools = [];
113
+ let entries;
114
+ try {
115
+ entries = await readdir(toolsDir);
116
+ }
117
+ catch {
118
+ return [];
119
+ }
120
+ for (const file of entries) {
121
+ if (file === 'index.ts' || !file.endsWith('.ts'))
122
+ continue;
123
+ try {
124
+ const content = await readFile(join(toolsDir, file), 'utf-8');
125
+ const nameMatch = content.match(/registerTool\(\s*['"]([^'"]+)['"]/);
126
+ if (!nameMatch)
127
+ continue;
128
+ const descMatch = content.match(/description:\s*[`'"]([^`'"]*)[`'"]/);
129
+ tools.push({
130
+ name: nameMatch[1],
131
+ description: descMatch ? descMatch[1] : '',
132
+ });
133
+ }
134
+ catch {
135
+ // skip files that can't be read
136
+ }
137
+ }
138
+ return tools;
139
+ }
140
+ /**
141
+ * Extract environment variable definitions from .env.example.
142
+ * Each line is expected to be: VAR_NAME=value # optional comment
143
+ */
144
+ async function extractEnvVars(projectDir) {
145
+ const envPath = resolve(projectDir, '.env.example');
146
+ if (!(await pathExists(envPath))) {
147
+ return [];
148
+ }
149
+ const content = await readFile(envPath, 'utf-8');
150
+ const vars = [];
151
+ for (const line of content.split('\n')) {
152
+ const trimmed = line.trim();
153
+ if (!trimmed || trimmed.startsWith('#'))
154
+ continue;
155
+ const eqIndex = trimmed.indexOf('=');
156
+ if (eqIndex === -1)
157
+ continue;
158
+ const name = trimmed.slice(0, eqIndex).trim();
159
+ if (!name)
160
+ continue;
161
+ // Extract inline comment as description
162
+ const valuePart = trimmed.slice(eqIndex + 1);
163
+ const commentIndex = valuePart.indexOf('#');
164
+ const description = commentIndex >= 0 ? valuePart.slice(commentIndex + 1).trim() : '';
165
+ vars.push({
166
+ name,
167
+ description,
168
+ required: true,
169
+ });
170
+ }
171
+ return vars;
172
+ }
@@ -0,0 +1,4 @@
1
+ import type { ProjectManifest } from '../types/index.js';
2
+ import type { CodeUnit } from './code-writer.js';
3
+ export declare function scaffoldProjectFiles(manifest: ProjectManifest): CodeUnit[];
4
+ export declare function scaffoldSharedModules(manifest: ProjectManifest): CodeUnit[];
@@ -0,0 +1,89 @@
1
+ import { renderTemplate } from './template-loader.js';
2
+ /**
3
+ * At/above this tool count, registering every tool upfront is a real token-cost
4
+ * problem; the README recommends `--dynamic-discovery` (the #1 buyer concern).
5
+ */
6
+ const DISCOVERY_RECOMMEND_THRESHOLD = 50;
7
+ export function scaffoldProjectFiles(manifest) {
8
+ const dynamicDiscovery = manifest.dynamicDiscovery ?? false;
9
+ const units = [
10
+ {
11
+ filePath: 'package.json',
12
+ content: renderTemplate('package.json', manifest),
13
+ },
14
+ {
15
+ filePath: 'tsconfig.json',
16
+ content: renderTemplate('tsconfig.json', manifest),
17
+ },
18
+ {
19
+ filePath: '.env.example',
20
+ content: renderTemplate('env.example', {
21
+ ...manifest,
22
+ authEnvVars: manifest.envVars.filter((v) => v.name !== 'BASE_URL'),
23
+ }),
24
+ },
25
+ {
26
+ filePath: '.gitignore',
27
+ content: renderTemplate('gitignore', manifest),
28
+ },
29
+ {
30
+ filePath: 'README.md',
31
+ content: renderTemplate('readme.md', {
32
+ ...manifest,
33
+ authEnvVars: manifest.envVars.filter((v) => v.name !== 'BASE_URL'),
34
+ dynamicDiscovery,
35
+ recommendDiscovery: !dynamicDiscovery && manifest.tools.length >= DISCOVERY_RECOMMEND_THRESHOLD,
36
+ }),
37
+ },
38
+ ];
39
+ if (manifest.transport === 'http') {
40
+ units.push({
41
+ filePath: 'Dockerfile',
42
+ content: renderTemplate('dockerfile', manifest),
43
+ });
44
+ }
45
+ return units;
46
+ }
47
+ export function scaffoldSharedModules(manifest) {
48
+ const hasOAuth = manifest.authSchemes.some((s) => s.type === 'oauth2');
49
+ const hasDynamicDiscovery = manifest.dynamicDiscovery ?? false;
50
+ const hasStaticTools = !hasDynamicDiscovery || (manifest.staticToolCount ?? 0) > 0;
51
+ const hasAsyncTools = manifest.tools.some((t) => t.isAsync);
52
+ const templateData = {
53
+ ...manifest,
54
+ hasResources: (manifest.resources?.length ?? 0) > 0,
55
+ hasPrompts: (manifest.prompts?.length ?? 0) > 0,
56
+ hasOAuth,
57
+ hasDynamicDiscovery,
58
+ hasStaticTools,
59
+ hasAsyncTools,
60
+ };
61
+ return [
62
+ {
63
+ filePath: 'src/index.ts',
64
+ content: renderTemplate(manifest.transport === 'http' ? 'server-main-http.ts' : 'server-main.ts', templateData),
65
+ },
66
+ {
67
+ filePath: 'src/config.ts',
68
+ content: renderTemplate('config.ts', manifest),
69
+ },
70
+ {
71
+ filePath: 'src/auth.ts',
72
+ content: renderTemplate('auth-provider.ts', templateData),
73
+ },
74
+ {
75
+ filePath: 'src/http.ts',
76
+ content: renderTemplate('http-executor.ts', manifest),
77
+ },
78
+ {
79
+ // W3C Trace Context — the executor stamps the active traceparent onto
80
+ // upstream calls; the HTTP server establishes the context per request.
81
+ filePath: 'src/trace.ts',
82
+ content: renderTemplate('trace.ts', manifest),
83
+ },
84
+ {
85
+ filePath: 'src/types.ts',
86
+ content: renderTemplate('types.ts', manifest),
87
+ },
88
+ ];
89
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Template loader for Python MCP server templates.
3
+ */
4
+ export declare function renderPythonTemplate(name: string, data: any): string;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Template loader for Python MCP server templates.
3
+ */
4
+ import { readFileSync } from 'node:fs';
5
+ import { resolve, dirname } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import Handlebars from 'handlebars';
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const PYTHON_TEMPLATE_DIR = resolve(__dirname, 'python-templates');
10
+ const pythonTemplateCache = new Map();
11
+ Handlebars.registerHelper('eq', function (a, b) {
12
+ return a === b;
13
+ });
14
+ Handlebars.registerHelper('pyDocstring', function (str) {
15
+ if (!str)
16
+ return '';
17
+ return str.replace(/"""/g, '\\"\\"\\"').replace(/\\/g, '\\\\');
18
+ });
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ export function renderPythonTemplate(name, data) {
21
+ if (!pythonTemplateCache.has(name)) {
22
+ const templatePath = resolve(PYTHON_TEMPLATE_DIR, `${name}.hbs`);
23
+ if (!templatePath.startsWith(PYTHON_TEMPLATE_DIR + '/')) {
24
+ throw new Error(`Invalid python template name: ${name}`);
25
+ }
26
+ const raw = readFileSync(templatePath, 'utf-8');
27
+ pythonTemplateCache.set(name, Handlebars.compile(raw, { noEscape: true }));
28
+ }
29
+ return pythonTemplateCache.get(name)(data);
30
+ }
@@ -0,0 +1,14 @@
1
+ FROM python:3.12-slim
2
+
3
+ WORKDIR /app
4
+ COPY requirements.txt .
5
+ RUN pip install --no-cache-dir -r requirements.txt
6
+ COPY server.py .
7
+ COPY .env.example .env
8
+
9
+ ENV TRANSPORT=http
10
+ ENV PORT=3000
11
+
12
+ EXPOSE 3000
13
+
14
+ CMD ["python", "server.py"]
@@ -0,0 +1,6 @@
1
+ # {{serverName}} configuration
2
+ BASE_URL={{{baseUrl}}}
3
+ {{#each authEnvVars}}
4
+ # {{description}}
5
+ {{name}}=
6
+ {{/each}}
@@ -0,0 +1,4 @@
1
+ mcp>=1.0.0
2
+ httpx>=0.27.0
3
+ pydantic>=2.0.0
4
+ python-dotenv>=1.0.0