@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,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <%= projectName %> — shared configuration and startup wiring.
|
|
3
|
+
*
|
|
4
|
+
* Startup phases are wrapped with StartupTracker for clear boundary markers
|
|
5
|
+
* and scoped logging. Each phase gets a scoped child logger.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { loadConfig, readPackageInfo, StartupTracker } from '@mcp-rune/mcp-rune/core'
|
|
9
|
+
import { createPromptCache } from '@mcp-rune/mcp-rune/prompts'
|
|
10
|
+
import { createServer as createServerFactory } from '@mcp-rune/mcp-rune/server'
|
|
11
|
+
<% if (hasHttp && !useStaticTokenAuth) { -%>
|
|
12
|
+
import { _setAdapter, OAuthService, PostgresqlAdapter } from '@mcp-rune/mcp-rune/oauth2'
|
|
13
|
+
<% } -%>
|
|
14
|
+
<% if (usePinoLogger) { -%>
|
|
15
|
+
import { errorTracking, tracing, vectorStorage } from '@mcp-rune/mcp-rune/runtime'
|
|
16
|
+
<% } else { -%>
|
|
17
|
+
import { errorTracking, logger, tracing, vectorStorage } from '@mcp-rune/mcp-rune/runtime'
|
|
18
|
+
<% } -%>
|
|
19
|
+
import { createPgvectorAdapter } from '@mcp-rune/mcp-rune/runtime/vendor/pgvector'
|
|
20
|
+
|
|
21
|
+
import { configSchema } from '../config/schema.js'
|
|
22
|
+
import { closeDatabase, initDatabase } from './db.js'
|
|
23
|
+
<% if (withDomain) { -%>
|
|
24
|
+
import { createDomainRegistry } from './domain/registry.js'
|
|
25
|
+
<% } -%>
|
|
26
|
+
import { MODEL_CLASSES } from './models/index.js'
|
|
27
|
+
<% if (usePinoLogger) { -%>
|
|
28
|
+
import { logger } from './observability/logger.js'
|
|
29
|
+
<% } -%>
|
|
30
|
+
import { getProfile } from './profiles.js'
|
|
31
|
+
import { promptRegistry as basePromptRegistry } from './prompts/index.js'
|
|
32
|
+
import { createToolRegistry } from './tools/index.js'
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Runtime-resolved shape of `configSchema`. Mirrors the schema tree; the
|
|
36
|
+
* library's `loadConfig` returns `Record<string, unknown>` so we narrow at
|
|
37
|
+
* the boundary with a single explicit cast and pay no cost downstream.
|
|
38
|
+
*/
|
|
39
|
+
interface AppConfig {
|
|
40
|
+
api: { url: string }
|
|
41
|
+
<% if (hasHttp && !useStaticTokenAuth) { -%>
|
|
42
|
+
oauth: {
|
|
43
|
+
serverUrl: string
|
|
44
|
+
clientId: string
|
|
45
|
+
clientSecret: string
|
|
46
|
+
scopes: string
|
|
47
|
+
}
|
|
48
|
+
<% } -%>
|
|
49
|
+
database: { url: string | undefined }
|
|
50
|
+
analysis: { enabled: boolean }
|
|
51
|
+
<% if (withDomain) { -%>
|
|
52
|
+
domain: { enabled: boolean }
|
|
53
|
+
<% } -%>
|
|
54
|
+
profile: { name: string }
|
|
55
|
+
transport: {
|
|
56
|
+
<% if (hasStdio) { -%>
|
|
57
|
+
local: { accessToken: string | undefined }
|
|
58
|
+
<% } -%>
|
|
59
|
+
<% if (hasHttp) { -%>
|
|
60
|
+
remote: {
|
|
61
|
+
port: number
|
|
62
|
+
baseUrl: string | undefined
|
|
63
|
+
pathPrefix: string
|
|
64
|
+
<% if (useStaticTokenAuth) { -%>
|
|
65
|
+
accessToken: string | undefined
|
|
66
|
+
<% } -%>
|
|
67
|
+
}
|
|
68
|
+
<% } -%>
|
|
69
|
+
}
|
|
70
|
+
http: { corsOrigins: string | undefined; environment: string }
|
|
71
|
+
errorTracking: { sentryDsn: string | undefined }
|
|
72
|
+
tracing: { langfusePublicKey: string | undefined; langfuseSecretKey: string | undefined }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const startup = new StartupTracker(logger)
|
|
76
|
+
|
|
77
|
+
const { name: SERVER_NAME, version: SERVER_VERSION } = startup.phase(
|
|
78
|
+
'identity',
|
|
79
|
+
'Server identity',
|
|
80
|
+
(log) => {
|
|
81
|
+
const pkg = readPackageInfo(import.meta.url, '../package.json')
|
|
82
|
+
logger.setApp(pkg.name)
|
|
83
|
+
log.debug(`${pkg.name}@${pkg.version}`)
|
|
84
|
+
return pkg
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
export const config = startup.phase('config', 'Load configuration', (log) => {
|
|
89
|
+
const cfg = loadConfig(configSchema)
|
|
90
|
+
log.debug(cfg.toString())
|
|
91
|
+
return cfg as unknown as AppConfig & { toString(): string }
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const isProduction = config.http.environment === 'production'
|
|
95
|
+
|
|
96
|
+
startup.phase('error-tracking', 'Error tracking', () => {
|
|
97
|
+
// Vendor (Sentry) reads SENTRY_DSN + NODE_ENV from env directly; the
|
|
98
|
+
// public option type here is intentionally narrow.
|
|
99
|
+
errorTracking.initErrorTracking({
|
|
100
|
+
serviceName: SERVER_NAME,
|
|
101
|
+
version: SERVER_VERSION
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
startup.phase('tracing', 'Tracing', () => {
|
|
106
|
+
// Vendor (Langfuse) reads LANGFUSE_PUBLIC_KEY + LANGFUSE_SECRET_KEY from
|
|
107
|
+
// env directly; the public option type here is intentionally narrow.
|
|
108
|
+
tracing.initTracing({
|
|
109
|
+
serviceName: SERVER_NAME,
|
|
110
|
+
version: SERVER_VERSION
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
startup.phase('database', 'Database', (log) => {
|
|
115
|
+
const pool = initDatabase(config.database.url)
|
|
116
|
+
if (!pool) {
|
|
117
|
+
log.debug('DATABASE_URL not set — database features disabled')
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
<% if (hasHttp && !useStaticTokenAuth) { -%>
|
|
122
|
+
_setAdapter(new PostgresqlAdapter({ pool }))
|
|
123
|
+
log.debug('OAuth token store connected')
|
|
124
|
+
|
|
125
|
+
<% } -%>
|
|
126
|
+
if (config.analysis.enabled) {
|
|
127
|
+
vectorStorage.initVectorStorage({
|
|
128
|
+
adapter: createPgvectorAdapter({ pool }),
|
|
129
|
+
serviceName: SERVER_NAME,
|
|
130
|
+
version: SERVER_VERSION,
|
|
131
|
+
})
|
|
132
|
+
log.debug('Vector storage enabled')
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
<% if (hasHttp && !useStaticTokenAuth) { -%>
|
|
137
|
+
export function createOAuthService({ redirectUri }: { redirectUri: string }): OAuthService {
|
|
138
|
+
return new OAuthService({
|
|
139
|
+
authServerUrl: config.oauth.serverUrl,
|
|
140
|
+
clientId: config.oauth.clientId,
|
|
141
|
+
clientSecret: config.oauth.clientSecret,
|
|
142
|
+
redirectUri,
|
|
143
|
+
scopes: config.oauth.scopes,
|
|
144
|
+
isProduction
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
<% } -%>
|
|
149
|
+
const promptRegistry = startup.phase('prompts', 'Prompts', () => {
|
|
150
|
+
return createPromptCache(basePromptRegistry, { ttl: 5 * 60 * 1000, maxSize: 100 })
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
<% if (withDomain) { -%>
|
|
154
|
+
const domainRegistry = config.domain.enabled
|
|
155
|
+
? startup.phase('domain', 'Domain intelligence', () => createDomainRegistry())
|
|
156
|
+
: null
|
|
157
|
+
<% } else { -%>
|
|
158
|
+
const domainRegistry = null
|
|
159
|
+
<% } -%>
|
|
160
|
+
|
|
161
|
+
const serverContext = {
|
|
162
|
+
name: '<%= projectNamePascal %>',
|
|
163
|
+
namespace: '<%= projectName %>',
|
|
164
|
+
description: 'MCP server scaffolded with @mcp-rune/create'
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const profile = getProfile(config.profile.name)
|
|
168
|
+
|
|
169
|
+
const toolRegistry = startup.phase('tools', 'Tools', (log) => {
|
|
170
|
+
log.debug(`Profile: ${config.profile.name}`)
|
|
171
|
+
return createToolRegistry({
|
|
172
|
+
logger,
|
|
173
|
+
models: MODEL_CLASSES,
|
|
174
|
+
promptRegistry,
|
|
175
|
+
serverContext,
|
|
176
|
+
domainRegistry,
|
|
177
|
+
apiUrl: config.api.url,
|
|
178
|
+
profile
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
startup.done()
|
|
183
|
+
|
|
184
|
+
export const mcpConfig = {
|
|
185
|
+
name: SERVER_NAME,
|
|
186
|
+
promptRegistry,
|
|
187
|
+
closeDatabase,
|
|
188
|
+
|
|
189
|
+
createServer({
|
|
190
|
+
sessionId: _sessionId,
|
|
191
|
+
getAccessToken
|
|
192
|
+
}: {
|
|
193
|
+
sessionId: string
|
|
194
|
+
transport: string
|
|
195
|
+
getAccessToken: () => Promise<string>
|
|
196
|
+
}) {
|
|
197
|
+
return createServerFactory({
|
|
198
|
+
name: SERVER_NAME,
|
|
199
|
+
version: SERVER_VERSION,
|
|
200
|
+
sessionId: _sessionId,
|
|
201
|
+
transport: 'stdio',
|
|
202
|
+
toolRegistry,
|
|
203
|
+
promptRegistry,
|
|
204
|
+
getAccessToken
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import pg from 'pg'
|
|
2
|
+
|
|
3
|
+
type Pool = InstanceType<typeof pg.Pool>
|
|
4
|
+
<% if (usePinoLogger) { -%>
|
|
5
|
+
import { logger } from './observability/logger.js'
|
|
6
|
+
<% } else { -%>
|
|
7
|
+
import { logger } from '@mcp-rune/mcp-rune/runtime'
|
|
8
|
+
<% } -%>
|
|
9
|
+
|
|
10
|
+
let pool: Pool | null = null
|
|
11
|
+
|
|
12
|
+
export function initDatabase(url: string | undefined): Pool | null {
|
|
13
|
+
if (pool) return pool
|
|
14
|
+
if (!url) return null
|
|
15
|
+
|
|
16
|
+
pool = new pg.Pool({
|
|
17
|
+
connectionString: url,
|
|
18
|
+
max: 5,
|
|
19
|
+
idleTimeoutMillis: 30000,
|
|
20
|
+
connectionTimeoutMillis: 5000
|
|
21
|
+
})
|
|
22
|
+
logger.info('Database pool created', { service: '<%= projectName %>-db' })
|
|
23
|
+
return pool
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getPool(): Pool | null {
|
|
27
|
+
return pool
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function closeDatabase(): Promise<void> {
|
|
31
|
+
if (!pool) return
|
|
32
|
+
await pool.end()
|
|
33
|
+
pool = null
|
|
34
|
+
logger.info('Database pool closed', { service: '<%= projectName %>-db' })
|
|
35
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AttributeDefinition } from '@mcp-rune/mcp-rune/models'
|
|
2
|
+
import { BaseModel } from '@mcp-rune/mcp-rune/models'
|
|
3
|
+
|
|
4
|
+
export class <%= model.namePascal %> extends BaseModel {
|
|
5
|
+
static override description = '<%= model.description || `A ${model.name} record` %>'
|
|
6
|
+
static override api = { endpoint: '<%= model.endpoint || `${model.fileName}s` %>' }
|
|
7
|
+
|
|
8
|
+
static override attributes: Record<string, AttributeDefinition> = {
|
|
9
|
+
<% for (const attr of model.attributes) { -%>
|
|
10
|
+
<%= attr.name %>: {
|
|
11
|
+
type: '<%= attr.type %>',
|
|
12
|
+
<% if (attr.required) { -%>
|
|
13
|
+
required: true,
|
|
14
|
+
<% } -%>
|
|
15
|
+
<% if (attr.description) { -%>
|
|
16
|
+
description: '<%= attr.description %>',
|
|
17
|
+
<% } -%>
|
|
18
|
+
},
|
|
19
|
+
<% } -%>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static get attributesConfig(): Record<string, AttributeDefinition> {
|
|
23
|
+
return this.attributes
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<% for (const m of models) { -%>
|
|
2
|
+
import { <%= m.namePascal %> } from './<%= m.fileName %>.js'
|
|
3
|
+
<% } -%>
|
|
4
|
+
|
|
5
|
+
export const MODEL_CLASSES = {
|
|
6
|
+
<% for (const m of models) { -%>
|
|
7
|
+
<%= m.fileName %>: <%= m.namePascal %>,
|
|
8
|
+
<% } -%>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
<% for (const m of models) { -%>
|
|
12
|
+
export { <%= m.namePascal %> }
|
|
13
|
+
<% } -%>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool exposure profiles.
|
|
3
|
+
*
|
|
4
|
+
* Selected at startup via MCP_PROFILE. Same image, same binary, different surface.
|
|
5
|
+
*
|
|
6
|
+
* - `full` — everything (default).
|
|
7
|
+
* - `chat` — apps enabled; raw-JSON data tools hidden so the LLM doesn't echo
|
|
8
|
+
* records that an app has already rendered.
|
|
9
|
+
* - `agent` — apps disabled; data and action tools enabled for headless callers.
|
|
10
|
+
* - `classify` — only `update_model` is exposed (token-minimal classification).
|
|
11
|
+
*
|
|
12
|
+
* Each profile carries:
|
|
13
|
+
* - `tools` — allowlist of tool names (null = all enabled tools)
|
|
14
|
+
* - `toolsExclude` — denylist applied after allowlist (null = no exclusions)
|
|
15
|
+
* - `apps` — 'enabled' | 'disabled'
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export interface Profile {
|
|
19
|
+
tools: string[] | null
|
|
20
|
+
toolsExclude: string[] | null
|
|
21
|
+
apps: 'enabled' | 'disabled'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const PROFILES = {
|
|
25
|
+
full: { tools: null, toolsExclude: null, apps: 'enabled' },
|
|
26
|
+
chat: {
|
|
27
|
+
tools: null,
|
|
28
|
+
toolsExclude: ['find_records', 'search_records', 'list_models'],
|
|
29
|
+
apps: 'enabled'
|
|
30
|
+
},
|
|
31
|
+
agent: { tools: null, toolsExclude: null, apps: 'disabled' },
|
|
32
|
+
classify: { tools: ['update_model'], toolsExclude: null, apps: 'disabled' }
|
|
33
|
+
} as const satisfies Record<string, Profile>
|
|
34
|
+
|
|
35
|
+
export type ProfileName = keyof typeof PROFILES
|
|
36
|
+
|
|
37
|
+
export function getProfile(name: string): Profile {
|
|
38
|
+
if (!(name in PROFILES)) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Unknown MCP_PROFILE "${name}". Valid: ${Object.keys(PROFILES).join(', ')}`
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
return PROFILES[name as ProfileName]
|
|
44
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BasePrompt,
|
|
3
|
+
derivePromptSchema,
|
|
4
|
+
PromptContentBuilder
|
|
5
|
+
} from '@mcp-rune/mcp-rune/prompts'
|
|
6
|
+
import type { FormStrategyType } from '@mcp-rune/mcp-rune/prompts'
|
|
7
|
+
|
|
8
|
+
import { <%= model.namePascal %> } from '../models/<%= model.fileName %>.js'
|
|
9
|
+
|
|
10
|
+
export class <%= model.namePascal %>Prompt extends BasePrompt {
|
|
11
|
+
static override formStrategy: FormStrategyType = 'hybrid'
|
|
12
|
+
|
|
13
|
+
static {
|
|
14
|
+
const schema = derivePromptSchema(<%= model.namePascal %>)
|
|
15
|
+
this.fieldGroups = schema.fieldGroups
|
|
16
|
+
this.fieldDefinitions = schema.fieldDefinitions
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override get promptContent(): string {
|
|
20
|
+
return PromptContentBuilder.for(<%= model.namePascal %>Prompt, '<%= model.fileName %>')
|
|
21
|
+
.add('# <%= model.namePascal %> guide')
|
|
22
|
+
.standard()
|
|
23
|
+
.toolUsage()
|
|
24
|
+
.attributeReference()
|
|
25
|
+
.build()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BasePromptRegistry } from '@mcp-rune/mcp-rune/prompts'
|
|
2
|
+
|
|
3
|
+
<% for (const m of models) { -%>
|
|
4
|
+
import { <%= m.namePascal %> } from '../models/<%= m.fileName %>.js'
|
|
5
|
+
<% } -%>
|
|
6
|
+
<% for (const m of models) { -%>
|
|
7
|
+
import { <%= m.namePascal %>Prompt } from './<%= m.fileName %>-prompt.js'
|
|
8
|
+
<% } -%>
|
|
9
|
+
|
|
10
|
+
export const promptRegistry = new BasePromptRegistry()
|
|
11
|
+
|
|
12
|
+
<% for (const m of models) { -%>
|
|
13
|
+
promptRegistry.register('create_<%= m.fileName %>', <%= m.namePascal %>Prompt, {
|
|
14
|
+
description: <%= m.namePascal %>.description,
|
|
15
|
+
required: false,
|
|
16
|
+
model: '<%= m.fileName %>'
|
|
17
|
+
})
|
|
18
|
+
<% } -%>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Apply mcp-rune database migrations against DATABASE_URL.
|
|
4
|
+
* Tracks applied migrations in a schema_migrations table.
|
|
5
|
+
*/
|
|
6
|
+
import { resolve } from 'node:path'
|
|
7
|
+
|
|
8
|
+
import { migrations } from '@mcp-rune/mcp-rune/db/migrations'
|
|
9
|
+
import pg from 'pg'
|
|
10
|
+
|
|
11
|
+
function maskUrl(url: string): string {
|
|
12
|
+
try {
|
|
13
|
+
const u = new URL(url)
|
|
14
|
+
if (u.username || u.password) {
|
|
15
|
+
u.username = '***'
|
|
16
|
+
u.password = ''
|
|
17
|
+
}
|
|
18
|
+
return u.toString().replace(/\/$/, '')
|
|
19
|
+
} catch {
|
|
20
|
+
return '***'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function loadEnv(): void {
|
|
25
|
+
try {
|
|
26
|
+
process.loadEnvFile(resolve('.env'))
|
|
27
|
+
} catch {
|
|
28
|
+
/* optional */
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function migrate(): Promise<void> {
|
|
33
|
+
loadEnv()
|
|
34
|
+
const url = process.env.DATABASE_URL
|
|
35
|
+
if (!url) {
|
|
36
|
+
console.log('DATABASE_URL not set — skipping')
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
console.log(`Connecting to ${maskUrl(url)} …`)
|
|
40
|
+
const pool = new pg.Pool({ connectionString: url })
|
|
41
|
+
try {
|
|
42
|
+
const client = await pool.connect()
|
|
43
|
+
try {
|
|
44
|
+
await client.query(`
|
|
45
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
46
|
+
version TEXT PRIMARY KEY,
|
|
47
|
+
name TEXT NOT NULL,
|
|
48
|
+
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
49
|
+
)
|
|
50
|
+
`)
|
|
51
|
+
const { rows } = await client.query<{ version: string }>(
|
|
52
|
+
`SELECT version FROM schema_migrations`
|
|
53
|
+
)
|
|
54
|
+
const applied = new Set(rows.map((r) => r.version))
|
|
55
|
+
let count = 0
|
|
56
|
+
for (const m of migrations) {
|
|
57
|
+
if (applied.has(m.version)) {
|
|
58
|
+
console.log(` skip ${m.version}_${m.name}`)
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
console.log(` apply ${m.version}_${m.name}`)
|
|
62
|
+
await client.query('BEGIN')
|
|
63
|
+
try {
|
|
64
|
+
await client.query(m.up)
|
|
65
|
+
await client.query(
|
|
66
|
+
`INSERT INTO schema_migrations (version, name) VALUES ($1, $2)`,
|
|
67
|
+
[m.version, m.name]
|
|
68
|
+
)
|
|
69
|
+
await client.query('COMMIT')
|
|
70
|
+
count++
|
|
71
|
+
} catch (err) {
|
|
72
|
+
await client.query('ROLLBACK')
|
|
73
|
+
throw err
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
console.log(
|
|
77
|
+
count === 0 ? 'All migrations already applied.' : `Applied ${count} migration(s).`
|
|
78
|
+
)
|
|
79
|
+
} finally {
|
|
80
|
+
client.release()
|
|
81
|
+
}
|
|
82
|
+
} finally {
|
|
83
|
+
await pool.end()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
migrate().catch((err: Error) => {
|
|
88
|
+
console.error(`Migration failed: ${err.message}`)
|
|
89
|
+
process.exit(1)
|
|
90
|
+
})
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool registry — wraps the library's `ToolRegistry` with an active-profile
|
|
3
|
+
* filter (MCP_PROFILE) so deployments can expose a narrower tool surface
|
|
4
|
+
* than the full set without touching the registry plumbing.
|
|
5
|
+
*
|
|
6
|
+
* The library's `ToolRegistry` already handles capability gating:
|
|
7
|
+
* - `requiresVectorStorage`: skipped when vector storage isn't configured.
|
|
8
|
+
* - `requiresDomainRegistry`: skipped when no `domainRegistry` is passed.
|
|
9
|
+
* - `requiresPromptRegistry`: skipped when no `promptRegistry` is passed.
|
|
10
|
+
* The profile filter layers on top of that with per-deployment allow/deny lists.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ApiClient } from '@mcp-rune/mcp-rune/core'
|
|
14
|
+
import type { DomainRegistry } from '@mcp-rune/mcp-rune/domain'
|
|
15
|
+
import type { PromptRegistry } from '@mcp-rune/mcp-rune/prompts'
|
|
16
|
+
import { vectorStorage } from '@mcp-rune/mcp-rune/runtime'
|
|
17
|
+
import type { ModelsRegistry, ServerContext, ToolClassMap } from '@mcp-rune/mcp-rune/tools'
|
|
18
|
+
import {
|
|
19
|
+
ANALYSIS_TOOL_CLASSES,
|
|
20
|
+
DATA_TOOL_CLASSES,
|
|
21
|
+
DOMAIN_TOOL_CLASSES,
|
|
22
|
+
FORM_STRATEGY_TOOL_CLASSES,
|
|
23
|
+
OPERATIONS_TOOL_CLASSES,
|
|
24
|
+
ToolRegistry
|
|
25
|
+
} from '@mcp-rune/mcp-rune/tools'
|
|
26
|
+
|
|
27
|
+
<% if (useFetchClient) { -%>
|
|
28
|
+
import { FetchApiClient } from '../api/fetch-client.js'
|
|
29
|
+
<% } -%>
|
|
30
|
+
<% if (useAxiosClient) { -%>
|
|
31
|
+
import { AxiosApiClient } from '../api/axios-client.js'
|
|
32
|
+
<% } -%>
|
|
33
|
+
<% if (useCustomApiClient) { -%>
|
|
34
|
+
import { CustomApiClient } from '../api/custom-client.js'
|
|
35
|
+
<% } -%>
|
|
36
|
+
<% if (useFlatRestConvention) { -%>
|
|
37
|
+
import { flatRestConvention } from '../conventions/flat-rest-convention.js'
|
|
38
|
+
<% } -%>
|
|
39
|
+
<% if (useCustomConvention) { -%>
|
|
40
|
+
import { customConvention } from '../conventions/custom-convention.js'
|
|
41
|
+
<% } -%>
|
|
42
|
+
<% if (useRansackSearch) { -%>
|
|
43
|
+
import { ransackSearchAdapter } from '../api-extensions/ransack-search-adapter.js'
|
|
44
|
+
<% } -%>
|
|
45
|
+
<% if (useCustomSearch) { -%>
|
|
46
|
+
import { customSearchAdapter } from '../api-extensions/custom-search-adapter.js'
|
|
47
|
+
<% } -%>
|
|
48
|
+
|
|
49
|
+
const ALL_TOOL_CLASSES: ToolClassMap = {
|
|
50
|
+
...FORM_STRATEGY_TOOL_CLASSES,
|
|
51
|
+
...DATA_TOOL_CLASSES,
|
|
52
|
+
...ANALYSIS_TOOL_CLASSES,
|
|
53
|
+
...OPERATIONS_TOOL_CLASSES,
|
|
54
|
+
...DOMAIN_TOOL_CLASSES
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface Profile {
|
|
58
|
+
tools?: string[] | null
|
|
59
|
+
toolsExclude?: string[] | null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface CreateToolRegistryDeps {
|
|
63
|
+
logger: {
|
|
64
|
+
info(message: string, meta?: Record<string, unknown>): void
|
|
65
|
+
warn(message: string, meta?: Record<string, unknown>): void
|
|
66
|
+
error(message: string, meta?: Record<string, unknown>): void
|
|
67
|
+
debug(message: string, meta?: Record<string, unknown>): void
|
|
68
|
+
}
|
|
69
|
+
models: ModelsRegistry
|
|
70
|
+
promptRegistry: PromptRegistry
|
|
71
|
+
serverContext: ServerContext
|
|
72
|
+
domainRegistry: DomainRegistry | null
|
|
73
|
+
apiUrl: string
|
|
74
|
+
profile: Profile
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function selectToolClasses(profile: Profile): ToolClassMap {
|
|
78
|
+
const all = Object.keys(ALL_TOOL_CLASSES)
|
|
79
|
+
const allow = profile.tools ? new Set(profile.tools) : null
|
|
80
|
+
const deny = profile.toolsExclude ? new Set(profile.toolsExclude) : null
|
|
81
|
+
const selected: ToolClassMap = {}
|
|
82
|
+
for (const name of all) {
|
|
83
|
+
if (allow && !allow.has(name)) continue
|
|
84
|
+
if (deny?.has(name)) continue
|
|
85
|
+
selected[name] = ALL_TOOL_CLASSES[name]!
|
|
86
|
+
}
|
|
87
|
+
return selected
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function createToolRegistry(deps: CreateToolRegistryDeps): ToolRegistry {
|
|
91
|
+
const createApiClient = (token: string): ApiClient => {
|
|
92
|
+
<% const conventionExpr = useFlatRestConvention ? 'flatRestConvention' : useCustomConvention ? 'customConvention' : null -%>
|
|
93
|
+
<% if (useFetchClient) { -%>
|
|
94
|
+
return new FetchApiClient({
|
|
95
|
+
baseUrl: deps.apiUrl,
|
|
96
|
+
accessToken: token<% if (conventionExpr) { %>,
|
|
97
|
+
convention: <%= conventionExpr %><% } %>
|
|
98
|
+
})
|
|
99
|
+
<% } else if (useAxiosClient) { -%>
|
|
100
|
+
return new AxiosApiClient({
|
|
101
|
+
baseUrl: deps.apiUrl,
|
|
102
|
+
accessToken: token<% if (conventionExpr) { %>,
|
|
103
|
+
convention: <%= conventionExpr %><% } %>
|
|
104
|
+
})
|
|
105
|
+
<% } else if (useCustomApiClient) { -%>
|
|
106
|
+
return new CustomApiClient({
|
|
107
|
+
baseUrl: deps.apiUrl,
|
|
108
|
+
accessToken: token<% if (conventionExpr) { %>,
|
|
109
|
+
convention: <%= conventionExpr %><% } %>
|
|
110
|
+
})
|
|
111
|
+
<% } else { -%>
|
|
112
|
+
// TODO: replace with your real ApiClient. The Proxy makes a missing
|
|
113
|
+
// implementation fail loudly instead of silently no-oping.
|
|
114
|
+
return new Proxy({} as ApiClient, {
|
|
115
|
+
get() {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`No ApiClient configured (token=${token.slice(0, 4)}…). Wire createApiClient in src/tools/index.ts.`
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
<% } -%>
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return new ToolRegistry({
|
|
125
|
+
toolClasses: selectToolClasses(deps.profile),
|
|
126
|
+
models: deps.models,
|
|
127
|
+
serverContext: deps.serverContext,
|
|
128
|
+
promptRegistry: deps.promptRegistry,
|
|
129
|
+
domainRegistry: deps.domainRegistry ?? undefined,
|
|
130
|
+
vectorStorageEnabled: vectorStorage.isVectorStorageEnabled(),
|
|
131
|
+
createApiClient
|
|
132
|
+
})
|
|
133
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
2
|
+
import { dirname, join } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from 'vitest'
|
|
6
|
+
|
|
7
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
8
|
+
|
|
9
|
+
describe('scaffold smoke', () => {
|
|
10
|
+
it('package.json declares the design-mandated scripts', async () => {
|
|
11
|
+
const pkg = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'))
|
|
12
|
+
for (const name of ['start', 'test', 'typecheck', 'inspect']) {
|
|
13
|
+
expect(pkg.scripts[name], `missing script: ${name}`).toBeTypeOf('string')
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"isolatedModules": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*.ts", "config/**/*.ts", "test/**/*.ts"]
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
MCP server scaffolded with [`@mcp-rune/create`](https://github.com/mcp-rune/mcp-rune-cli).
|
|
4
|
+
|
|
5
|
+
## Run
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cp .env.example .env
|
|
9
|
+
npm install
|
|
10
|
+
npm run start:local
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Inspect with the MCP Inspector:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm run inspect
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Other scripts:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm run start # same entry as start:local for the simple preset
|
|
23
|
+
npm run test # vitest smoke tests
|
|
24
|
+
npm run typecheck # tsc --noEmit
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Layout
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
src/
|
|
31
|
+
├── server.js StdioServer entry point
|
|
32
|
+
├── config.js Tool / prompt registry wiring
|
|
33
|
+
├── models/ Domain model classes
|
|
34
|
+
├── prompts/ Prompt classes (auto-derived from models)
|
|
35
|
+
└── tools/ Tool registry (CRUD + strategy)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Add a new model by dropping a file under `src/models/` and registering it in `src/models/index.js`. The prompt scaffold is auto-derived.
|
|
39
|
+
|
|
40
|
+
See the [mcp-rune docs](https://github.com/mcp-rune/mcp-rune) for the full framework reference.
|