@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.
- package/LICENSE +21 -0
- package/README.md +184 -0
- package/bin/rune.js +7 -0
- package/dist/commands/add-model.d.ts +5 -0
- package/dist/commands/add-model.d.ts.map +1 -0
- package/dist/commands/add-model.js +131 -0
- package/dist/commands/add-model.js.map +1 -0
- package/dist/commands/db-up.d.ts +2 -0
- package/dist/commands/db-up.d.ts.map +1 -0
- package/dist/commands/db-up.js +29 -0
- package/dist/commands/db-up.js.map +1 -0
- package/dist/commands/doctor/env-checks.d.ts +4 -0
- package/dist/commands/doctor/env-checks.d.ts.map +1 -0
- package/dist/commands/doctor/env-checks.js +88 -0
- package/dist/commands/doctor/env-checks.js.map +1 -0
- package/dist/commands/doctor/index.d.ts +21 -0
- package/dist/commands/doctor/index.d.ts.map +1 -0
- package/dist/commands/doctor/index.js +44 -0
- package/dist/commands/doctor/index.js.map +1 -0
- package/dist/commands/doctor/project-validation.d.ts +5 -0
- package/dist/commands/doctor/project-validation.d.ts.map +1 -0
- package/dist/commands/doctor/project-validation.js +166 -0
- package/dist/commands/doctor/project-validation.js.map +1 -0
- package/dist/commands/doctor.d.ts +7 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +306 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/inspect.d.ts +16 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +66 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/new/actions/apps.d.ts +5 -0
- package/dist/commands/new/actions/apps.d.ts.map +1 -0
- package/dist/commands/new/actions/apps.js +51 -0
- package/dist/commands/new/actions/apps.js.map +1 -0
- package/dist/commands/new/actions/architecture.d.ts +5 -0
- package/dist/commands/new/actions/architecture.d.ts.map +1 -0
- package/dist/commands/new/actions/architecture.js +45 -0
- package/dist/commands/new/actions/architecture.js.map +1 -0
- package/dist/commands/new/actions/auth.d.ts +5 -0
- package/dist/commands/new/actions/auth.d.ts.map +1 -0
- package/dist/commands/new/actions/auth.js +30 -0
- package/dist/commands/new/actions/auth.js.map +1 -0
- package/dist/commands/new/actions/database.d.ts +5 -0
- package/dist/commands/new/actions/database.d.ts.map +1 -0
- package/dist/commands/new/actions/database.js +60 -0
- package/dist/commands/new/actions/database.js.map +1 -0
- package/dist/commands/new/actions/fetch-template.d.ts +5 -0
- package/dist/commands/new/actions/fetch-template.d.ts.map +1 -0
- package/dist/commands/new/actions/fetch-template.js +22 -0
- package/dist/commands/new/actions/fetch-template.js.map +1 -0
- package/dist/commands/new/actions/intro.d.ts +5 -0
- package/dist/commands/new/actions/intro.d.ts.map +1 -0
- package/dist/commands/new/actions/intro.js +7 -0
- package/dist/commands/new/actions/intro.js.map +1 -0
- package/dist/commands/new/actions/layers.d.ts +5 -0
- package/dist/commands/new/actions/layers.d.ts.map +1 -0
- package/dist/commands/new/actions/layers.js +38 -0
- package/dist/commands/new/actions/layers.js.map +1 -0
- package/dist/commands/new/actions/models.d.ts +5 -0
- package/dist/commands/new/actions/models.d.ts.map +1 -0
- package/dist/commands/new/actions/models.js +18 -0
- package/dist/commands/new/actions/models.js.map +1 -0
- package/dist/commands/new/actions/next-steps.d.ts +5 -0
- package/dist/commands/new/actions/next-steps.d.ts.map +1 -0
- package/dist/commands/new/actions/next-steps.js +38 -0
- package/dist/commands/new/actions/next-steps.js.map +1 -0
- package/dist/commands/new/actions/observability.d.ts +5 -0
- package/dist/commands/new/actions/observability.d.ts.map +1 -0
- package/dist/commands/new/actions/observability.js +45 -0
- package/dist/commands/new/actions/observability.js.map +1 -0
- package/dist/commands/new/actions/post-scaffold.d.ts +5 -0
- package/dist/commands/new/actions/post-scaffold.d.ts.map +1 -0
- package/dist/commands/new/actions/post-scaffold.js +81 -0
- package/dist/commands/new/actions/post-scaffold.js.map +1 -0
- package/dist/commands/new/actions/preset.d.ts +5 -0
- package/dist/commands/new/actions/preset.d.ts.map +1 -0
- package/dist/commands/new/actions/preset.js +23 -0
- package/dist/commands/new/actions/preset.js.map +1 -0
- package/dist/commands/new/actions/prompts.d.ts +5 -0
- package/dist/commands/new/actions/prompts.d.ts.map +1 -0
- package/dist/commands/new/actions/prompts.js +33 -0
- package/dist/commands/new/actions/prompts.js.map +1 -0
- package/dist/commands/new/actions/render.d.ts +5 -0
- package/dist/commands/new/actions/render.d.ts.map +1 -0
- package/dist/commands/new/actions/render.js +35 -0
- package/dist/commands/new/actions/render.js.map +1 -0
- package/dist/commands/new/actions/scaffold-header.d.ts +5 -0
- package/dist/commands/new/actions/scaffold-header.d.ts.map +1 -0
- package/dist/commands/new/actions/scaffold-header.js +19 -0
- package/dist/commands/new/actions/scaffold-header.js.map +1 -0
- package/dist/commands/new/actions/scaffold-mode.d.ts +5 -0
- package/dist/commands/new/actions/scaffold-mode.d.ts.map +1 -0
- package/dist/commands/new/actions/scaffold-mode.js +49 -0
- package/dist/commands/new/actions/scaffold-mode.js.map +1 -0
- package/dist/commands/new/actions/summary.d.ts +5 -0
- package/dist/commands/new/actions/summary.d.ts.map +1 -0
- package/dist/commands/new/actions/summary.js +71 -0
- package/dist/commands/new/actions/summary.js.map +1 -0
- package/dist/commands/new/actions/toggles.d.ts +5 -0
- package/dist/commands/new/actions/toggles.d.ts.map +1 -0
- package/dist/commands/new/actions/toggles.js +25 -0
- package/dist/commands/new/actions/toggles.js.map +1 -0
- package/dist/commands/new/actions/tools.d.ts +5 -0
- package/dist/commands/new/actions/tools.d.ts.map +1 -0
- package/dist/commands/new/actions/tools.js +36 -0
- package/dist/commands/new/actions/tools.js.map +1 -0
- package/dist/commands/new/actions/transport.d.ts +5 -0
- package/dist/commands/new/actions/transport.d.ts.map +1 -0
- package/dist/commands/new/actions/transport.js +24 -0
- package/dist/commands/new/actions/transport.js.map +1 -0
- package/dist/commands/new/context.d.ts +76 -0
- package/dist/commands/new/context.d.ts.map +1 -0
- package/dist/commands/new/context.js +134 -0
- package/dist/commands/new/context.js.map +1 -0
- package/dist/commands/new/index.d.ts +5 -0
- package/dist/commands/new/index.d.ts.map +1 -0
- package/dist/commands/new/index.js +18 -0
- package/dist/commands/new/index.js.map +1 -0
- package/dist/commands/new/pipeline.d.ts +12 -0
- package/dist/commands/new/pipeline.d.ts.map +1 -0
- package/dist/commands/new/pipeline.js +67 -0
- package/dist/commands/new/pipeline.js.map +1 -0
- package/dist/commands/new/presets.d.ts +40 -0
- package/dist/commands/new/presets.d.ts.map +1 -0
- package/dist/commands/new/presets.js +91 -0
- package/dist/commands/new/presets.js.map +1 -0
- package/dist/commands/new.d.ts +24 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +162 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/post-scaffold.d.ts +6 -0
- package/dist/commands/post-scaffold.d.ts.map +1 -0
- package/dist/commands/post-scaffold.js +24 -0
- package/dist/commands/post-scaffold.js.map +1 -0
- package/dist/core/cancel.d.ts +3 -0
- package/dist/core/cancel.d.ts.map +1 -0
- package/dist/core/cancel.js +17 -0
- package/dist/core/cancel.js.map +1 -0
- package/dist/core/color.d.ts +12 -0
- package/dist/core/color.d.ts.map +1 -0
- package/dist/core/color.js +14 -0
- package/dist/core/color.js.map +1 -0
- package/dist/core/db-setup.d.ts +13 -0
- package/dist/core/db-setup.d.ts.map +1 -0
- package/dist/core/db-setup.js +63 -0
- package/dist/core/db-setup.js.map +1 -0
- package/dist/core/fs-utils.d.ts +4 -0
- package/dist/core/fs-utils.d.ts.map +1 -0
- package/dist/core/fs-utils.js +31 -0
- package/dist/core/fs-utils.js.map +1 -0
- package/dist/core/output.d.ts +19 -0
- package/dist/core/output.d.ts.map +1 -0
- package/dist/core/output.js +42 -0
- package/dist/core/output.js.map +1 -0
- package/dist/core/prompts.d.ts +2 -0
- package/dist/core/prompts.d.ts.map +1 -0
- package/dist/core/prompts.js +2 -0
- package/dist/core/prompts.js.map +1 -0
- package/dist/core/tasks.d.ts +11 -0
- package/dist/core/tasks.d.ts.map +1 -0
- package/dist/core/tasks.js +28 -0
- package/dist/core/tasks.js.map +1 -0
- package/dist/data/mascot.d.ts +13 -0
- package/dist/data/mascot.d.ts.map +1 -0
- package/dist/data/mascot.js +80 -0
- package/dist/data/mascot.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/render/copy-tree.d.ts +5 -0
- package/dist/render/copy-tree.d.ts.map +1 -0
- package/dist/render/copy-tree.js +146 -0
- package/dist/render/copy-tree.js.map +1 -0
- package/dist/render/fetch-template.d.ts +9 -0
- package/dist/render/fetch-template.d.ts.map +1 -0
- package/dist/render/fetch-template.js +113 -0
- package/dist/render/fetch-template.js.map +1 -0
- package/dist/render/model-gen.d.ts +3 -0
- package/dist/render/model-gen.d.ts.map +1 -0
- package/dist/render/model-gen.js +23 -0
- package/dist/render/model-gen.js.map +1 -0
- package/dist/templates/registry.d.ts +14 -0
- package/dist/templates/registry.d.ts.map +1 -0
- package/dist/templates/registry.js +34 -0
- package/dist/templates/registry.js.map +1 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/wizard/presets.d.ts +35 -0
- package/dist/wizard/presets.d.ts.map +1 -0
- package/dist/wizard/presets.js +67 -0
- package/dist/wizard/presets.js.map +1 -0
- package/dist/wizard/questions.d.ts +11 -0
- package/dist/wizard/questions.d.ts.map +1 -0
- package/dist/wizard/questions.js +154 -0
- package/dist/wizard/questions.js.map +1 -0
- package/package.json +52 -0
- package/templates/advanced/.env.example.ejs +82 -0
- package/templates/advanced/.node-version +1 -0
- package/templates/advanced/README.md.ejs +76 -0
- package/templates/advanced/__only_if_hasHttp__/src/servers/remote.ts.ejs +36 -0
- package/templates/advanced/__only_if_hasStdio__/src/servers/local.ts +30 -0
- package/templates/advanced/__only_if_useAxiosClient__/src/api/axios-client.ts +74 -0
- package/templates/advanced/__only_if_useCustomApiClient__/src/api/custom-client.ts +48 -0
- package/templates/advanced/__only_if_useCustomConvention__/src/conventions/custom-convention.ts +64 -0
- package/templates/advanced/__only_if_useCustomSearch__/src/api-extensions/custom-search-adapter.ts +30 -0
- package/templates/advanced/__only_if_useFetchClient__/src/api/fetch-client.ts +111 -0
- package/templates/advanced/__only_if_useFlatRestConvention__/src/conventions/flat-rest-convention.ts +85 -0
- package/templates/advanced/__only_if_usePinoLogger__/src/observability/logger.ts +62 -0
- package/templates/advanced/__only_if_useRansackSearch__/src/api-extensions/ransack-search-adapter.ts +41 -0
- package/templates/advanced/__only_if_useSharedModelAttrs__/src/models/app-base-model.ts +22 -0
- package/templates/advanced/__only_if_useVectorStorage__/src/storage/vector-store.ts +21 -0
- package/templates/advanced/__only_if_withAnalysis__/docker-compose.yml +18 -0
- package/templates/advanced/__only_if_withDomain__/src/domain/registry.ts +25 -0
- package/templates/advanced/config/schema.ts.ejs +126 -0
- package/templates/advanced/package.json.ejs +50 -0
- package/templates/advanced/src/config.ts.ejs +207 -0
- package/templates/advanced/src/db.ts.ejs +35 -0
- package/templates/advanced/src/models/_model_.ts.ejs +25 -0
- package/templates/advanced/src/models/index.ts.ejs +13 -0
- package/templates/advanced/src/profiles.ts +44 -0
- package/templates/advanced/src/prompts/_model_-prompt.ts.ejs +27 -0
- package/templates/advanced/src/prompts/index.ts.ejs +18 -0
- package/templates/advanced/src/scripts/db-migrate.ts +90 -0
- package/templates/advanced/src/tools/index.ts.ejs +133 -0
- package/templates/advanced/test/smoke.test.ts +16 -0
- package/templates/advanced/tsconfig.json +14 -0
- package/templates/simple/.env.example +3 -0
- package/templates/simple/.node-version +1 -0
- package/templates/simple/README.md +40 -0
- package/templates/simple/package.json.ejs +27 -0
- package/templates/simple/src/config.ts.ejs +56 -0
- package/templates/simple/src/models/_model_.ts.ejs +25 -0
- package/templates/simple/src/models/index.ts.ejs +13 -0
- package/templates/simple/src/prompts/_model_-prompt.ts.ejs +27 -0
- package/templates/simple/src/prompts/index.ts.ejs +18 -0
- package/templates/simple/src/server.ts +12 -0
- package/templates/simple/test/smoke.test.ts +16 -0
- 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
|
+
}
|
package/templates/advanced/__only_if_useFlatRestConvention__/src/conventions/flat-rest-convention.ts
ADDED
|
@@ -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
|
+
}
|
package/templates/advanced/__only_if_useRansackSearch__/src/api-extensions/ransack-search-adapter.ts
ADDED
|
@@ -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
|
+
}
|