@mcp-rune/create 0.11.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 (242) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +184 -0
  3. package/bin/rune.js +7 -0
  4. package/dist/commands/add-model.d.ts +5 -0
  5. package/dist/commands/add-model.d.ts.map +1 -0
  6. package/dist/commands/add-model.js +131 -0
  7. package/dist/commands/add-model.js.map +1 -0
  8. package/dist/commands/db-up.d.ts +2 -0
  9. package/dist/commands/db-up.d.ts.map +1 -0
  10. package/dist/commands/db-up.js +29 -0
  11. package/dist/commands/db-up.js.map +1 -0
  12. package/dist/commands/doctor/env-checks.d.ts +4 -0
  13. package/dist/commands/doctor/env-checks.d.ts.map +1 -0
  14. package/dist/commands/doctor/env-checks.js +88 -0
  15. package/dist/commands/doctor/env-checks.js.map +1 -0
  16. package/dist/commands/doctor/index.d.ts +21 -0
  17. package/dist/commands/doctor/index.d.ts.map +1 -0
  18. package/dist/commands/doctor/index.js +44 -0
  19. package/dist/commands/doctor/index.js.map +1 -0
  20. package/dist/commands/doctor/project-validation.d.ts +5 -0
  21. package/dist/commands/doctor/project-validation.d.ts.map +1 -0
  22. package/dist/commands/doctor/project-validation.js +166 -0
  23. package/dist/commands/doctor/project-validation.js.map +1 -0
  24. package/dist/commands/doctor.d.ts +7 -0
  25. package/dist/commands/doctor.d.ts.map +1 -0
  26. package/dist/commands/doctor.js +306 -0
  27. package/dist/commands/doctor.js.map +1 -0
  28. package/dist/commands/inspect.d.ts +16 -0
  29. package/dist/commands/inspect.d.ts.map +1 -0
  30. package/dist/commands/inspect.js +66 -0
  31. package/dist/commands/inspect.js.map +1 -0
  32. package/dist/commands/new/actions/apps.d.ts +5 -0
  33. package/dist/commands/new/actions/apps.d.ts.map +1 -0
  34. package/dist/commands/new/actions/apps.js +51 -0
  35. package/dist/commands/new/actions/apps.js.map +1 -0
  36. package/dist/commands/new/actions/architecture.d.ts +5 -0
  37. package/dist/commands/new/actions/architecture.d.ts.map +1 -0
  38. package/dist/commands/new/actions/architecture.js +45 -0
  39. package/dist/commands/new/actions/architecture.js.map +1 -0
  40. package/dist/commands/new/actions/auth.d.ts +5 -0
  41. package/dist/commands/new/actions/auth.d.ts.map +1 -0
  42. package/dist/commands/new/actions/auth.js +30 -0
  43. package/dist/commands/new/actions/auth.js.map +1 -0
  44. package/dist/commands/new/actions/database.d.ts +5 -0
  45. package/dist/commands/new/actions/database.d.ts.map +1 -0
  46. package/dist/commands/new/actions/database.js +60 -0
  47. package/dist/commands/new/actions/database.js.map +1 -0
  48. package/dist/commands/new/actions/fetch-template.d.ts +5 -0
  49. package/dist/commands/new/actions/fetch-template.d.ts.map +1 -0
  50. package/dist/commands/new/actions/fetch-template.js +22 -0
  51. package/dist/commands/new/actions/fetch-template.js.map +1 -0
  52. package/dist/commands/new/actions/intro.d.ts +5 -0
  53. package/dist/commands/new/actions/intro.d.ts.map +1 -0
  54. package/dist/commands/new/actions/intro.js +7 -0
  55. package/dist/commands/new/actions/intro.js.map +1 -0
  56. package/dist/commands/new/actions/layers.d.ts +5 -0
  57. package/dist/commands/new/actions/layers.d.ts.map +1 -0
  58. package/dist/commands/new/actions/layers.js +38 -0
  59. package/dist/commands/new/actions/layers.js.map +1 -0
  60. package/dist/commands/new/actions/models.d.ts +5 -0
  61. package/dist/commands/new/actions/models.d.ts.map +1 -0
  62. package/dist/commands/new/actions/models.js +18 -0
  63. package/dist/commands/new/actions/models.js.map +1 -0
  64. package/dist/commands/new/actions/next-steps.d.ts +5 -0
  65. package/dist/commands/new/actions/next-steps.d.ts.map +1 -0
  66. package/dist/commands/new/actions/next-steps.js +38 -0
  67. package/dist/commands/new/actions/next-steps.js.map +1 -0
  68. package/dist/commands/new/actions/observability.d.ts +5 -0
  69. package/dist/commands/new/actions/observability.d.ts.map +1 -0
  70. package/dist/commands/new/actions/observability.js +45 -0
  71. package/dist/commands/new/actions/observability.js.map +1 -0
  72. package/dist/commands/new/actions/post-scaffold.d.ts +5 -0
  73. package/dist/commands/new/actions/post-scaffold.d.ts.map +1 -0
  74. package/dist/commands/new/actions/post-scaffold.js +81 -0
  75. package/dist/commands/new/actions/post-scaffold.js.map +1 -0
  76. package/dist/commands/new/actions/preset.d.ts +5 -0
  77. package/dist/commands/new/actions/preset.d.ts.map +1 -0
  78. package/dist/commands/new/actions/preset.js +23 -0
  79. package/dist/commands/new/actions/preset.js.map +1 -0
  80. package/dist/commands/new/actions/prompts.d.ts +5 -0
  81. package/dist/commands/new/actions/prompts.d.ts.map +1 -0
  82. package/dist/commands/new/actions/prompts.js +33 -0
  83. package/dist/commands/new/actions/prompts.js.map +1 -0
  84. package/dist/commands/new/actions/render.d.ts +5 -0
  85. package/dist/commands/new/actions/render.d.ts.map +1 -0
  86. package/dist/commands/new/actions/render.js +35 -0
  87. package/dist/commands/new/actions/render.js.map +1 -0
  88. package/dist/commands/new/actions/scaffold-header.d.ts +5 -0
  89. package/dist/commands/new/actions/scaffold-header.d.ts.map +1 -0
  90. package/dist/commands/new/actions/scaffold-header.js +19 -0
  91. package/dist/commands/new/actions/scaffold-header.js.map +1 -0
  92. package/dist/commands/new/actions/scaffold-mode.d.ts +5 -0
  93. package/dist/commands/new/actions/scaffold-mode.d.ts.map +1 -0
  94. package/dist/commands/new/actions/scaffold-mode.js +49 -0
  95. package/dist/commands/new/actions/scaffold-mode.js.map +1 -0
  96. package/dist/commands/new/actions/summary.d.ts +5 -0
  97. package/dist/commands/new/actions/summary.d.ts.map +1 -0
  98. package/dist/commands/new/actions/summary.js +71 -0
  99. package/dist/commands/new/actions/summary.js.map +1 -0
  100. package/dist/commands/new/actions/toggles.d.ts +5 -0
  101. package/dist/commands/new/actions/toggles.d.ts.map +1 -0
  102. package/dist/commands/new/actions/toggles.js +25 -0
  103. package/dist/commands/new/actions/toggles.js.map +1 -0
  104. package/dist/commands/new/actions/tools.d.ts +5 -0
  105. package/dist/commands/new/actions/tools.d.ts.map +1 -0
  106. package/dist/commands/new/actions/tools.js +36 -0
  107. package/dist/commands/new/actions/tools.js.map +1 -0
  108. package/dist/commands/new/actions/transport.d.ts +5 -0
  109. package/dist/commands/new/actions/transport.d.ts.map +1 -0
  110. package/dist/commands/new/actions/transport.js +24 -0
  111. package/dist/commands/new/actions/transport.js.map +1 -0
  112. package/dist/commands/new/context.d.ts +76 -0
  113. package/dist/commands/new/context.d.ts.map +1 -0
  114. package/dist/commands/new/context.js +134 -0
  115. package/dist/commands/new/context.js.map +1 -0
  116. package/dist/commands/new/index.d.ts +5 -0
  117. package/dist/commands/new/index.d.ts.map +1 -0
  118. package/dist/commands/new/index.js +18 -0
  119. package/dist/commands/new/index.js.map +1 -0
  120. package/dist/commands/new/pipeline.d.ts +12 -0
  121. package/dist/commands/new/pipeline.d.ts.map +1 -0
  122. package/dist/commands/new/pipeline.js +67 -0
  123. package/dist/commands/new/pipeline.js.map +1 -0
  124. package/dist/commands/new/presets.d.ts +40 -0
  125. package/dist/commands/new/presets.d.ts.map +1 -0
  126. package/dist/commands/new/presets.js +91 -0
  127. package/dist/commands/new/presets.js.map +1 -0
  128. package/dist/commands/new.d.ts +24 -0
  129. package/dist/commands/new.d.ts.map +1 -0
  130. package/dist/commands/new.js +162 -0
  131. package/dist/commands/new.js.map +1 -0
  132. package/dist/commands/post-scaffold.d.ts +6 -0
  133. package/dist/commands/post-scaffold.d.ts.map +1 -0
  134. package/dist/commands/post-scaffold.js +24 -0
  135. package/dist/commands/post-scaffold.js.map +1 -0
  136. package/dist/core/cancel.d.ts +3 -0
  137. package/dist/core/cancel.d.ts.map +1 -0
  138. package/dist/core/cancel.js +17 -0
  139. package/dist/core/cancel.js.map +1 -0
  140. package/dist/core/color.d.ts +12 -0
  141. package/dist/core/color.d.ts.map +1 -0
  142. package/dist/core/color.js +14 -0
  143. package/dist/core/color.js.map +1 -0
  144. package/dist/core/db-setup.d.ts +13 -0
  145. package/dist/core/db-setup.d.ts.map +1 -0
  146. package/dist/core/db-setup.js +63 -0
  147. package/dist/core/db-setup.js.map +1 -0
  148. package/dist/core/fs-utils.d.ts +4 -0
  149. package/dist/core/fs-utils.d.ts.map +1 -0
  150. package/dist/core/fs-utils.js +31 -0
  151. package/dist/core/fs-utils.js.map +1 -0
  152. package/dist/core/output.d.ts +19 -0
  153. package/dist/core/output.d.ts.map +1 -0
  154. package/dist/core/output.js +42 -0
  155. package/dist/core/output.js.map +1 -0
  156. package/dist/core/prompts.d.ts +2 -0
  157. package/dist/core/prompts.d.ts.map +1 -0
  158. package/dist/core/prompts.js +2 -0
  159. package/dist/core/prompts.js.map +1 -0
  160. package/dist/core/tasks.d.ts +11 -0
  161. package/dist/core/tasks.d.ts.map +1 -0
  162. package/dist/core/tasks.js +28 -0
  163. package/dist/core/tasks.js.map +1 -0
  164. package/dist/data/mascot.d.ts +13 -0
  165. package/dist/data/mascot.d.ts.map +1 -0
  166. package/dist/data/mascot.js +80 -0
  167. package/dist/data/mascot.js.map +1 -0
  168. package/dist/index.d.ts +4 -0
  169. package/dist/index.d.ts.map +1 -0
  170. package/dist/index.js +68 -0
  171. package/dist/index.js.map +1 -0
  172. package/dist/render/copy-tree.d.ts +5 -0
  173. package/dist/render/copy-tree.d.ts.map +1 -0
  174. package/dist/render/copy-tree.js +146 -0
  175. package/dist/render/copy-tree.js.map +1 -0
  176. package/dist/render/fetch-template.d.ts +9 -0
  177. package/dist/render/fetch-template.d.ts.map +1 -0
  178. package/dist/render/fetch-template.js +113 -0
  179. package/dist/render/fetch-template.js.map +1 -0
  180. package/dist/render/model-gen.d.ts +3 -0
  181. package/dist/render/model-gen.d.ts.map +1 -0
  182. package/dist/render/model-gen.js +23 -0
  183. package/dist/render/model-gen.js.map +1 -0
  184. package/dist/templates/registry.d.ts +14 -0
  185. package/dist/templates/registry.d.ts.map +1 -0
  186. package/dist/templates/registry.js +34 -0
  187. package/dist/templates/registry.js.map +1 -0
  188. package/dist/types.d.ts +87 -0
  189. package/dist/types.d.ts.map +1 -0
  190. package/dist/types.js +8 -0
  191. package/dist/types.js.map +1 -0
  192. package/dist/wizard/presets.d.ts +35 -0
  193. package/dist/wizard/presets.d.ts.map +1 -0
  194. package/dist/wizard/presets.js +67 -0
  195. package/dist/wizard/presets.js.map +1 -0
  196. package/dist/wizard/questions.d.ts +11 -0
  197. package/dist/wizard/questions.d.ts.map +1 -0
  198. package/dist/wizard/questions.js +154 -0
  199. package/dist/wizard/questions.js.map +1 -0
  200. package/package.json +52 -0
  201. package/templates/advanced/.env.example.ejs +82 -0
  202. package/templates/advanced/.node-version +1 -0
  203. package/templates/advanced/README.md.ejs +76 -0
  204. package/templates/advanced/__only_if_hasHttp__/src/servers/remote.ts.ejs +36 -0
  205. package/templates/advanced/__only_if_hasStdio__/src/servers/local.ts +30 -0
  206. package/templates/advanced/__only_if_useAxiosClient__/src/api/axios-client.ts +74 -0
  207. package/templates/advanced/__only_if_useCustomApiClient__/src/api/custom-client.ts +48 -0
  208. package/templates/advanced/__only_if_useCustomConvention__/src/conventions/custom-convention.ts +64 -0
  209. package/templates/advanced/__only_if_useCustomSearch__/src/api-extensions/custom-search-adapter.ts +30 -0
  210. package/templates/advanced/__only_if_useFetchClient__/src/api/fetch-client.ts +111 -0
  211. package/templates/advanced/__only_if_useFlatRestConvention__/src/conventions/flat-rest-convention.ts +85 -0
  212. package/templates/advanced/__only_if_usePinoLogger__/src/observability/logger.ts +62 -0
  213. package/templates/advanced/__only_if_useRansackSearch__/src/api-extensions/ransack-search-adapter.ts +41 -0
  214. package/templates/advanced/__only_if_useSharedModelAttrs__/src/models/app-base-model.ts +22 -0
  215. package/templates/advanced/__only_if_useVectorStorage__/src/storage/vector-store.ts +21 -0
  216. package/templates/advanced/__only_if_withAnalysis__/docker-compose.yml +18 -0
  217. package/templates/advanced/__only_if_withDomain__/src/domain/registry.ts +25 -0
  218. package/templates/advanced/config/schema.ts.ejs +126 -0
  219. package/templates/advanced/package.json.ejs +50 -0
  220. package/templates/advanced/src/config.ts.ejs +207 -0
  221. package/templates/advanced/src/db.ts.ejs +35 -0
  222. package/templates/advanced/src/models/_model_.ts.ejs +25 -0
  223. package/templates/advanced/src/models/index.ts.ejs +13 -0
  224. package/templates/advanced/src/profiles.ts +44 -0
  225. package/templates/advanced/src/prompts/_model_-prompt.ts.ejs +27 -0
  226. package/templates/advanced/src/prompts/index.ts.ejs +18 -0
  227. package/templates/advanced/src/scripts/db-migrate.ts +90 -0
  228. package/templates/advanced/src/tools/index.ts.ejs +133 -0
  229. package/templates/advanced/test/smoke.test.ts +16 -0
  230. package/templates/advanced/tsconfig.json +14 -0
  231. package/templates/simple/.env.example +3 -0
  232. package/templates/simple/.node-version +1 -0
  233. package/templates/simple/README.md +40 -0
  234. package/templates/simple/package.json.ejs +27 -0
  235. package/templates/simple/src/config.ts.ejs +56 -0
  236. package/templates/simple/src/models/_model_.ts.ejs +25 -0
  237. package/templates/simple/src/models/index.ts.ejs +13 -0
  238. package/templates/simple/src/prompts/_model_-prompt.ts.ejs +27 -0
  239. package/templates/simple/src/prompts/index.ts.ejs +18 -0
  240. package/templates/simple/src/server.ts +12 -0
  241. package/templates/simple/test/smoke.test.ts +16 -0
  242. package/templates/simple/tsconfig.json +14 -0
