@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,111 @@
1
+ /**
2
+ * FetchApiClient — starter scaffolded by `rune new --api-client fetch`.
3
+ *
4
+ * Implements the framework's `ApiClient` interface using native `fetch` (Node 18+).
5
+ * Returns `Promise<Record<string, unknown>>` from each verb. Adapt the
6
+ * convention hook and error parsing for your backend's wire format.
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 FetchApiClientOptions {
13
+ baseUrl: string
14
+ accessToken?: string
15
+ convention?: BaseConvention
16
+ fetchImpl?: typeof fetch
17
+ }
18
+
19
+ interface HttpError extends Error {
20
+ status?: number
21
+ body?: unknown
22
+ }
23
+
24
+ export class FetchApiClient implements ApiClient {
25
+ baseUrl: string
26
+ private accessToken?: string
27
+ private convention?: BaseConvention
28
+ private _fetch: typeof fetch
29
+
30
+ constructor({ baseUrl, accessToken, convention, fetchImpl = globalThis.fetch }: FetchApiClientOptions) {
31
+ this.baseUrl = baseUrl
32
+ this.accessToken = accessToken
33
+ this.convention = convention
34
+ this._fetch = fetchImpl
35
+ }
36
+
37
+ async get(url: string, params?: Record<string, unknown>): Promise<Record<string, unknown>> {
38
+ return this._request('GET', url, { params })
39
+ }
40
+
41
+ async post(url: string, data?: Record<string, unknown>): Promise<Record<string, unknown>> {
42
+ return this._request('POST', url, { body: data })
43
+ }
44
+
45
+ async put(url: string, data?: Record<string, unknown>): Promise<Record<string, unknown>> {
46
+ return this._request('PUT', url, { body: data })
47
+ }
48
+
49
+ async patch(url: string, data?: Record<string, unknown>): Promise<Record<string, unknown>> {
50
+ return this._request('PATCH', url, { body: data })
51
+ }
52
+
53
+ async delete(url: string): Promise<Record<string, unknown>> {
54
+ return this._request('DELETE', url)
55
+ }
56
+
57
+ private async _request(
58
+ method: string,
59
+ url: string,
60
+ { params, body }: { params?: Record<string, unknown>; body?: unknown } = {}
61
+ ): Promise<Record<string, unknown>> {
62
+ const target = new URL(this._resolveUrl(url))
63
+ if (params) {
64
+ for (const [k, v] of Object.entries(params)) {
65
+ if (v === undefined || v === null) continue
66
+ target.searchParams.set(k, String(v))
67
+ }
68
+ }
69
+
70
+ const headers: Record<string, string> = { Accept: 'application/json' }
71
+ if (this.accessToken) headers.Authorization = `Bearer ${this.accessToken}`
72
+
73
+ const init: RequestInit = { method, headers }
74
+ if (body !== undefined) {
75
+ headers['Content-Type'] = 'application/json'
76
+ init.body = JSON.stringify(body)
77
+ }
78
+
79
+ const response = await this._fetch(target, init)
80
+ const text = await response.text()
81
+ const payload = text ? safeJson(text) : {}
82
+
83
+ if (!response.ok) {
84
+ const messages = this.convention?.parseErrorResponse?.({
85
+ status: response.status,
86
+ data: payload
87
+ }) ?? [text || `HTTP ${response.status}`]
88
+ const err: HttpError = new Error(messages.join('; '))
89
+ err.status = response.status
90
+ err.body = payload
91
+ throw err
92
+ }
93
+
94
+ return payload as Record<string, unknown>
95
+ }
96
+
97
+ private _resolveUrl(url: string): string {
98
+ if (/^https?:\/\//i.test(url)) return url
99
+ const base = this.baseUrl?.replace(/\/$/, '') ?? ''
100
+ const path = url.startsWith('/') ? url : `/${url}`
101
+ return `${base}${path}`
102
+ }
103
+ }
104
+
105
+ function safeJson(text: string): Record<string, unknown> {
106
+ try {
107
+ return JSON.parse(text) as Record<string, unknown>
108
+ } catch {
109
+ return { raw: text }
110
+ }
111
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Flat REST convention — starter scaffolded by `rune new --api-convention rest-flat`.
3
+ *
4
+ * Targets plain REST APIs that accept and return resources without a JSON:API
5
+ * `data: { attributes: ... }` envelope. Customize for your backend's exact
6
+ * shape (pagination, association style, error envelope).
7
+ */
8
+
9
+ import type {
10
+ FieldDefinition,
11
+ NormalizedListResponse
12
+ } from '@mcp-rune/mcp-rune/api-conventions'
13
+ import { BaseConvention } from '@mcp-rune/mcp-rune/api-conventions'
14
+ import type { BelongsToAssociation, HasManyAssociation } from '@mcp-rune/mcp-rune/models'
15
+
16
+ export class FlatRestConvention extends BaseConvention {
17
+ override get name(): string {
18
+ return 'rest-flat'
19
+ }
20
+
21
+ override resolveAssociationFields(
22
+ relName: string,
23
+ relConfig: BelongsToAssociation | HasManyAssociation,
24
+ overrides: Record<string, Partial<FieldDefinition>> = {}
25
+ ): Record<string, FieldDefinition> {
26
+ const isMany = 'many' in relConfig && Boolean(relConfig.many)
27
+ const fieldName = isMany ? `${singularize(relName)}_ids` : `${relName}_id`
28
+ const field: FieldDefinition = {
29
+ name: fieldName,
30
+ type: isMany ? 'array' : 'integer',
31
+ required: Boolean(relConfig.required),
32
+ description: relConfig.description ?? `Association to ${relConfig.target_model}`,
33
+ ...overrides[fieldName]
34
+ }
35
+ if (isMany) field.items = { type: 'integer' }
36
+ return { [fieldName]: field }
37
+ }
38
+
39
+ override normalizeListResponse(
40
+ response: Record<string, unknown> | unknown[],
41
+ { page, perPage }: { page: number; perPage: number }
42
+ ): NormalizedListResponse {
43
+ const records = pickRecords(response)
44
+ const total = pickTotal(response, records.length)
45
+ return {
46
+ records,
47
+ pagination: {
48
+ page,
49
+ per_page: perPage,
50
+ total,
51
+ total_pages: Math.max(1, Math.ceil(total / perPage))
52
+ }
53
+ }
54
+ }
55
+
56
+ override cleanResponse(data: unknown): unknown {
57
+ return data
58
+ }
59
+ }
60
+
61
+ function singularize(name: string): string {
62
+ if (name.endsWith('ies')) return name.slice(0, -3) + 'y'
63
+ if (name.endsWith('s')) return name.slice(0, -1)
64
+ return name
65
+ }
66
+
67
+ function pickRecords(response: unknown): Record<string, unknown>[] {
68
+ if (Array.isArray(response)) return response as Record<string, unknown>[]
69
+ if (response && typeof response === 'object') {
70
+ const r = response as { data?: unknown; records?: unknown }
71
+ if (Array.isArray(r.data)) return r.data as Record<string, unknown>[]
72
+ if (Array.isArray(r.records)) return r.records as Record<string, unknown>[]
73
+ }
74
+ return []
75
+ }
76
+
77
+ function pickTotal(response: unknown, fallback: number): number {
78
+ if (response && typeof response === 'object') {
79
+ const r = response as { total?: unknown }
80
+ if (typeof r.total === 'number') return r.total
81
+ }
82
+ return fallback
83
+ }
84
+
85
+ export const flatRestConvention = new FlatRestConvention()
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Pino logger — starter scaffolded by `rune new --logger pino`.
3
+ *
4
+ * Re-exports a pino instance shaped to match the surface mcp-rune's built-in
5
+ * logger exposes: `info / warn / error / debug` + `setApp(name)` + `child(meta)`.
6
+ * Project code imports from this file; the framework keeps using its own
7
+ * logger internally.
8
+ */
9
+
10
+ import pino from 'pino'
11
+
12
+ type LogMethod = 'info' | 'warn' | 'error' | 'debug'
13
+
14
+ interface ChildLogger {
15
+ info: (message: string, meta?: Record<string, unknown>) => void
16
+ warn: (message: string, meta?: Record<string, unknown>) => void
17
+ error: (message: string, meta?: Record<string, unknown>) => void
18
+ debug: (message: string, meta?: Record<string, unknown>) => void
19
+ child: (meta: Record<string, unknown>) => ChildLogger
20
+ }
21
+
22
+ const base = pino({
23
+ level: process.env.LOG_LEVEL ?? 'info',
24
+ formatters: {
25
+ level(label: string): { level: string } {
26
+ return { level: label }
27
+ }
28
+ }
29
+ })
30
+
31
+ let appName = process.env.MCP_SERVER_NAME ?? 'app'
32
+
33
+ function emit(method: LogMethod, bound: Record<string, unknown>) {
34
+ return (message: string, meta: Record<string, unknown> = {}): void => {
35
+ base[method]({ app: appName, ...bound, ...meta }, message)
36
+ }
37
+ }
38
+
39
+ function buildChild(bound: Record<string, unknown>): ChildLogger {
40
+ return {
41
+ info: emit('info', bound),
42
+ warn: emit('warn', bound),
43
+ error: emit('error', bound),
44
+ debug: emit('debug', bound),
45
+ child(extra) {
46
+ return buildChild({ ...bound, ...extra })
47
+ }
48
+ }
49
+ }
50
+
51
+ export const logger = {
52
+ info: emit('info', {}),
53
+ warn: emit('warn', {}),
54
+ error: emit('error', {}),
55
+ debug: emit('debug', {}),
56
+ setApp(name: string): void {
57
+ appName = name
58
+ },
59
+ child(meta: Record<string, unknown> = {}): ChildLogger {
60
+ return buildChild(meta)
61
+ }
62
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Ransack search request shaper — starter scaffolded by
3
+ * `rune new --search-adapter ransack`.
4
+ *
5
+ * Rails backends using the Ransack gem expect filters under a single `q`
6
+ * parameter with `field_predicate` suffixes (e.g. `q[name_eq]`,
7
+ * `q[created_at_gt]`). This subclass of the framework's
8
+ * `SearchRequestShaper` builds that envelope; tweak the predicate logic for
9
+ * your endpoint as needed.
10
+ *
11
+ * For complete Rails coverage (filter nesting + range mappings) consider
12
+ * `RailsSearchRequestShaper` from `@mcp-rune/mcp-rune/api-extensions/search`
13
+ * instead.
14
+ */
15
+
16
+ import { SearchRequestShaper } from '@mcp-rune/mcp-rune/api-extensions/search'
17
+ import type { Pagination, SearchConfig } from '@mcp-rune/mcp-rune/api-extensions/search'
18
+
19
+ export class RansackSearchAdapter extends SearchRequestShaper {
20
+ override buildBody(
21
+ query: string | null,
22
+ filters: Record<string, unknown> | undefined,
23
+ { page, perPage }: Pagination,
24
+ searchConfig: SearchConfig
25
+ ): Record<string, unknown> {
26
+ const body: Record<string, unknown> = { page, per_page: perPage }
27
+
28
+ const q: Record<string, unknown> =
29
+ filters && Object.keys(filters).length > 0 ? { ...filters } : {}
30
+
31
+ if (query) {
32
+ const queryField = searchConfig?.query?.queryParam ?? 'name_cont'
33
+ q[queryField] = query
34
+ }
35
+
36
+ if (Object.keys(q).length > 0) body.q = q
37
+ return body
38
+ }
39
+ }
40
+
41
+ export const ransackSearchAdapter = new RansackSearchAdapter()
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Shared ModelLayer base — stub scaffolded by `rune new --shared-model-attrs`.
3
+ *
4
+ * Intermediate class between the framework's `BaseModel` and the per-resource
5
+ * models in `src/models/`. Put attributes / methods that apply across every
6
+ * model here (e.g. tenancy keys, audit timestamps, soft-delete fields) so
7
+ * each concrete model only declares what's unique to it.
8
+ *
9
+ * Scaffolded models still extend `BaseModel` directly today; switch their
10
+ * `extends` to `AppBaseModel` to pick this up.
11
+ */
12
+
13
+ import type { AttributeDefinition } from '@mcp-rune/mcp-rune/models'
14
+ import { BaseModel } from '@mcp-rune/mcp-rune/models'
15
+
16
+ export class AppBaseModel extends BaseModel {
17
+ static override attributes: Record<string, AttributeDefinition> = {
18
+ // TODO: shared attributes, e.g.:
19
+ // created_at: { type: 'datetime', description: 'Created at' },
20
+ // updated_at: { type: 'datetime', description: 'Updated at' },
21
+ }
22
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Vector storage — scaffolded by `rune new --vector-storage`.
3
+ *
4
+ * Call `enableVectorStorage(pool)` once at server startup (before ToolRegistry
5
+ * is constructed). Tools tagged `requiresVectorStorage` only appear in
6
+ * `tools/list` after this call succeeds.
7
+ *
8
+ * Retention options can be tuned via `createPgvectorAdapter` options:
9
+ * toolMemoriesRetentionDays (default 30)
10
+ * ingestedRecordsRetentionDays (default 7)
11
+ */
12
+
13
+ import type { Pool } from 'pg'
14
+ import { vectorStorage } from '@mcp-rune/mcp-rune/runtime'
15
+ import { createPgvectorAdapter } from '@mcp-rune/mcp-rune/runtime/vendor/pgvector'
16
+
17
+ export function enableVectorStorage(pool: Pool): void {
18
+ vectorStorage.initVectorStorage({
19
+ adapter: createPgvectorAdapter({ pool }),
20
+ })
21
+ }
@@ -0,0 +1,18 @@
1
+ services:
2
+ db:
3
+ image: pgvector/pgvector:pg16
4
+ environment:
5
+ POSTGRES_PASSWORD: dev
6
+ POSTGRES_USER: postgres
7
+ POSTGRES_DB: {{projectName}}
8
+ ports:
9
+ - "5544:5432"
10
+ volumes:
11
+ - pgdata:/var/lib/postgresql/data
12
+ healthcheck:
13
+ test: ["CMD", "pg_isready", "-U", "postgres"]
14
+ interval: 1s
15
+ timeout: 3s
16
+ retries: 30
17
+ volumes:
18
+ pgdata:
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Domain registry — workflows, business rules, and domain knowledge.
3
+ *
4
+ * Organise domain items into DomainModule objects (one per domain area) and
5
+ * spread them into InMemoryDomainAdapter. Each module can export concepts,
6
+ * rules, and workflows together, replacing the separate per-resource files.
7
+ *
8
+ * The framework surfaces domain knowledge via the domain tools:
9
+ * `get_domain_context`, `get_workflow_step`, `suggest_workflow`, `check_business_rules`.
10
+ */
11
+
12
+ import { DomainRegistry, InMemoryDomainAdapter } from '@mcp-rune/mcp-rune/domain'
13
+ import type { DomainModule } from '@mcp-rune/mcp-rune/domain'
14
+
15
+ // Define domain modules here or import them from ./modules/
16
+ // Example: import { catchupModule } from './modules/catchup.js'
17
+ const domainModules: DomainModule[] = [
18
+ // { concepts: [...], rules: [...], workflows: [...] }
19
+ ]
20
+
21
+ export function createDomainRegistry(): DomainRegistry {
22
+ return new DomainRegistry({
23
+ adapter: new InMemoryDomainAdapter(domainModules)
24
+ })
25
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * <%= projectName %> — Declarative Configuration Schema
3
+ *
4
+ * Single source of truth for all environment variables. Loaded via loadConfig()
5
+ * at startup. Every env var read in the codebase should be declared here.
6
+ */
7
+
8
+ import type { ConfigSchema } from '@mcp-rune/mcp-rune/core'
9
+
10
+ export const configSchema = {
11
+ api: {
12
+ url: {
13
+ env: 'API_URL',
14
+ default: 'http://localhost:4001/api/v1',
15
+ doc: 'Backend API base URL',
16
+ },
17
+ },
18
+
19
+ <% if (hasHttp && !useStaticTokenAuth) { -%>
20
+ oauth: {
21
+ serverUrl: {
22
+ env: 'OAUTH_SERVER_URL',
23
+ default: 'http://localhost:4000',
24
+ doc: 'OAuth2 authorization server URL',
25
+ },
26
+ clientId: {
27
+ env: 'OAUTH_CLIENT_ID',
28
+ doc: 'OAuth2 client ID',
29
+ },
30
+ clientSecret: {
31
+ env: 'OAUTH_CLIENT_SECRET',
32
+ sensitive: true,
33
+ doc: 'OAuth2 client secret',
34
+ },
35
+ scopes: {
36
+ env: 'OAUTH_SCOPES',
37
+ default: 'read write',
38
+ doc: 'OAuth2 scopes',
39
+ },
40
+ },
41
+
42
+ <% } -%>
43
+ database: {
44
+ url: {
45
+ env: 'DATABASE_URL',
46
+ sensitive: true,
47
+ doc: 'PostgreSQL connection string (optional; enables OAuth token persistence + analysis)',
48
+ },
49
+ },
50
+
51
+ analysis: {
52
+ enabled: {
53
+ env: 'ANALYSIS_ENABLED',
54
+ default: false,
55
+ type: 'boolean',
56
+ doc: 'Enable analysis tools. Requires DATABASE_URL pointing at Postgres with pgvector.',
57
+ },
58
+ },
59
+
60
+ <% if (withDomain) { -%>
61
+ domain: {
62
+ enabled: {
63
+ env: 'DOMAIN_ENABLED',
64
+ default: true,
65
+ type: 'boolean',
66
+ doc: 'Enable domain intelligence tools (workflows, business rules).',
67
+ },
68
+ },
69
+
70
+ <% } -%>
71
+ profile: {
72
+ name: {
73
+ env: 'MCP_PROFILE',
74
+ default: 'full',
75
+ doc: 'Tool exposure profile: full | chat | agent | classify',
76
+ },
77
+ },
78
+
79
+ transport: {
80
+ <% if (hasStdio) { -%>
81
+ local: {
82
+ accessToken: {
83
+ env: 'ACCESS_TOKEN',
84
+ sensitive: true,
85
+ doc: 'Bearer token used by stdio for API calls',
86
+ },
87
+ },
88
+ <% } -%>
89
+ <% if (hasHttp) { -%>
90
+ remote: {
91
+ port: { env: 'PORT', type: 'integer', default: 4100, doc: 'HTTP server port' },
92
+ baseUrl: { env: 'BASE_URL', doc: 'Public base URL for the server' },
93
+ pathPrefix: { env: 'PATH_PREFIX', default: '', doc: 'Path prefix for all routes' },
94
+ <% if (useStaticTokenAuth) { -%>
95
+ accessToken: {
96
+ env: 'HTTP_ACCESS_TOKEN',
97
+ sensitive: true,
98
+ doc: 'Static bearer token required by HTTP clients (replaces OAuth)',
99
+ },
100
+ <% } -%>
101
+ },
102
+ <% } -%>
103
+ },
104
+
105
+ http: {
106
+ corsOrigins: { env: 'CORS_ORIGINS', doc: 'Comma-separated list of allowed CORS origins' },
107
+ environment: { env: 'NODE_ENV', default: 'development', doc: 'Runtime environment' },
108
+ },
109
+
110
+ errorTracking: {
111
+ sentryDsn: { env: 'SENTRY_DSN', sensitive: true, doc: 'Sentry DSN for error tracking' },
112
+ },
113
+
114
+ tracing: {
115
+ langfusePublicKey: {
116
+ env: 'LANGFUSE_PUBLIC_KEY',
117
+ sensitive: true,
118
+ doc: 'Langfuse public key',
119
+ },
120
+ langfuseSecretKey: {
121
+ env: 'LANGFUSE_SECRET_KEY',
122
+ sensitive: true,
123
+ doc: 'Langfuse secret key',
124
+ },
125
+ },
126
+ } satisfies ConfigSchema;
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "<%= projectName %>",
3
+ "version": "0.1.0",
4
+ "description": "MCP server scaffolded with @mcp-rune/create (advanced preset)",
5
+ "type": "module",
6
+ "private": true,
7
+ "engines": {
8
+ "node": "<%= nodeEngine %>"
9
+ },
10
+ "scripts": {
11
+ <% if (hasHttp) { -%>
12
+ "start": "tsx src/servers/remote.ts",
13
+ <% } else { -%>
14
+ "start": "tsx src/servers/local.ts",
15
+ <% } -%>
16
+ <% if (hasStdio) { -%>
17
+ "start:local": "tsx src/servers/local.ts",
18
+ <% } -%>
19
+ <% if (hasHttp) { -%>
20
+ "start:remote": "tsx src/servers/remote.ts",
21
+ "start:chat": "MCP_PROFILE=chat tsx src/servers/remote.ts",
22
+ "start:agent": "MCP_PROFILE=agent tsx src/servers/remote.ts",
23
+ <% } -%>
24
+ "build": "tsc",
25
+ "inspect": "mcp-rune inspect",
26
+ "test": "vitest run",
27
+ "typecheck": "tsc --noEmit",
28
+ "db:migrate": "tsx src/scripts/db-migrate.ts"<% if (withAnalysis) { %>,
29
+ "db:up": "docker compose up -d db && npm run db:migrate",
30
+ "db:down": "docker compose down"<% } %>
31
+ },
32
+ "dependencies": {
33
+ "@mcp-rune/mcp-rune": "<%= mcpRuneVersion %>",
34
+ "dotenv": "^17.3.1",
35
+ <% if (useAxiosClient) { -%>
36
+ "axios": "^1.7.0",
37
+ <% } -%>
38
+ <% if (usePinoLogger) { -%>
39
+ "pino": "^9.5.0",
40
+ <% } -%>
41
+ "pg": "^8.18.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^24.0.0",
45
+ "@types/pg": "^8.15.0",
46
+ "tsx": "^4.22.3",
47
+ "typescript": "^5.9.3",
48
+ "vitest": "^4.1.7"
49
+ }
50
+ }