@mcp-rune/create 0.11.0 → 0.11.1

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/README.md CHANGED
@@ -147,6 +147,12 @@ These change the run itself — they don't answer a question, so they only appea
147
147
  | `--offline-template <path>` | Use a local template directory instead of fetching via `tiged`. |
148
148
  | `--mcp-rune-local <path>` | Point the scaffolded project at a local `mcp-rune` checkout (also reads `MCP_RUNE_LOCAL_PATH`). |
149
149
 
150
+ ## Logging in generated projects
151
+
152
+ Generated projects log via the framework logger (or Pino with `--logger pino`). Log behavior is controlled by env vars — `LOG_LEVEL` (`error|warn|info|debug`), `LOG_FORMAT` (`text|json`), `LOG_FILE_FORMAT`, `LOG_FILE_ENABLED`, and `NODE_ENV` (production forces JSON). `LOG_LEVEL=debug rune-app` works out of the box, since the logger honors these at startup.
153
+
154
+ The **advanced** preset goes further: its `config/schema.js` spreads mcp-rune's `frameworkConfigSchema`, so those vars are validated by `loadConfig` and applied to the logger via `configureLogging()` in `src/config.ts` — one source of truth, no stray `process.env` reads. The **simple** preset relies on the logger's bootstrap defaults. See each preset's `.env.example`.
155
+
150
156
  ## Templates
151
157
 
152
158
  As an alternative to presets, scaffold from a runnable example in the [`mcp-rune/examples`](https://github.com/mcp-rune/examples) repo:
@@ -118,7 +118,7 @@ function makeVars(cwd, preset, models) {
118
118
  useLangfuse: false,
119
119
  toolClasses: [],
120
120
  promptStrategies: {},
121
- mcpRuneVersion: pkg.dependencies?.['@mcp-rune/mcp-rune'] ?? '^0.102.0',
121
+ mcpRuneVersion: pkg.dependencies?.['@mcp-rune/mcp-rune'] ?? '^0.105.1',
122
122
  nodeEngine: '>=24.0.0',
123
123
  };
124
124
  }
@@ -128,7 +128,7 @@ function makeVars(answers) {
128
128
  toolClasses: answers.toolClasses ?? [],
129
129
  promptStrategies: answers.promptStrategies ?? {},
130
130
  models: normalizeModels(answers.models),
131
- mcpRuneVersion: answers.mcpRuneVersion ?? '^0.102.0',
131
+ mcpRuneVersion: answers.mcpRuneVersion ?? '^0.105.1',
132
132
  nodeEngine: answers.nodeEngine ?? '>=24.0.0',
133
133
  };
134
134
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-rune/create",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "Scaffolder for mcp-rune-based MCP servers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -80,3 +80,14 @@ LANGFUSE_SECRET_KEY=
80
80
  # LANGFUSE_PUBLIC_KEY=
81
81
  # LANGFUSE_SECRET_KEY=
82
82
  <% } -%>
83
+
84
+ # =============================================================================
85
+ # Logging (optional)
86
+ # =============================================================================
87
+ # Declared via frameworkConfigSchema (spread into config/schema.js) and applied
88
+ # by configureLogging() in src/config.ts, so these are validated config — not
89
+ # loose env reads. LOG_LEVEL=debug also works ad-hoc when debugging.
90
+ # LOG_LEVEL=info # error | warn | info | debug
91
+ # LOG_FORMAT=json # text (default) | json; NODE_ENV=production forces json
92
+ # LOG_FILE_FORMAT=json # file format; inherits LOG_FORMAT if unset
93
+ # LOG_FILE_ENABLED=false # write rotating daily files under logs/
@@ -17,7 +17,7 @@ const { config, createOAuthService, mcpConfig } = await import('../config.js')
17
17
 
18
18
  const port = config.transport.remote.port
19
19
  const baseUrl = config.transport.remote.baseUrl ?? `http://localhost:${port}`
20
- const isProduction = config.http.environment === 'production'
20
+ const isProduction = config.runtime.environment === 'production'
21
21
 
