@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,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":"AAAA,OAAO,EAAE,KAAK,IAAI,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAqB,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAgB7D,MAAM,qBAAqB,GAA2B;IACpD,SAAS,EAAE,wFAAwF;IACnG,KAAK,EAAE,iGAAiG;IACxG,gBAAgB,EAAE,+FAA+F;IACjH,iBAAiB,EAAE,4GAA4G;IAC/H,kBAAkB,EAAE,kFAAkF;CACvG,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAwB;QAC/C,OAAO,EAAE,8BAA8B;QACvC,OAAO,EAAE;YACP,EAAE,IAAI,EAAE,4CAA4C,EAAE,KAAK,EAAE,QAAQ,EAAE;YACvE,EAAE,IAAI,EAAE,6CAA6C,EAAE,KAAK,EAAE,UAAU,EAAE;SAC3E;QACD,OAAO,EAAE,QAAQ;KAClB,CAAC,CAAC;IAEH,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAEjD,MAAM,EAAE,GAAG,MAAM,MAAM,CAAS;QAC9B,OAAO,EAAE,iBAAiB;QAC1B,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACrD,IAAI,EAAE,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;YACrF,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;KACJ,CAAC,CAAC;IAEH,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAqB;IACnD,MAAM,MAAM,GACT,OAAO,CAAC,MAA6B;QACtC,CAAC,MAAM,MAAM,CAAS;YACpB,OAAO,EAAE,eAAe;YACxB,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,uCAAuC,EAAE,KAAK,EAAE,QAAQ,EAAE;gBAClE,EAAE,IAAI,EAAE,oDAAoD,EAAE,KAAK,EAAE,UAAU,EAAE;aAClF;YACD,OAAO,EAAE,QAAQ;SAClB,CAAC,CAAC,CAAC;IAEN,IAAI,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IAC/B,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,SAAS,GAAG,MAAM,OAAO,CAAC;YACxB,OAAO,EAAE,sDAAsD;YAC/D,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GAWV,EAAE,CAAC;IAEP,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAC1B,QAAQ,CAAC,SAAS;YAChB,OAAO,CAAC,SAAS;gBACjB,CAAC,MAAM,MAAM,CAAY;oBACvB,OAAO,EAAE,YAAY;oBACrB,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,+CAA+C,EAAE,KAAK,EAAE,OAAO,EAAE;wBACzE,EAAE,IAAI,EAAE,2BAA2B,EAAE,KAAK,EAAE,MAAM,EAAE;wBACpD,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,EAAE;qBAC9C;oBACD,OAAO,EAAE,MAAM;iBAChB,CAAC,CAAC,CAAC;QAEN,QAAQ,CAAC,YAAY;YACnB,OAAO,CAAC,YAAY;gBACpB,CAAC,MAAM,OAAO,CAAC;oBACb,OAAO,EAAE,4DAA4D;oBACrE,OAAO,EAAE,KAAK;iBACf,CAAC,CAAC,CAAC;QAEN,QAAQ,CAAC,UAAU;YACjB,OAAO,CAAC,UAAU;gBAClB,CAAC,MAAM,OAAO,CAAC;oBACb,OAAO,EAAE,sDAAsD;oBAC/D,OAAO,EAAE,KAAK;iBACf,CAAC,CAAC,CAAC;QAEN,uEAAuE;QACvE,QAAQ,CAAC,aAAa;YACpB,OAAO,CAAC,aAAa;gBACrB,CAAC,MAAM,MAAM,CAAgB;oBAC3B,OAAO,EAAE,iBAAiB;oBAC1B,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,oDAAoD,EAAE,KAAK,EAAE,SAAS,EAAE;wBAChF,EAAE,IAAI,EAAE,8CAA8C,EAAE,KAAK,EAAE,WAAW,EAAE;qBAC7E;oBACD,OAAO,EAAE,SAAS;iBACnB,CAAC,CAAC,CAAC;QAEN,QAAQ,CAAC,SAAS;YAChB,OAAO,CAAC,SAAS;gBACjB,CAAC,MAAM,MAAM,CAAkB;oBAC7B,OAAO,EAAE,aAAa;oBACtB,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,+CAA+C,EAAE,KAAK,EAAE,MAAM,EAAE;wBACxE,EAAE,IAAI,EAAE,mDAAmD,EAAE,KAAK,EAAE,OAAO,EAAE;qBAC9E;oBACD,OAAO,EAAE,MAAM;iBAChB,CAAC,CAAC,CAAC;QAEN,QAAQ,CAAC,aAAa;YACpB,OAAO,CAAC,aAAa;gBACrB,CAAC,MAAM,MAAM,CAAsB;oBACjC,OAAO,EAAE,iBAAiB;oBAC1B,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,+CAA+C,EAAE,KAAK,EAAE,MAAM,EAAE;wBACxE,EAAE,IAAI,EAAE,mDAAmD,EAAE,KAAK,EAAE,SAAS,EAAE;qBAChF;oBACD,OAAO,EAAE,MAAM;iBAChB,CAAC,CAAC,CAAC;QAEN,MAAM,gBAAgB,GACpB,QAAQ,CAAC,SAAS,KAAK,MAAM,IAAI,QAAQ,CAAC,SAAS,KAAK,MAAM,CAAC;QACjE,IAAI,gBAAgB,EAAE,CAAC;YACrB,QAAQ,CAAC,UAAU;gBACjB,OAAO,CAAC,UAAU;oBAClB,CAAC,MAAM,MAAM,CAAa;wBACxB,OAAO,EAAE,mBAAmB;wBAC5B,OAAO,EAAE;4BACP,EAAE,IAAI,EAAE,+CAA+C,EAAE,KAAK,EAAE,OAAO,EAAE;4BACzE,EAAE,IAAI,EAAE,qDAAqD,EAAE,KAAK,EAAE,cAAc,EAAE;yBACvF;wBACD,OAAO,EAAE,OAAO;qBACjB,CAAC,CAAC,CAAC;QACR,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC;QACtD,CAAC;QAED,uEAAuE;QACvE,QAAQ,CAAC,MAAM;YACb,OAAO,CAAC,MAAM;gBACd,CAAC,MAAM,MAAM,CAAe;oBAC1B,OAAO,EAAE,SAAS;oBAClB,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,6CAA6C,EAAE,KAAK,EAAE,WAAW,EAAE;wBAC3E,EAAE,IAAI,EAAE,iDAAiD,EAAE,KAAK,EAAE,MAAM,EAAE;qBAC3E;oBACD,OAAO,EAAE,WAAW;iBACrB,CAAC,CAAC,CAAC;QAEN,QAAQ,CAAC,aAAa;YACpB,OAAO,CAAC,aAAa;gBACrB,CAAC,MAAM,MAAM,CAAsB;oBACjC,OAAO,EAAE,iBAAiB;oBAC1B,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,0BAA0B,EAAE,KAAK,EAAE,MAAM,EAAE;wBACnD,EAAE,IAAI,EAAE,kDAAkD,EAAE,KAAK,EAAE,QAAQ,EAAE;qBAC9E;oBACD,OAAO,EAAE,MAAM;iBAChB,CAAC,CAAC,CAAC;QAEN,QAAQ,CAAC,OAAO;YACd,OAAO,CAAC,OAAO;gBACf,CAAC,MAAM,MAAM,CAAgB;oBAC3B,OAAO,EAAE,UAAU;oBACnB,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,sCAAsC,EAAE,KAAK,EAAE,MAAM,EAAE;wBAC/D,EAAE,IAAI,EAAE,yDAAyD,EAAE,KAAK,EAAE,UAAU,EAAE;qBACvF;oBACD,OAAO,EAAE,MAAM;iBAChB,CAAC,CAAC,CAAC;IACR,CAAC;IAED,OAAO,cAAc,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;AAChF,CAAC"}
|
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
|
+
}
|
package/templates/advanced/__only_if_useCustomConvention__/src/conventions/custom-convention.ts
ADDED
|
@@ -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()
|
package/templates/advanced/__only_if_useCustomSearch__/src/api-extensions/custom-search-adapter.ts
ADDED
|
@@ -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()
|