@@ -0,0 +1,154 @@
1
+ import { input as askText, select, confirm } from '@inquirer/prompts';
2
+ import { resolveAnswers } from './presets.js';
3
+ import { TEMPLATE_REGISTRY } from '../templates/registry.js';
4
+ const TEMPLATE_DESCRIPTIONS = {
5
+ bookshelf: 'Full mcp-rune surface — apps, tools, prompts, GraphRAG. In-memory adapter, zero setup.',
6
+ tasks: 'Three models (project, task, tag) with belongsTo + hasMany. Shows the polymorphic tool surface.',
7
+ 'bookshelf-rest': 'Same Book model as bookshelf but backed by a real Express + fetch adapter. Adapter-swap demo.',
8
+ 'bookshelf-graph': 'GraphRAG-focused: pgvector + DomainRegistry + 500-book graph fixture pre-wired for every summary strategy.',
9
+ 'bookshelf-remote': 'HttpServer with static-token auth (OAuth path documented). Remote MCP transport.',
10
+ };
11
+ export async function selectScaffoldMode() {
12
+ const kind = await select({
13
+ message: 'How would you like to start?',
14
+ choices: [
15
+ { name: 'From scratch — pick a preset and configure', value: 'preset' },
16
+ { name: 'From an example — clone a runnable template', value: 'template' },
17
+ ],
18
+ default: 'preset',
19
+ });
20
+ if (kind === 'preset')
21
+ return { kind: 'preset' };
22
+ const id = await select({
23
+ message: 'Which template?',
24
+ choices: Object.keys(TEMPLATE_REGISTRY).map((name) => ({
25
+ name: TEMPLATE_DESCRIPTIONS[name] ? `${name} — ${TEMPLATE_DESCRIPTIONS[name]}` : name,
26
+ value: name,
27
+ })),
28
+ });
29
+ return { kind: 'template', id };
30
+ }
31
+ export async function runWizard(initial) {
32
+ const preset = initial.preset ??
33
+ (await select({
34
+ message: 'Which preset?',
35
+ choices: [
36
+ { name: 'Simple — stdio, no DB, CRUD on models', value: 'simple' },
37
+ { name: 'Advanced — HTTP+OAuth, optional analysis, profiles', value: 'advanced' },
38
+ ],
39
+ default: 'simple',
40
+ }));
41
+ let modelsRaw = initial.models;
42
+ if (modelsRaw === undefined) {
43
+ modelsRaw = await askText({
44
+ message: 'Models to scaffold (comma-separated, empty for none)',
45
+ default: '',
46
+ });
47
+ }
48
+ const advanced = {};
49
+ if (preset === 'advanced') {
50
+ advanced.transport =
51
+ initial.transport ??
52
+ (await select({
53
+ message: 'Transport?',
54
+ choices: [
55
+ { name: 'stdio only (local dev with Claude Code, etc.)', value: 'stdio' },
56
+ { name: 'HTTP only (remote, OAuth)', value: 'http' },
57
+ { name: 'both (recommended)', value: 'both' },
58
+ ],
59
+ default: 'both',
60
+ }));
61
+ advanced.withAnalysis =
62
+ initial.withAnalysis ??
63
+ (await confirm({
64
+ message: 'Enable analysis module? (adds docker-compose.yml + pg dep)',
65
+ default: false,
66
+ }));
67
+ advanced.withDomain =
68
+ initial.withDomain ??
69
+ (await confirm({
70
+ message: 'Enable domain workflows? (creates src/domain/ stubs)',
71
+ default: false,
72
+ }));
73
+ // ──── Architecture ──────────────────────────────────────────────────
74
+ advanced.apiConvention =
75
+ initial.apiConvention ??
76
+ (await select({
77
+ message: 'API convention?',
78
+ choices: [
79
+ { name: 'jsonapi — framework default (JSON:API wire format)', value: 'jsonapi' },
80
+ { name: 'rest-flat — starter (flat REST, no envelope)', value: 'rest-flat' },
81
+ ],
82
+ default: 'jsonapi',
83
+ }));
84
+ advanced.apiClient =
85
+ initial.apiClient ??
86
+ (await select({
87
+ message: 'API client?',
88
+ choices: [
89
+ { name: 'none — leave a placeholder for you to fill in', value: 'none' },
90
+ { name: 'fetch — starter implementation using native fetch', value: 'fetch' },
91
+ ],
92
+ default: 'none',
93
+ }));
94
+ advanced.searchAdapter =
95
+ initial.searchAdapter ??
96
+ (await select({
97
+ message: 'Search adapter?',
98
+ choices: [
99
+ { name: 'none — framework default (flat filter spread)', value: 'none' },
100
+ { name: 'ransack — starter for Rails Ransack q[...] syntax', value: 'ransack' },
101
+ ],
102
+ default: 'none',
103
+ }));
104
+ const transportHasHttp = advanced.transport === 'http' || advanced.transport === 'both';
105
+ if (transportHasHttp) {
106
+ advanced.serverAuth =
107
+ initial.serverAuth ??
108
+ (await select({
109
+ message: 'HTTP server auth?',
110
+ choices: [
111
+ { name: 'oauth — OAuth2 discovery + PKCE (recommended)', value: 'oauth' },
112
+ { name: 'static-token — single ACCESS_TOKEN bearer (simpler)', value: 'static-token' },
113
+ ],
114
+ default: 'oauth',
115
+ }));
116
+ }
117
+ else {
118
+ advanced.serverAuth = initial.serverAuth ?? 'oauth';
119
+ }
120
+ // ──── Observability ─────────────────────────────────────────────────
121
+ advanced.logger =
122
+ initial.logger ??
123
+ (await select({
124
+ message: 'Logger?',
125
+ choices: [
126
+ { name: 'framework — use mcp-rune\'s built-in logger', value: 'framework' },
127
+ { name: 'pino — starter wrapper exposing a pino instance', value: 'pino' },
128
+ ],
129
+ default: 'framework',
130
+ }));
131
+ advanced.errorTracking =
132
+ initial.errorTracking ??
133
+ (await select({
134
+ message: 'Error tracking?',
135
+ choices: [
136
+ { name: 'none — no DSN configured', value: 'none' },
137
+ { name: 'sentry — pre-populate SENTRY_DSN in .env.example', value: 'sentry' },
138
+ ],
139
+ default: 'none',
140
+ }));
141
+ advanced.tracing =
142
+ initial.tracing ??
143
+ (await select({
144
+ message: 'Tracing?',
145
+ choices: [
146
+ { name: 'none — no tracing backend configured', value: 'none' },
147
+ { name: 'langfuse — pre-populate LANGFUSE_* keys in .env.example', value: 'langfuse' },
148
+ ],
149
+ default: 'none',
150
+ }));
151
+ }
152
+ return resolveAnswers({ ...initial, ...advanced, preset, models: modelsRaw });
153
+ }
154
+ //# sourceMappingURL=questions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"questions.js","sourceRoot":"","sources":["../../src/wizard/questions.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@mcp-rune/create",
3
+ "version": "0.11.0",
4
+ "description": "Scaffolder for mcp-rune-based MCP servers",
5
+ "type": "module",
6
+ "bin": {
7
+ "rune": "./bin/rune.js",
8
+ "create-mcp-rune": "./bin/rune.js"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "engines": {
13
+ "node": ">=22"
14
+ },
15
+ "files": [
16
+ "bin/",
17
+ "dist/",
18
+ "templates/",
19
+ "README.md"
20
+ ],
21
+ "publishConfig": {
22
+ "registry": "https://registry.npmjs.org",
23
+ "access": "public"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "build:watch": "tsc --watch",
28
+ "build:check": "tsc --noEmit",
29
+ "dev": "tsx src/index.ts",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest",
32
+ "test:coverage": "vitest run --coverage",
33
+ "prepublishOnly": "npm run build"
34
+ },
35
+ "dependencies": {
36
+ "@clack/prompts": "^1.5.1",
37
+ "boxen": "^8.0.1",
38
+ "commander": "^12.1.0",
39
+ "ejs": "^3.1.10",
40
+ "execa": "^9.5.2",
41
+ "picocolors": "^1.1.1",
42
+ "terminal-link": "^5.0.0",
43
+ "tiged": "^2.12.8"
44
+ },
45
+ "devDependencies": {
46
+ "@types/ejs": "^3.1.5",
47
+ "@types/node": "^24.12.4",
48
+ "tsx": "^4.22.3",
49
+ "typescript": "^5.9.3",
50
+ "vitest": "^4.1.7"
51
+ }
52
+ }
@@ -0,0 +1,82 @@
1
+ # <%= projectName %> — environment configuration
2
+ #
3
+ # Copy to .env and fill in. Never commit secrets.
4
+
5
+ <% if (hasStdio) { -%>
6
+ # =============================================================================
7
+ # Stdio transport (local development)
8
+ # =============================================================================
9
+ # Pre-authenticated token used by stdio for tool API calls.
10
+ ACCESS_TOKEN=demo-token
11
+
12
+ <% } -%>
13
+ <% if (hasHttp) { -%>
14
+ # =============================================================================
15
+ # HTTP transport (<%= useStaticTokenAuth ? 'static token' : 'OAuth2' %> / remote)
16
+ # =============================================================================
17
+ PORT=4100
18
+ BASE_URL=http://localhost:4100
19
+ # CORS_ORIGINS=https://claude.ai,https://chatgpt.com
20
+
21
+ <% if (useStaticTokenAuth) { -%>
22
+ # Static bearer token required by HTTP clients
23
+ HTTP_ACCESS_TOKEN=
24
+
25
+ <% } else { -%>
26
+ # OAuth2 — register a client in your auth server and paste values here
27
+ OAUTH_SERVER_URL=http://localhost:4000
28
+ OAUTH_CLIENT_ID=
29
+ OAUTH_CLIENT_SECRET=
30
+ OAUTH_SCOPES=read write
31
+
32
+ <% } -%>
33
+ <% } -%>
34
+ # =============================================================================
35
+ # API backend (the system your MCP tools call)
36
+ # =============================================================================
37
+ API_URL=http://localhost:4001/api/v1
38
+
39
+ # =============================================================================
40
+ # Tool exposure profile
41
+ # =============================================================================
42
+ # full | chat | agent | classify — see src/profiles.js
43
+ MCP_PROFILE=full
44
+
45
+ <% if (withAnalysis) { -%>
46
+ # =============================================================================
47
+ # Analysis module (PostgreSQL + pgvector)
48
+ # =============================================================================
49
+ # Connection string for the docker-compose pgvector instance shipped with this project.
50
+ DATABASE_URL=postgres://postgres:dev@localhost:5544/<%= projectName %>
51
+ ANALYSIS_ENABLED=true
52
+
53
+ <% } else { -%>
54
+ # =============================================================================
55
+ # Database (optional — enables OAuth token persistence)
56
+ # =============================================================================
57
+ # DATABASE_URL=postgres://user:pass@localhost:5432/<%= projectName %>
58
+ # ANALYSIS_ENABLED=false
59
+
60
+ <% } -%>
61
+ <% if (withDomain) { -%>
62
+ # =============================================================================
63
+ # Domain workflows
64
+ # =============================================================================
65
+ DOMAIN_ENABLED=true
66
+
67
+ <% } -%>
68
+ # =============================================================================
69
+ # Observability (optional)
70
+ # =============================================================================
71
+ <% if (useSentry) { -%>
72
+ SENTRY_DSN=
73
+ <% } else { -%>
74
+ # SENTRY_DSN=
75
+ <% } -%>
76
+ <% if (useLangfuse) { -%>
77
+ LANGFUSE_PUBLIC_KEY=
78
+ LANGFUSE_SECRET_KEY=
79
+ <% } else { -%>
80
+ # LANGFUSE_PUBLIC_KEY=
81
+ # LANGFUSE_SECRET_KEY=
82
+ <% } -%>
@@ -0,0 +1 @@
1
+ 24
@@ -0,0 +1,76 @@
1
+ # <%= projectName %>
2
+
3
+ MCP server scaffolded with [`@mcp-rune/create`](https://github.com/mcp-rune/mcp-rune-cli) (advanced preset).
4
+
5
+ ## Run
6
+
7
+ ```bash
8
+ cp .env.example .env
9
+ # fill in OAUTH_*, etc.
10
+ npm install
11
+ <% if (hasHttp) { -%>
12
+ npm run start # production default — HTTP + OAuth (alias for start:remote)
13
+ <% } else { -%>
14
+ npm run start # production default — stdio (alias for start:local)
15
+ <% } -%>
16
+ <% if (hasStdio) { -%>
17
+ npm run start:local # stdio (for Claude Code, Cursor, etc.)
18
+ <% } -%>
19
+ <% if (hasHttp) { -%>
20
+ npm run start:remote # HTTP + OAuth
21
+ <% } -%>
22
+ npm run test # vitest smoke tests
23
+ npm run typecheck # tsc --noEmit
24
+ npm run inspect # open the MCP Inspector pre-wired
25
+ ```
26
+
27
+ <% if (withAnalysis) { -%>
28
+ ## Analysis module
29
+
30
+ This project ships with a `docker-compose.yml` for Postgres + pgvector. Bring it up before running the server:
31
+
32
+ ```bash
33
+ npm run db:up # docker compose up -d + migrations
34
+ npm run db:down # tear down
35
+ ```
36
+
37
+ `ANALYSIS_ENABLED=true` in `.env` exposes the analysis tools (semantic search, stratified sampling, ingestion).
38
+
39
+ <% } -%>
40
+ ## Profiles
41
+
42
+ Set `MCP_PROFILE` to control the exposed tool surface (same binary, different shape):
43
+
44
+ | Profile | Tools | Apps |
45
+ |------------|------------------------------------|----------|
46
+ | `full` | everything | enabled |
47
+ | `chat` | hides raw-JSON data tools | enabled |
48
+ | `agent` | all | disabled |
49
+ | `classify` | only `update_model` | disabled |
50
+
51
+ See `src/profiles.js`.
52
+
53
+ ## Layout
54
+
55
+ ```
56
+ src/
57
+ ├── servers/
58
+ │ ├── local.js stdio entry point
59
+ │ └── remote.js HTTP + OAuth entry point
60
+ ├── config.js startup phases (identity → config → DB → tools)
61
+ ├── db.js shared pg.Pool
62
+ ├── profiles.js tool exposure profiles
63
+ ├── models/ domain models
64
+ ├── prompts/ prompt classes (auto-derived from models)
65
+ ├── tools/ tool registry
66
+ <% if (withDomain) { -%>
67
+ ├── domain/ workflows + business rules
68
+ <% } -%>
69
+ └── scripts/
70
+ └── db-migrate.js apply mcp-rune migrations
71
+ ```
72
+
73
+ ## Useful links
74
+
75
+ - [mcp-rune docs](https://github.com/mcp-rune/mcp-rune)
76
+ - MCP Inspector: `npm run inspect`
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ import dotenv from 'dotenv'
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
8
+
9
+ dotenv.config({ path: path.join(__dirname, '../..', '.env'), quiet: true })
10
+
11
+ const { HttpServer } = await import('@mcp-rune/mcp-rune/server')
12
+ <% if (useStaticTokenAuth) { -%>
13
+ const { config, mcpConfig } = await import('../config.js')
14
+ <% } else { -%>
15
+ const { config, createOAuthService, mcpConfig } = await import('../config.js')
16
+ <% } -%>
17
+
18
+ const port = config.transport.remote.port
19
+ const baseUrl = config.transport.remote.baseUrl ?? `http://localhost:${port}`
20
+ const isProduction = config.http.environment === 'production'
21
+
22
+ const server = new HttpServer({
23
+ port,
24
+ baseUrl,
25
+ pathPrefix: config.transport.remote.pathPrefix,
26
+ <% if (useStaticTokenAuth) { -%>
27
+ accessToken: config.transport.remote.accessToken,
28
+ <% } else { -%>
29
+ oauth: createOAuthService({ redirectUri: `${baseUrl}/oauth/callback` }),
30
+ <% } -%>
31
+ mcp: mcpConfig,
32
+ isProduction,
33
+ corsOrigins: config.http.corsOrigins
34
+ })
35
+
36
+ server.start()
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ import dotenv from 'dotenv'
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
8
+
9
+ // Load .env BEFORE importing modules that read process.env at module load.
10
+ // quiet: dotenv v17+ otherwise prints to stdout, which corrupts the MCP stdio stream.
11
+ dotenv.config({ path: path.join(__dirname, '../..', '.env'), quiet: true })
12
+
13
+ const { StdioServer } = await import('@mcp-rune/mcp-rune/server')
14
+ const { config, mcpConfig } = await import('../config.js')
15
+
16
+ const accessToken = config.transport.local.accessToken
17
+ if (!accessToken) {
18
+ console.error('ACCESS_TOKEN env var is required for the stdio server.')
19
+ process.exit(1)
20
+ }
21
+
22
+ const server = new StdioServer({
23
+ accessToken,
24
+ mcp: mcpConfig
25
+ })
26
+
27
+ server.start().catch((err: Error) => {
28
+ console.error('Server startup failed:', err.stack ?? err.message)
29
+ process.exit(1)
30
+ })
@@ -0,0 +1,74 @@
1
+ /**
2
+ * AxiosApiClient — starter scaffolded by `rune new --api-client axios`.
3
+ *
4
+ * Implements the framework's `ApiClient` interface on top of axios. Adapt the
5
+ * convention hook and error parsing for your backend's wire format.
6
+ */
7
+
8
+ import axios, { type AxiosInstance, type AxiosResponse } from 'axios'
9
+ import type { BaseConvention } from '@mcp-rune/mcp-rune/api-conventions'
10
+ import type { ApiClient } from '@mcp-rune/mcp-rune/core'
11
+
12
+ interface AxiosApiClientOptions {
13
+ baseUrl: string
14
+ accessToken?: string
15
+ convention?: BaseConvention
16
+ }
17
+
18
+ interface HttpError extends Error {
19
+ status?: number
20
+ body?: unknown
21
+ }
22
+
23
+ export class AxiosApiClient implements ApiClient {
24
+ private client: AxiosInstance
25
+ private convention?: BaseConvention
26
+
27
+ constructor({ baseUrl, accessToken, convention }: AxiosApiClientOptions) {
28
+ this.convention = convention
29
+ this.client = axios.create({
30
+ baseURL: baseUrl,
31
+ headers: {
32
+ Accept: 'application/json',
33
+ ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {})
34
+ }
35
+ })
36
+ }
37
+
38
+ async get(url: string, params?: Record<string, unknown>): Promise<Record<string, unknown>> {
39
+ return this._unwrap(this.client.get(url, { params }))
40
+ }
41
+
42
+ async post(url: string, data?: Record<string, unknown>): Promise<Record<string, unknown>> {
43
+ return this._unwrap(this.client.post(url, data))
44
+ }
45
+
46
+ async put(url: string, data?: Record<string, unknown>): Promise<Record<string, unknown>> {
47
+ return this._unwrap(this.client.put(url, data))
48
+ }
49
+
50
+ async patch(url: string, data?: Record<string, unknown>): Promise<Record<string, unknown>> {
51
+ return this._unwrap(this.client.patch(url, data))
52
+ }
53
+
54
+ async delete(url: string): Promise<Record<string, unknown>> {
55
+ return this._unwrap(this.client.delete(url))
56
+ }
57
+
58
+ private async _unwrap(p: Promise<AxiosResponse>): Promise<Record<string, unknown>> {
59
+ try {
60
+ const response = await p
61
+ return (response.data ?? {}) as Record<string, unknown>
62
+ } catch (raw) {
63
+ const err = raw as { response?: { status?: number; data?: unknown }; message?: string }
64
+ const messages = this.convention?.parseErrorResponse?.({
65
+ status: err.response?.status ?? 0,
66
+ data: err.response?.data
67
+ }) ?? [err.message ?? 'HTTP error']
68
+ const out: HttpError = new Error(messages.join('; '))
69
+ out.status = err.response?.status
70
+ out.body = err.response?.data
71
+ throw out
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Custom ApiClient stub — scaffolded by `rune new --api-client custom`.
3
+ *
4
+ * Implement the framework's `ApiClient` interface against whatever HTTP layer
5
+ * (got, undici, your own pool) your backend needs. Each verb must return
6
+ * `Promise<Record<string, unknown>>` parsed from the response body.
7
+ */
8
+
9
+ import type { BaseConvention } from '@mcp-rune/mcp-rune/api-conventions'
10
+ import type { ApiClient } from '@mcp-rune/mcp-rune/core'
11
+
12
+ interface CustomApiClientOptions {
13
+ baseUrl: string
14
+ accessToken?: string
15
+ convention?: BaseConvention
16
+ }
17
+
18
+ export class CustomApiClient implements ApiClient {
19
+ baseUrl: string
20
+ protected accessToken?: string
21
+ protected convention?: BaseConvention
22
+
23
+ constructor({ baseUrl, accessToken, convention }: CustomApiClientOptions) {
24
+ this.baseUrl = baseUrl
25
+ this.accessToken = accessToken
26
+ this.convention = convention
27
+ }
28
+
29
+ async get(_url: string, _params?: Record<string, unknown>): Promise<Record<string, unknown>> {
30
+ throw new Error('CustomApiClient.get: TODO implement')
31
+ }
32
+
33
+ async post(_url: string, _data?: Record<string, unknown>): Promise<Record<string, unknown>> {
34
+ throw new Error('CustomApiClient.post: TODO implement')
35
+ }
36
+
37
+ async put(_url: string, _data?: Record<string, unknown>): Promise<Record<string, unknown>> {
38
+ throw new Error('CustomApiClient.put: TODO implement')
39
+ }
40
+
41
+ async patch(_url: string, _data?: Record<string, unknown>): Promise<Record<string, unknown>> {
42
+ throw new Error('CustomApiClient.patch: TODO implement')
43
+ }
44
+
45
+ async delete(_url: string): Promise<Record<string, unknown>> {
46
+ throw new Error('CustomApiClient.delete: TODO implement')
47
+ }
48
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Custom convention — stub scaffolded by `rune new --api-convention custom`.
3
+ *
4
+ * Extend `BaseConvention` to describe how your backend's wire format maps
5
+ * onto the framework's normalized shape. Fill in the three override hooks
6
+ * for your API: association field naming, list-response normalization,
7
+ * and response cleaning.
8
+ */
9
+
10
+ import type {
11
+ FieldDefinition,
12
+ NormalizedListResponse
13
+ } from '@mcp-rune/mcp-rune/api-conventions'
14
+ import { BaseConvention } from '@mcp-rune/mcp-rune/api-conventions'
15
+ import type { BelongsToAssociation, HasManyAssociation } from '@mcp-rune/mcp-rune/models'
16
+
17
+ export class CustomConvention extends BaseConvention {
18
+ override get name(): string {
19
+ return 'custom'
20
+ }
21
+
22
+ override resolveAssociationFields(
23
+ relName: string,
24
+ relConfig: BelongsToAssociation | HasManyAssociation,
25
+ overrides: Record<string, Partial<FieldDefinition>> = {}
26
+ ): Record<string, FieldDefinition> {
27
+ // TODO: shape this for your backend (e.g. embedded objects, foreign keys,
28
+ // or a `relationships: {}` envelope).
29
+ const isMany = 'many' in relConfig && Boolean(relConfig.many)
30
+ const fieldName = isMany ? `${relName}` : `${relName}_id`
31
+ const field: FieldDefinition = {
32
+ name: fieldName,
33
+ type: isMany ? 'array' : 'integer',
34
+ required: Boolean(relConfig.required),
35
+ description: relConfig.description ?? `Association to ${relConfig.target_model}`,
36
+ ...overrides[fieldName]
37
+ }
38
+ return { [fieldName]: field }
39
+ }
40
+
41
+ override normalizeListResponse(
42
+ response: Record<string, unknown> | unknown[],
43
+ { page, perPage }: { page: number; perPage: number }
44
+ ): NormalizedListResponse {
45
+ // TODO: pull records + pagination metadata out of your envelope.
46
+ const records = Array.isArray(response) ? (response as Record<string, unknown>[]) : []
47
+ return {
48
+ records,
49
+ pagination: {
50
+ page,
51
+ per_page: perPage,
52
+ total: records.length,
53
+ total_pages: 1
54
+ }
55
+ }
56
+ }
57
+
58
+ override cleanResponse(data: unknown): unknown {
59
+ // TODO: strip metadata wrappers; default is identity.
60
+ return data
61
+ }
62
+ }
63
+
64
+ export const customConvention = new CustomConvention()
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Custom search request shaper — stub scaffolded by `rune new --search-adapter custom`.
3
+ *
4
+ * Extend `SearchRequestShaper` and override `buildBody` to translate the
5
+ * framework's `(query, filters, pagination, config)` shape into your search
6
+ * endpoint's wire format (POST body, GET params, GraphQL query, etc.).
7
+ *
8
+ * Wire it into the search api-extension at the ToolRegistry construction
9
+ * site (see `src/tools/index.ts` `searchExtension({ shaper: customSearchAdapter })`).
10
+ */
11
+
12
+ import { SearchRequestShaper } from '@mcp-rune/mcp-rune/api-extensions/search'
13
+ import type { Pagination, SearchConfig } from '@mcp-rune/mcp-rune/api-extensions/search'
14
+
15
+ export class CustomSearchAdapter extends SearchRequestShaper {
16
+ override buildBody(
17
+ query: string | null,
18
+ filters: Record<string, unknown> | undefined,
19
+ { page, perPage }: Pagination,
20
+ _searchConfig: SearchConfig
21
+ ): Record<string, unknown> {
22
+ // TODO: shape the body for your search endpoint.
23
+ const body: Record<string, unknown> = { page, per_page: perPage }
24
+ if (query) body.q = query
25
+ if (filters && Object.keys(filters).length > 0) body.filters = filters
26
+ return body
27
+ }
28
+ }
29
+
30
+ export const customSearchAdapter = new CustomSearchAdapter()