22
22
  const server = new HttpServer({
23
23
  port,
@@ -19,6 +19,10 @@ interface ChildLogger {
19
19
  child: (meta: Record<string, unknown>) => ChildLogger
20
20
  }
21
21
 
22
+ // Bootstrap level honors LOG_LEVEL (the usual pino/12-factor convention) so
23
+ // logging works before config is loaded. `configureLogging()` below lets the
24
+ // validated config supersede it — keeping a single source of truth once
25
+ // loadConfig has run.
22
26
  const base = pino({
23
27
  level: process.env.LOG_LEVEL ?? 'info',
24
28
  formatters: {
@@ -56,6 +60,14 @@ export const logger = {
56
60
  setApp(name: string): void {
57
61
  appName = name
58
62
  },
63
+ /**
64
+ * Apply validated logging config (from loadConfig), superseding the
65
+ * LOG_LEVEL bootstrap. Mirrors the framework logger's configureLogging so
66
+ * config.ts can wire both with one shape.
67
+ */
68
+ configureLogging(opts: { level?: string } = {}): void {
69
+ if (opts.level) base.level = opts.level
70
+ },
59
71
  child(meta: Record<string, unknown> = {}): ChildLogger {
60
72
  return buildChild(meta)
61
73
  }
@@ -5,9 +5,14 @@
5
5
  * at startup. Every env var read in the codebase should be declared here.
6
6
  */
7
7
 
8
- import type { ConfigSchema } from '@mcp-rune/mcp-rune/core'
8
+ import { frameworkConfigSchema, type ConfigSchema } from '@mcp-rune/mcp-rune/core'
9
9
 
10
10
  export const configSchema = {
11
+ // Framework-owned logging + runtime vars (logging.* → LOG_*,
12
+ // runtime.environment → NODE_ENV). Applied to the logger in config.ts via
13
+ // configureLogging() so the validated values supersede the env bootstrap.
14
+ ...frameworkConfigSchema,
15
+
11
16
  api: {
12
17
  url: {
13
18
  env: 'API_URL',
@@ -104,7 +109,7 @@ export const configSchema = {
104
109
 
105
110
  http: {
106
111
  corsOrigins: { env: 'CORS_ORIGINS', doc: 'Comma-separated list of allowed CORS origins' },
107
- environment: { env: 'NODE_ENV', default: 'development', doc: 'Runtime environment' },
112
+ // NODE_ENV is provided by frameworkConfigSchema as runtime.environment.
108
113
  },
109
114
 
110
115
  errorTracking: {
@@ -12,11 +12,14 @@ import { createServer as createServerFactory } from '@mcp-rune/mcp-rune/server'
12
12
  import { _setAdapter, OAuthService, PostgresqlAdapter } from '@mcp-rune/mcp-rune/oauth2'
13
13
  <% } -%>
14
14
  <% if (usePinoLogger) { -%>
15
- import { errorTracking, tracing, vectorStorage } from '@mcp-rune/mcp-rune/runtime'
15
+ // Your app logs via Pino (below), but mcp-rune's internals log via the
16
+ // framework logger — configure it from the same validated config.
17
+ import { errorTracking, logger as frameworkLogger, tracing, vectorStorage } from '@mcp-rune/mcp-rune/runtime'
16
18
  <% } else { -%>
17
19
  import { errorTracking, logger, tracing, vectorStorage } from '@mcp-rune/mcp-rune/runtime'
18
20
  <% } -%>
19
21
  import { createPgvectorAdapter } from '@mcp-rune/mcp-rune/runtime/vendor/pgvector'
22
+ import { assertMigrationsCurrent, type Feature } from '@mcp-rune/mcp-rune/db/migrations'
20
23
 
21
24
  import { configSchema } from '../config/schema.js'
22
25
  import { closeDatabase, initDatabase } from './db.js'
@@ -67,7 +70,15 @@ interface AppConfig {
67
70
  }
68
71
  <% } -%>
69
72
  }
70
- http: { corsOrigins: string | undefined; environment: string }
73
+ http: { corsOrigins: string | undefined }
74
+ // From frameworkConfigSchema (spread into configSchema):
75
+ logging: {
76
+ level: string
77
+ format?: 'text' | 'json'
78
+ fileFormat?: 'text' | 'json'
79
+ fileEnabled: boolean
80
+ }
81
+ runtime: { environment: string }
71
82
  errorTracking: { sentryDsn: string | undefined }
72
83
  tracing: { langfusePublicKey: string | undefined; langfuseSecretKey: string | undefined }
73
84
  }
@@ -88,10 +99,28 @@ const { name: SERVER_NAME, version: SERVER_VERSION } = startup.phase(
88
99
  export const config = startup.phase('config', 'Load configuration', (log) => {
89
100
  const cfg = loadConfig(configSchema)
90
101
  log.debug(cfg.toString())
91
- return cfg as unknown as AppConfig & { toString(): string }
102
+ const appCfg = cfg as unknown as AppConfig & { toString(): string }
103
+ // Apply the validated logging config, superseding the framework logger's
104
+ // env-derived bootstrap. No process.env reads — the loaded config is the
105
+ // single source of truth.
106
+ <% if (usePinoLogger) { -%>
107
+ // Framework internals log via the framework logger; your app logs via Pino.
108
+ // Configure both from the same validated config.
109
+ frameworkLogger.configureLogging({
110
+ ...appCfg.logging,
111
+ production: appCfg.runtime.environment === 'production',
112
+ })
113
+ logger.configureLogging({ level: appCfg.logging.level })
114
+ <% } else { -%>
115
+ logger.configureLogging({
116
+ ...appCfg.logging,
117
+ production: appCfg.runtime.environment === 'production',
118
+ })
119
+ <% } -%>
120
+ return appCfg
92
121
  })
93
122
 
94
- const isProduction = config.http.environment === 'production'
123
+ const isProduction = config.runtime.environment === 'production'
95
124
 
96
125
  startup.phase('error-tracking', 'Error tracking', () => {
97
126
  // Vendor (Sentry) reads SENTRY_DSN + NODE_ENV from env directly; the
@@ -111,13 +140,22 @@ startup.phase('tracing', 'Tracing', () => {
111
140
  })
112
141
  })
113
142
 
114
- startup.phase('database', 'Database', (log) => {
143
+ await startup.phaseAsync('database', 'Database', async (log) => {
115
144
  const pool = initDatabase(config.database.url)
116
145
  if (!pool) {
117
146
  log.debug('DATABASE_URL not set — database features disabled')
118
147
  return
119
148
  }
120
149
 
150
+ // Fail fast if the database is behind on migrations, instead of letting a
151
+ // missing column surface as a cryptic SQL error mid-tool-call. The feature
152
+ // set mirrors what this server provisions: always `core`, plus `analysis`
153
+ // when vector storage is enabled. Module-independent — covers every pending
154
+ // migration, not just analysis ones.
155
+ const features: Feature[] = ['core']
156
+ if (config.analysis.enabled) features.push('analysis')
157
+ await assertMigrationsCurrent(pool, { features })
158
+
121
159
  <% if (hasHttp && !useStaticTokenAuth) { -%>
122
160
  _setAdapter(new PostgresqlAdapter({ pool }))
123
161
  log.debug('OAuth token store connected')
@@ -1,3 +1,8 @@
1
1
  # Token used by the framework's CRUD tools when calling your API backend.
2
2
  # For local dev without a real backend, any non-empty value works.
3
3
  ACCESS_TOKEN=demo-token
4
+
5
+ # Logging — the framework logger honors these at bootstrap (the simple preset
6
+ # doesn't use loadConfig). LOG_LEVEL=debug works ad-hoc when debugging.
7
+ # LOG_LEVEL=info # error | warn | info | debug
8
+ # LOG_FORMAT=json # text (default) | json