@cyanheads/mcp-ts-core 0.13.7 → 0.13.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/AGENTS.md +19 -15
  2. package/CLAUDE.md +19 -15
  3. package/README.md +4 -2
  4. package/changelog/0.13.x/0.13.8.md +101 -0
  5. package/changelog/0.13.x/0.13.9.md +113 -0
  6. package/dist/config/index.d.ts +9 -0
  7. package/dist/config/index.d.ts.map +1 -1
  8. package/dist/config/index.js +39 -9
  9. package/dist/config/index.js.map +1 -1
  10. package/dist/core/app.d.ts +6 -3
  11. package/dist/core/app.d.ts.map +1 -1
  12. package/dist/core/app.js +20 -6
  13. package/dist/core/app.js.map +1 -1
  14. package/dist/core/context.d.ts +25 -1
  15. package/dist/core/context.d.ts.map +1 -1
  16. package/dist/core/context.js +13 -3
  17. package/dist/core/context.js.map +1 -1
  18. package/dist/core/serverManifest.d.ts +6 -0
  19. package/dist/core/serverManifest.d.ts.map +1 -1
  20. package/dist/core/serverManifest.js +6 -0
  21. package/dist/core/serverManifest.js.map +1 -1
  22. package/dist/core/worker.d.ts +2 -0
  23. package/dist/core/worker.d.ts.map +1 -1
  24. package/dist/core/worker.js +2 -0
  25. package/dist/core/worker.js.map +1 -1
  26. package/dist/linter/rules/enrichment-rules.d.ts +3 -2
  27. package/dist/linter/rules/enrichment-rules.d.ts.map +1 -1
  28. package/dist/linter/rules/enrichment-rules.js +9 -2
  29. package/dist/linter/rules/enrichment-rules.js.map +1 -1
  30. package/dist/linter/rules/handler-body-rules.d.ts.map +1 -1
  31. package/dist/linter/rules/handler-body-rules.js +10 -4
  32. package/dist/linter/rules/handler-body-rules.js.map +1 -1
  33. package/dist/linter/rules/schema-rules.d.ts +5 -0
  34. package/dist/linter/rules/schema-rules.d.ts.map +1 -1
  35. package/dist/linter/rules/schema-rules.js +44 -17
  36. package/dist/linter/rules/schema-rules.js.map +1 -1
  37. package/dist/linter/rules/tool-rules.d.ts +2 -1
  38. package/dist/linter/rules/tool-rules.d.ts.map +1 -1
  39. package/dist/linter/rules/tool-rules.js +36 -1
  40. package/dist/linter/rules/tool-rules.js.map +1 -1
  41. package/dist/mcp-server/inputRequired.d.ts +14 -5
  42. package/dist/mcp-server/inputRequired.d.ts.map +1 -1
  43. package/dist/mcp-server/inputRequired.js +15 -8
  44. package/dist/mcp-server/inputRequired.js.map +1 -1
  45. package/dist/mcp-server/outputContract.d.ts +33 -0
  46. package/dist/mcp-server/outputContract.d.ts.map +1 -0
  47. package/dist/mcp-server/outputContract.js +43 -0
  48. package/dist/mcp-server/outputContract.js.map +1 -0
  49. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
  50. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +10 -2
  51. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
  52. package/dist/mcp-server/tools/utils/inputPrevalidation.d.ts +163 -40
  53. package/dist/mcp-server/tools/utils/inputPrevalidation.d.ts.map +1 -1
  54. package/dist/mcp-server/tools/utils/inputPrevalidation.js +330 -114
  55. package/dist/mcp-server/tools/utils/inputPrevalidation.js.map +1 -1
  56. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +39 -15
  57. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
  58. package/dist/mcp-server/tools/utils/toolHandlerFactory.js +361 -93
  59. package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
  60. package/dist/mcp-server/transports/auth/lib/authUtils.js +4 -1
  61. package/dist/mcp-server/transports/auth/lib/authUtils.js.map +1 -1
  62. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.d.ts.map +1 -1
  63. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js +1 -1
  64. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js.map +1 -1
  65. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.d.ts.map +1 -1
  66. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js +2 -5
  67. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js.map +1 -1
  68. package/dist/mcp-server/transports/http/httpTransport.d.ts.map +1 -1
  69. package/dist/mcp-server/transports/http/httpTransport.js +65 -9
  70. package/dist/mcp-server/transports/http/httpTransport.js.map +1 -1
  71. package/dist/mcp-server/transports/http/sessionStore.d.ts.map +1 -1
  72. package/dist/mcp-server/transports/http/sessionStore.js +2 -2
  73. package/dist/mcp-server/transports/http/sessionStore.js.map +1 -1
  74. package/dist/mcp-server/transports/stdio/stdioTransport.d.ts +9 -5
  75. package/dist/mcp-server/transports/stdio/stdioTransport.d.ts.map +1 -1
  76. package/dist/mcp-server/transports/stdio/stdioTransport.js +9 -5
  77. package/dist/mcp-server/transports/stdio/stdioTransport.js.map +1 -1
  78. package/dist/services/canvas/core/CanvasRegistry.d.ts +6 -2
  79. package/dist/services/canvas/core/CanvasRegistry.d.ts.map +1 -1
  80. package/dist/services/canvas/core/CanvasRegistry.js +8 -4
  81. package/dist/services/canvas/core/CanvasRegistry.js.map +1 -1
  82. package/dist/services/canvas/core/DataCanvas.d.ts.map +1 -1
  83. package/dist/services/canvas/core/DataCanvas.js +7 -5
  84. package/dist/services/canvas/core/DataCanvas.js.map +1 -1
  85. package/dist/services/canvas/core/canvasFactory.d.ts.map +1 -1
  86. package/dist/services/canvas/core/canvasFactory.js +2 -2
  87. package/dist/services/canvas/core/canvasFactory.js.map +1 -1
  88. package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts +82 -18
  89. package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
  90. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +645 -344
  91. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
  92. package/dist/services/canvas/providers/duckdb/exportWriter.d.ts +11 -7
  93. package/dist/services/canvas/providers/duckdb/exportWriter.d.ts.map +1 -1
  94. package/dist/services/canvas/providers/duckdb/exportWriter.js +19 -16
  95. package/dist/services/canvas/providers/duckdb/exportWriter.js.map +1 -1
  96. package/dist/services/llm/providers/openrouter.provider.js +1 -1
  97. package/dist/services/llm/providers/openrouter.provider.js.map +1 -1
  98. package/dist/services/mirror/core/defineMirror.d.ts +1 -0
  99. package/dist/services/mirror/core/defineMirror.d.ts.map +1 -1
  100. package/dist/services/mirror/core/defineMirror.js +1 -0
  101. package/dist/services/mirror/core/defineMirror.js.map +1 -1
  102. package/dist/services/speech/providers/elevenlabs.provider.js +3 -3
  103. package/dist/services/speech/providers/elevenlabs.provider.js.map +1 -1
  104. package/dist/services/speech/providers/whisper.provider.d.ts.map +1 -1
  105. package/dist/services/speech/providers/whisper.provider.js +5 -5
  106. package/dist/services/speech/providers/whisper.provider.js.map +1 -1
  107. package/dist/storage/core/StorageService.d.ts.map +1 -1
  108. package/dist/storage/core/StorageService.js +3 -6
  109. package/dist/storage/core/StorageService.js.map +1 -1
  110. package/dist/storage/core/storageFactory.d.ts.map +1 -1
  111. package/dist/storage/core/storageFactory.js +12 -15
  112. package/dist/storage/core/storageFactory.js.map +1 -1
  113. package/dist/storage/core/storageValidation.d.ts +13 -13
  114. package/dist/storage/core/storageValidation.d.ts.map +1 -1
  115. package/dist/storage/core/storageValidation.js +49 -125
  116. package/dist/storage/core/storageValidation.js.map +1 -1
  117. package/dist/storage/providers/cloudflare/d1Provider.d.ts.map +1 -1
  118. package/dist/storage/providers/cloudflare/d1Provider.js +5 -3
  119. package/dist/storage/providers/cloudflare/d1Provider.js.map +1 -1
  120. package/dist/storage/providers/cloudflare/kvProvider.d.ts.map +1 -1
  121. package/dist/storage/providers/cloudflare/kvProvider.js +1 -1
  122. package/dist/storage/providers/cloudflare/kvProvider.js.map +1 -1
  123. package/dist/storage/providers/cloudflare/r2Provider.d.ts.map +1 -1
  124. package/dist/storage/providers/cloudflare/r2Provider.js +3 -3
  125. package/dist/storage/providers/cloudflare/r2Provider.js.map +1 -1
  126. package/dist/storage/providers/fileSystem/fileSystemProvider.js +4 -4
  127. package/dist/storage/providers/fileSystem/fileSystemProvider.js.map +1 -1
  128. package/dist/storage/providers/inMemory/inMemoryProvider.d.ts +1 -1
  129. package/dist/storage/providers/inMemory/inMemoryProvider.d.ts.map +1 -1
  130. package/dist/storage/providers/inMemory/inMemoryProvider.js +6 -5
  131. package/dist/storage/providers/inMemory/inMemoryProvider.js.map +1 -1
  132. package/dist/testing/fuzz.d.ts.map +1 -1
  133. package/dist/testing/fuzz.js +7 -1
  134. package/dist/testing/fuzz.js.map +1 -1
  135. package/dist/testing/index.d.ts +15 -2
  136. package/dist/testing/index.d.ts.map +1 -1
  137. package/dist/testing/index.js +51 -6
  138. package/dist/testing/index.js.map +1 -1
  139. package/dist/types-global/errors.d.ts +7 -4
  140. package/dist/types-global/errors.d.ts.map +1 -1
  141. package/dist/types-global/errors.js.map +1 -1
  142. package/dist/utils/formatting/codeSpan.d.ts +27 -0
  143. package/dist/utils/formatting/codeSpan.d.ts.map +1 -0
  144. package/dist/utils/formatting/codeSpan.js +42 -0
  145. package/dist/utils/formatting/codeSpan.js.map +1 -0
  146. package/dist/utils/formatting/diffFormatter.d.ts.map +1 -1
  147. package/dist/utils/formatting/diffFormatter.js +7 -15
  148. package/dist/utils/formatting/diffFormatter.js.map +1 -1
  149. package/dist/utils/formatting/markdownBuilder.d.ts +12 -5
  150. package/dist/utils/formatting/markdownBuilder.d.ts.map +1 -1
  151. package/dist/utils/formatting/markdownBuilder.js +14 -2
  152. package/dist/utils/formatting/markdownBuilder.js.map +1 -1
  153. package/dist/utils/formatting/tableFormatter.d.ts.map +1 -1
  154. package/dist/utils/formatting/tableFormatter.js +5 -9
  155. package/dist/utils/formatting/tableFormatter.js.map +1 -1
  156. package/dist/utils/formatting/treeFormatter.d.ts.map +1 -1
  157. package/dist/utils/formatting/treeFormatter.js +5 -9
  158. package/dist/utils/formatting/treeFormatter.js.map +1 -1
  159. package/dist/utils/index.d.ts +1 -1
  160. package/dist/utils/index.d.ts.map +1 -1
  161. package/dist/utils/index.js.map +1 -1
  162. package/dist/utils/internal/error-handler/errorHandler.d.ts +17 -10
  163. package/dist/utils/internal/error-handler/errorHandler.d.ts.map +1 -1
  164. package/dist/utils/internal/error-handler/errorHandler.js +47 -26
  165. package/dist/utils/internal/error-handler/errorHandler.js.map +1 -1
  166. package/dist/utils/internal/error-handler/mappings.d.ts +17 -1
  167. package/dist/utils/internal/error-handler/mappings.d.ts.map +1 -1
  168. package/dist/utils/internal/error-handler/mappings.js +22 -1
  169. package/dist/utils/internal/error-handler/mappings.js.map +1 -1
  170. package/dist/utils/internal/error-handler/types.d.ts +2 -0
  171. package/dist/utils/internal/error-handler/types.d.ts.map +1 -1
  172. package/dist/utils/internal/logger.d.ts +75 -3
  173. package/dist/utils/internal/logger.d.ts.map +1 -1
  174. package/dist/utils/internal/logger.js +181 -52
  175. package/dist/utils/internal/logger.js.map +1 -1
  176. package/dist/utils/internal/performance.d.ts +11 -0
  177. package/dist/utils/internal/performance.d.ts.map +1 -1
  178. package/dist/utils/internal/performance.js +46 -12
  179. package/dist/utils/internal/performance.js.map +1 -1
  180. package/dist/utils/network/fetchWithTimeout.d.ts +11 -5
  181. package/dist/utils/network/fetchWithTimeout.d.ts.map +1 -1
  182. package/dist/utils/network/fetchWithTimeout.js +50 -23
  183. package/dist/utils/network/fetchWithTimeout.js.map +1 -1
  184. package/dist/utils/network/pacer.d.ts +38 -5
  185. package/dist/utils/network/pacer.d.ts.map +1 -1
  186. package/dist/utils/network/pacer.js +87 -25
  187. package/dist/utils/network/pacer.js.map +1 -1
  188. package/dist/utils/network/retry.d.ts +16 -8
  189. package/dist/utils/network/retry.d.ts.map +1 -1
  190. package/dist/utils/network/retry.js +19 -8
  191. package/dist/utils/network/retry.js.map +1 -1
  192. package/dist/utils/overflow/outlineOnOverflow.d.ts +18 -2
  193. package/dist/utils/overflow/outlineOnOverflow.d.ts.map +1 -1
  194. package/dist/utils/overflow/outlineOnOverflow.js +28 -3
  195. package/dist/utils/overflow/outlineOnOverflow.js.map +1 -1
  196. package/dist/utils/pagination/pagination.d.ts +3 -1
  197. package/dist/utils/pagination/pagination.d.ts.map +1 -1
  198. package/dist/utils/pagination/pagination.js +10 -2
  199. package/dist/utils/pagination/pagination.js.map +1 -1
  200. package/dist/utils/parsing/csvParser.d.ts.map +1 -1
  201. package/dist/utils/parsing/csvParser.js +4 -2
  202. package/dist/utils/parsing/csvParser.js.map +1 -1
  203. package/dist/utils/parsing/htmlExtractor.js +1 -1
  204. package/dist/utils/parsing/htmlExtractor.js.map +1 -1
  205. package/dist/utils/parsing/jsonParser.d.ts.map +1 -1
  206. package/dist/utils/parsing/jsonParser.js +3 -1
  207. package/dist/utils/parsing/jsonParser.js.map +1 -1
  208. package/dist/utils/parsing/xmlParser.d.ts.map +1 -1
  209. package/dist/utils/parsing/xmlParser.js +3 -1
  210. package/dist/utils/parsing/xmlParser.js.map +1 -1
  211. package/dist/utils/parsing/yamlParser.d.ts.map +1 -1
  212. package/dist/utils/parsing/yamlParser.js +3 -1
  213. package/dist/utils/parsing/yamlParser.js.map +1 -1
  214. package/dist/utils/security/idGenerator.d.ts.map +1 -1
  215. package/dist/utils/security/idGenerator.js +20 -4
  216. package/dist/utils/security/idGenerator.js.map +1 -1
  217. package/dist/utils/security/sanitization.d.ts +31 -0
  218. package/dist/utils/security/sanitization.d.ts.map +1 -1
  219. package/dist/utils/security/sanitization.js +98 -11
  220. package/dist/utils/security/sanitization.js.map +1 -1
  221. package/dist/utils/telemetry/attributes.d.ts +21 -2
  222. package/dist/utils/telemetry/attributes.d.ts.map +1 -1
  223. package/dist/utils/telemetry/attributes.js +21 -2
  224. package/dist/utils/telemetry/attributes.js.map +1 -1
  225. package/dist/utils/telemetry/instrumentation.d.ts +9 -3
  226. package/dist/utils/telemetry/instrumentation.d.ts.map +1 -1
  227. package/dist/utils/telemetry/instrumentation.js +85 -13
  228. package/dist/utils/telemetry/instrumentation.js.map +1 -1
  229. package/framework-skills/add-app-tool/SKILL.md +3 -3
  230. package/framework-skills/add-export/SKILL.md +5 -16
  231. package/framework-skills/add-prompt/SKILL.md +7 -3
  232. package/framework-skills/add-resource/SKILL.md +7 -5
  233. package/framework-skills/add-tool/SKILL.md +12 -10
  234. package/framework-skills/api-auth/SKILL.md +4 -2
  235. package/framework-skills/api-canvas/SKILL.md +19 -10
  236. package/framework-skills/api-config/SKILL.md +9 -6
  237. package/framework-skills/api-context/SKILL.md +16 -5
  238. package/framework-skills/api-errors/SKILL.md +23 -17
  239. package/framework-skills/api-linter/SKILL.md +32 -9
  240. package/framework-skills/api-mirror/SKILL.md +2 -1
  241. package/framework-skills/api-telemetry/SKILL.md +34 -14
  242. package/framework-skills/api-testing/SKILL.md +5 -3
  243. package/framework-skills/api-utils/SKILL.md +10 -10
  244. package/framework-skills/api-utils/references/formatting.md +1 -1
  245. package/framework-skills/api-utils/references/parsing.md +2 -2
  246. package/framework-skills/api-utils/references/security.md +6 -4
  247. package/framework-skills/design-mcp-server/SKILL.md +2 -2
  248. package/framework-skills/field-test/SKILL.md +4 -4
  249. package/framework-skills/git-wrapup/SKILL.md +12 -7
  250. package/framework-skills/maintenance/SKILL.md +2 -2
  251. package/framework-skills/orchestrations/SKILL.md +7 -6
  252. package/framework-skills/orchestrations/workflows/field-test-fix.md +9 -19
  253. package/framework-skills/orchestrations/workflows/fix-wrapup-release.md +7 -7
  254. package/framework-skills/orchestrations/workflows/greenfield-build.md +8 -5
  255. package/framework-skills/orchestrations/workflows/maintenance-release.md +8 -8
  256. package/framework-skills/polish-docs-meta/SKILL.md +4 -4
  257. package/framework-skills/polish-docs-meta/references/readme.md +1 -0
  258. package/framework-skills/release-and-publish/SKILL.md +7 -5
  259. package/framework-skills/release-pr-review/SKILL.md +37 -23
  260. package/framework-skills/report-issue-framework/SKILL.md +7 -5
  261. package/framework-skills/report-issue-local/SKILL.md +8 -6
  262. package/framework-skills/security-pass/SKILL.md +8 -8
  263. package/framework-skills/techniques/SKILL.md +1 -1
  264. package/framework-skills/techniques/references/outline-on-overflow.md +12 -7
  265. package/package.json +20 -5
  266. package/scripts/check-skill-versions.ts +103 -22
  267. package/scripts/devcheck.ts +11 -9
  268. package/scripts/lint-mcp.ts +87 -27
  269. package/scripts/lint-packaging.ts +99 -1
  270. package/scripts/release-github.ts +117 -5
  271. package/templates/.env.example +4 -0
  272. package/templates/Dockerfile +26 -6
  273. package/templates/_.mcpbignore +2 -0
  274. package/templates/package.json +1 -0
@@ -12,6 +12,11 @@
12
12
  * 3. Extracts exported definitions by duck-typing (has name/handler/input etc.)
13
13
  * 4. Feeds them into `validateDefinitions()`
14
14
  *
15
+ * A definition file whose `import()` rejects, and a `server.json` that exists
16
+ * but does not parse, are errors (`definition-import-failed`,
17
+ * `server-json-parse`): what they declare cannot be checked, so the run fails
18
+ * instead of passing without them. The remaining files are still linted.
19
+ *
15
20
  * Runtime-agnostic: works with bun, tsx, and Node.js (via ts-node/esm).
16
21
  *
17
22
  * Rule knobs come from the project's `devcheck.config.json` `lint` block, so one
@@ -20,7 +25,7 @@
20
25
  * @module scripts/lint-mcp
21
26
  */
22
27
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
23
- import { join, resolve } from 'node:path';
28
+ import { join, relative, resolve } from 'node:path';
24
29
  import { fileURLToPath } from 'node:url';
25
30
 
26
31
  // ---------------------------------------------------------------------------
@@ -100,19 +105,64 @@ function discoverFiles(): string[] {
100
105
  return [...new Set(files)];
101
106
  }
102
107
 
108
+ // ---------------------------------------------------------------------------
109
+ // Load failures
110
+ // ---------------------------------------------------------------------------
111
+
112
+ /** Where the rule reference lives — the breadcrumb `validateDefinitions()` appends too. */
113
+ const SKILL_REFERENCE_PATH = 'framework-skills/api-linter/SKILL.md';
114
+
115
+ /** A file the CLI could not load, shaped like the diagnostics it is printed beside. */
116
+ interface LoadFailure {
117
+ message: string;
118
+ rule: 'definition-import-failed' | 'server-json-parse';
119
+ }
120
+
121
+ /**
122
+ * The text of a thrown value. An `AggregateError` keeps its causes in `errors`
123
+ * — Bun rejects a definition that fails to transpile with one whose own message
124
+ * only counts them ("2 errors building …") — so each cause is appended.
125
+ */
126
+ function describeError(err: unknown): string {
127
+ if (!(err instanceof Error)) return String(err);
128
+ const message = err.message || err.name;
129
+ if (!(err instanceof AggregateError) || err.errors.length === 0) return message;
130
+ return `${message} (${err.errors.map(describeError).join('; ')})`;
131
+ }
132
+
133
+ function loadFailure(rule: LoadFailure['rule'], file: string, err: unknown): LoadFailure {
134
+ const anchor = rule === 'server-json-parse' ? 'server-json-rules' : rule;
135
+ return {
136
+ rule,
137
+ message: `${file}: ${describeError(err)}\nSee: ${SKILL_REFERENCE_PATH}#${anchor}`,
138
+ };
139
+ }
140
+
103
141
  // ---------------------------------------------------------------------------
104
142
  // Main
105
143
  // ---------------------------------------------------------------------------
106
144
 
107
- /** Try to read and parse a JSON file. Returns undefined on failure. */
108
- function tryReadJson(path: string): unknown {
145
+ /** A parsed JSON file, or the error an existing one failed to read or parse with. */
146
+ type JsonRead = { ok: true; value: unknown } | { ok: false; error: unknown };
147
+
148
+ /** Reads and parses a JSON file. `undefined` when the file does not exist. */
149
+ function readJson(path: string): JsonRead | undefined {
150
+ if (!existsSync(path)) return;
109
151
  try {
110
- if (!existsSync(path)) return;
111
- return JSON.parse(readFileSync(path, 'utf-8'));
112
- } catch (err) {
113
- console.warn(`Warning: Failed to parse ${path}: ${err instanceof Error ? err.message : err}`);
152
+ return { ok: true, value: JSON.parse(readFileSync(path, 'utf-8')) };
153
+ } catch (error) {
154
+ return { ok: false, error };
155
+ }
156
+ }
157
+
158
+ /** Try to read and parse a JSON file. Returns undefined when absent, and warns when unparseable. */
159
+ function tryReadJson(path: string): unknown {
160
+ const read = readJson(path);
161
+ if (read?.ok === false) {
162
+ console.warn(`Warning: Failed to parse ${path}: ${describeError(read.error)}`);
114
163
  return;
115
164
  }
165
+ return read?.value;
116
166
  }
117
167
 
118
168
  /** The `lint` block of `devcheck.config.json`, as far as this script reads it. */
@@ -149,12 +199,17 @@ export function readLintOptions(configPath = resolve('devcheck.config.json')): L
149
199
 
150
200
  async function main(): Promise<void> {
151
201
  const files = discoverFiles();
202
+ const failures: LoadFailure[] = [];
152
203
 
153
204
  // Discover server.json and package.json at project root
154
- const serverJson = tryReadJson(resolve('server.json'));
205
+ const serverJsonRead = readJson(resolve('server.json'));
206
+ if (serverJsonRead?.ok === false) {
207
+ failures.push(loadFailure('server-json-parse', 'server.json', serverJsonRead.error));
208
+ }
209
+ const serverJson = serverJsonRead?.ok ? serverJsonRead.value : undefined;
155
210
  const packageJson = tryReadJson(resolve('package.json')) as { version?: string } | undefined;
156
211
 
157
- if (files.length === 0 && serverJson == null) {
212
+ if (files.length === 0 && serverJson == null && failures.length === 0) {
158
213
  console.log('No MCP definition files or server.json found. Skipping lint.');
159
214
  process.exit(0);
160
215
  }
@@ -162,36 +217,42 @@ async function main(): Promise<void> {
162
217
  const tools: unknown[] = [];
163
218
  const resources: unknown[] = [];
164
219
  const prompts: unknown[] = [];
220
+ let failedImports = 0;
165
221
 
166
222
  for (const file of files) {
223
+ let mod: Record<string, unknown>;
167
224
  try {
168
- const mod = await import(file);
169
- for (const exported of Object.values(mod)) {
170
- if (isToolLike(exported)) tools.push(exported);
171
- else if (isResourceLike(exported)) resources.push(exported);
172
- else if (isPromptLike(exported)) prompts.push(exported);
173
- }
225
+ mod = await import(file);
174
226
  } catch (err) {
175
- console.warn(
176
- `Warning: Failed to import ${file}: ${err instanceof Error ? err.message : err}`,
177
- );
227
+ failures.push(loadFailure('definition-import-failed', relative(process.cwd(), file), err));
228
+ failedImports++;
229
+ continue;
230
+ }
231
+ for (const exported of Object.values(mod)) {
232
+ if (isToolLike(exported)) tools.push(exported);
233
+ else if (isResourceLike(exported)) resources.push(exported);
234
+ else if (isPromptLike(exported)) prompts.push(exported);
178
235
  }
179
236
  }
180
237
 
181
238
  const defTotal = tools.length + resources.length + prompts.length;
182
- if (defTotal === 0 && serverJson == null) {
239
+ if (defTotal === 0 && serverJson == null && failures.length === 0) {
183
240
  console.log(`Scanned ${files.length} files but found no definitions. Skipping lint.`);
184
241
  process.exit(0);
185
242
  }
186
243
 
187
244
  const parts: string[] = [];
188
- if (defTotal > 0) {
245
+ if (defTotal > 0 || failedImports > 0) {
246
+ const fileCount =
247
+ failedImports === 0
248
+ ? `${files.length}`
249
+ : `${files.length - failedImports} of ${files.length}`;
189
250
  parts.push(
190
- `${tools.length} tool(s), ${resources.length} resource(s), ${prompts.length} prompt(s) from ${files.length} file(s)`,
251
+ `${tools.length} tool(s), ${resources.length} resource(s), ${prompts.length} prompt(s) from ${fileCount} file(s)`,
191
252
  );
192
253
  }
193
254
  if (serverJson != null) parts.push('server.json');
194
- console.log(`Linting ${parts.join(' + ')}...`);
255
+ if (parts.length > 0) console.log(`Linting ${parts.join(' + ')}...`);
195
256
 
196
257
  const report = validateDefinitions({
197
258
  tools,
@@ -202,14 +263,15 @@ async function main(): Promise<void> {
202
263
  ...readLintOptions(),
203
264
  });
204
265
 
266
+ const errors = [...failures, ...report.errors];
205
267
  for (const w of report.warnings) {
206
268
  console.warn(` ⚠ [${w.rule}] ${w.message}`);
207
269
  }
208
- for (const e of report.errors) {
270
+ for (const e of errors) {
209
271
  console.error(` ✗ [${e.rule}] ${e.message}`);
210
272
  }
211
273
 
212
- if (report.passed) {
274
+ if (errors.length === 0) {
213
275
  if (report.warnings.length > 0) {
214
276
  console.log(`\nPassed with ${report.warnings.length} warning(s).`);
215
277
  } else {
@@ -217,9 +279,7 @@ async function main(): Promise<void> {
217
279
  }
218
280
  process.exit(0);
219
281
  } else {
220
- console.error(
221
- `\nFailed: ${report.errors.length} error(s), ${report.warnings.length} warning(s).`,
222
- );
282
+ console.error(`\nFailed: ${errors.length} error(s), ${report.warnings.length} warning(s).`);
223
283
  process.exit(1);
224
284
  }
225
285
  }
@@ -59,6 +59,22 @@
59
59
  * package's headline version on GitHub and npmjs.com and ships in the
60
60
  * tarball, so a half-finished bump is publicly visible. Skipped when the
61
61
  * README, the badge, or the package version is absent (issue #418).
62
+ * 13. npm `files` excludes the built bundle: when `manifest.json` exists and
63
+ * `package.json` `files` covers `dist/` wholesale, it must also carry
64
+ * `"!dist/*.mcpb"`. The `bundle` script writes the `.mcpb` into `dist/`,
65
+ * so without the entry a release that bundles before publishing ships the
66
+ * server and its production dependencies inside the npm tarball
67
+ * (issue #469). Skipped when `manifest.json` or `files` is absent.
68
+ * 14. manifest.json version parity: `version` must equal `package.json`'s, the
69
+ * same rule check 10 applies to the plugin manifests — the bundle's install
70
+ * dialog shows it. Skipped when `manifest.json` or the package version is
71
+ * absent.
72
+ * 15. Dockerfile build platform: every stage that runs `bun run build` must
73
+ * start `FROM --platform=$BUILDPLATFORM`. Without it the non-native leg of
74
+ * a multi-arch `docker buildx` build runs under QEMU, where bun >= 1.4
75
+ * aborts inside the build and no image publishes for either architecture —
76
+ * and the image publishes last, after npm and the MCP Registry. Skipped
77
+ * when there is no `Dockerfile` or no stage builds.
62
78
  *
63
79
  * Every check skips cleanly when its input is absent — consumers who deleted
64
80
  * `manifest.json` for an HTTP-only deploy, or who haven't built a bundle,
@@ -97,6 +113,7 @@ interface Manifest {
97
113
  name?: string;
98
114
  server?: { mcp_config?: { args?: unknown[]; env?: Record<string, string> } };
99
115
  user_config?: Record<string, ManifestUserConfigEntry>;
116
+ version?: unknown;
100
117
  }
101
118
 
102
119
  const USER_CONFIG_REF = /^\$\{user_config\.([\w-]+)\}$/;
@@ -742,6 +759,76 @@ export function checkReadmeVersionBadge(readme: string, packageVersion?: string)
742
759
  ];
743
760
  }
744
761
 
762
+ /** The `files` entry that keeps the `bundle` script's `dist/<name>.mcpb` out of the npm tarball. */
763
+ const BUNDLE_EXCLUSION = '!dist/*.mcpb';
764
+
765
+ /**
766
+ * Check 13: a `files` allowlist that covers `dist/` wholesale must exclude the
767
+ * `.mcpb` the `bundle` script writes there. npm and Bun both honor `!` entries
768
+ * in `files`, and both ignore `.npmignore` once `files` is set, so the entry is
769
+ * the only place the exclusion can live. The caller runs this only when
770
+ * `manifest.json` exists — a project without one never builds a bundle.
771
+ */
772
+ export function checkBundleExcludedFromFiles(files: unknown): string[] {
773
+ if (!Array.isArray(files)) return [];
774
+ const entries = files.filter((entry): entry is string => typeof entry === 'string');
775
+
776
+ const coversDist = entries.some(
777
+ (entry) => entry.replace(/^\.\//, '').replace(/\/(?:\*\*(?:\/\*)?|\*)?$/, '') === 'dist',
778
+ );
779
+ if (!coversDist) return [];
780
+ if (entries.includes(BUNDLE_EXCLUSION) || entries.includes('!dist/**/*.mcpb')) return [];
781
+
782
+ return [
783
+ `package.json "files" covers dist/ without "${BUNDLE_EXCLUSION}" — the bundle script writes ` +
784
+ `dist/<name>.mcpb, so a release that bundles before publishing ships it in the npm tarball; ` +
785
+ `add "${BUNDLE_EXCLUSION}" to "files"`,
786
+ ];
787
+ }
788
+
789
+ /**
790
+ * Check 14: manifest.json `version` must equal `package.json`'s. Skipped when
791
+ * the package version is absent — the same fail-safe as checks 10 and 12.
792
+ */
793
+ export function checkManifestVersion(manifest: Manifest, packageVersion?: string): string[] {
794
+ if (!packageVersion || manifest.version === packageVersion) return [];
795
+ return [
796
+ manifest.version === undefined
797
+ ? `manifest.json has no "version" — must declare the package.json version "${packageVersion}"`
798
+ : `manifest.json "version" is "${String(manifest.version)}" — must equal the package.json version "${packageVersion}"`,
799
+ ];
800
+ }
801
+
802
+ const DOCKERFILE_FROM = /^\s*FROM\s/i;
803
+ const DOCKERFILE_BUILD_PLATFORM = /--platform=\$\{?BUILDPLATFORM\}?(?:\s|$)/;
804
+ const DOCKERFILE_BUILD_STEP = /\bbun run (?:re)?build\b/;
805
+
806
+ /**
807
+ * Check 15: every Dockerfile stage that runs `bun run build` must be pinned to
808
+ * the build platform. Stages are split at `FROM` lines; comment lines are
809
+ * ignored so a note mentioning the build does not count as one.
810
+ */
811
+ export function checkDockerfileBuildPlatform(dockerfile: string): string[] {
812
+ const stages: { from: string; line: number; builds: boolean }[] = [];
813
+ for (const [index, line] of dockerfile.split('\n').entries()) {
814
+ if (DOCKERFILE_FROM.test(line)) {
815
+ stages.push({ from: line.trim(), line: index + 1, builds: false });
816
+ } else if (!line.trimStart().startsWith('#') && DOCKERFILE_BUILD_STEP.test(line)) {
817
+ const stage = stages.at(-1);
818
+ if (stage) stage.builds = true;
819
+ }
820
+ }
821
+
822
+ return stages
823
+ .filter((stage) => stage.builds && !DOCKERFILE_BUILD_PLATFORM.test(stage.from))
824
+ .map(
825
+ (stage) =>
826
+ `Dockerfile:${stage.line} "${stage.from}" runs \`bun run build\` without --platform=$BUILDPLATFORM — ` +
827
+ `a multi-arch buildx build then runs it under QEMU, where bun aborts and no image publishes; ` +
828
+ `start the build stage with "FROM --platform=$BUILDPLATFORM" and copy dist/ into a separate runtime stage`,
829
+ );
830
+ }
831
+
745
832
  /** Read `packaging.pluginManifests` from devcheck.config.json; default on. */
746
833
  function pluginManifestsEnabled(): boolean {
747
834
  const cfg = tryReadJson<{ packaging?: { pluginManifests?: boolean } }>(
@@ -755,7 +842,9 @@ async function main(): Promise<void> {
755
842
  const warnings: string[] = [];
756
843
  const notes: string[] = [];
757
844
 
758
- const pkg = tryReadJson<{ name?: string; version?: string }>(resolve('package.json'));
845
+ const pkg = tryReadJson<{ files?: unknown; name?: string; version?: string }>(
846
+ resolve('package.json'),
847
+ );
759
848
  const unscopedName = pkg?.name?.split('/').pop();
760
849
 
761
850
  // ── Manifest-dependent checks (1–4 + manifest identity) ──
@@ -826,6 +915,9 @@ async function main(): Promise<void> {
826
915
  if (unscopedName) {
827
916
  errors.push(...checkManifestIdentity(manifest, unscopedName));
828
917
  }
918
+
919
+ errors.push(...checkManifestVersion(manifest, pkg?.version));
920
+ errors.push(...checkBundleExcludedFromFiles(pkg?.files));
829
921
  } else {
830
922
  notes.push('No manifest.json — skipping manifest/server.json alignment checks.');
831
923
  }
@@ -874,6 +966,12 @@ async function main(): Promise<void> {
874
966
  }
875
967
  }
876
968
 
969
+ // ── Dockerfile build platform (check 15) ──
970
+ const dockerfilePath = resolve('Dockerfile');
971
+ if (existsSync(dockerfilePath)) {
972
+ errors.push(...checkDockerfileBuildPlatform(readFileSync(dockerfilePath, 'utf-8')));
973
+ }
974
+
877
975
  // ── README version badge (check 12) ──
878
976
  const readmePath = resolve('README.md');
879
977
  if (existsSync(readmePath)) {
@@ -18,6 +18,15 @@
18
18
  * The framework itself has no `manifest.json`/`.mcpb`, so the attach path is
19
19
  * skipped here but scaffolded servers that do have a manifest get the full flow.
20
20
  *
21
+ * Before any `gh` call it validates the tag — `--notes-from-tag` publishes the
22
+ * message verbatim as the release body, so a malformed tag becomes a malformed
23
+ * public release. `--check` runs only that validation, for use right after
24
+ * `git tag -a` and before the tag is pushed. The rules are the ones the
25
+ * `release-and-publish` skill states for the annotation: an annotated tag, a
26
+ * subject of at most 72 characters with no version and no `;`, flat bullets
27
+ * with no section headers, no signature block leaked into the body, and the
28
+ * `[CHANGELOG v<version>](…)` link as the final line.
29
+ *
21
30
  * @module scripts/release-github
22
31
  *
23
32
  * @example
@@ -25,6 +34,10 @@
25
34
  * // bun run release:github
26
35
  *
27
36
  * @example
37
+ * // Validate the tag annotation only (exit 1 on a violation, no gh calls):
38
+ * // bun run release:github -- --check
39
+ *
40
+ * @example
28
41
  * // Dry-run — print the command that would be executed without running it:
29
42
  * // bun run release:github -- --dry-run
30
43
  */
@@ -33,8 +46,86 @@ import { spawnSync } from 'node:child_process';
33
46
  import { existsSync, readFileSync } from 'node:fs';
34
47
  import { resolve } from 'node:path';
35
48
  import process from 'node:process';
49
+ import { fileURLToPath } from 'node:url';
36
50
 
37
51
  const DRY_RUN = process.argv.includes('--dry-run');
52
+ const CHECK_ONLY = process.argv.includes('--check');
53
+
54
+ /** A tag subject longer than this reads as a digest, not a release title. */
55
+ const MAX_SUBJECT_LENGTH = 72;
56
+
57
+ /**
58
+ * Section headers belong in the changelog entry, never in the tag body: a
59
+ * markdown heading, a Keep a Changelog section name, or any other line that is
60
+ * not a bullet and ends in a colon (`Dependency bumps:`, `Highlights:`).
61
+ */
62
+ const SECTION_HEADER =
63
+ /^(?:#{1,6}\s.*|(?:Added|Changed|Deprecated|Removed|Fixed|Security|Dependencies)\s*:?|[^\s\-*+[].*:)$/;
64
+
65
+ /** The parts of an annotated tag that become the GitHub Release title and body. */
66
+ export interface TagMessage {
67
+ body: string;
68
+ /** `git for-each-ref %(objecttype)` — `tag` for an annotated tag, `commit` for a lightweight one. */
69
+ objectType: string;
70
+ subject: string;
71
+ }
72
+
73
+ /**
74
+ * Validates a tag annotation against the release-body rules. Returns one
75
+ * message per violation; an empty array means the tag is publishable.
76
+ */
77
+ export function checkTagMessage(tag: TagMessage, version: string): string[] {
78
+ if (tag.objectType !== 'tag') {
79
+ return [
80
+ `v${version} is a lightweight tag — recreate it annotated: git tag -a v${version} -F <file>`,
81
+ ];
82
+ }
83
+
84
+ const errors: string[] = [];
85
+ const { subject } = tag;
86
+ if (subject.length > MAX_SUBJECT_LENGTH) {
87
+ errors.push(
88
+ `subject is ${subject.length} characters — keep it one short theme (≤${MAX_SUBJECT_LENGTH}); the bullets carry the digest`,
89
+ );
90
+ }
91
+ if (subject.includes(version)) {
92
+ errors.push(
93
+ `subject contains the version "${version}" — GitHub prepends "v${version}:" to the title`,
94
+ );
95
+ }
96
+ if (subject.includes(';')) {
97
+ errors.push('subject contains ";" — one theme, not a list of changes');
98
+ }
99
+
100
+ if (tag.body.includes('-----BEGIN')) {
101
+ errors.push(
102
+ 'body contains a signature block — the signature did not parse (usually --cleanup=verbatim); recreate the tag with --cleanup=whitespace before it is pushed',
103
+ );
104
+ }
105
+
106
+ const lines = tag.body
107
+ .split('\n')
108
+ .map((line) => line.trim())
109
+ .filter((line) => line.length > 0);
110
+ const headers = lines.filter((line) => SECTION_HEADER.test(line));
111
+ if (headers.length > 0) {
112
+ errors.push(
113
+ `body has section headers (${headers.map((h) => `"${h}"`).join(', ')}) — flat bullets only; sections belong in the changelog entry`,
114
+ );
115
+ }
116
+
117
+ const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
118
+ const changelogLink = new RegExp(
119
+ `^\\[CHANGELOG v${escaped}\\]\\(\\S+/changelog/\\d+\\.\\d+\\.x/${escaped}\\.md\\)`,
120
+ );
121
+ if (!changelogLink.test(lines.at(-1) ?? '')) {
122
+ errors.push(
123
+ `final line is not the changelog link — end the body with "[CHANGELOG v${version}](https://github.com/<OWNER>/<REPO>/blob/main/changelog/<major.minor>.x/${version}.md)"`,
124
+ );
125
+ }
126
+
127
+ return errors;
128
+ }
38
129
 
39
130
  // ── Helpers ───────────────────────────────────────────────────────────────────
40
131
 
@@ -107,15 +198,34 @@ function main(): void {
107
198
  if (!subject) {
108
199
  console.error(
109
200
  `Tag ${tag} not found locally or has no subject line. ` +
110
- `Create the annotated tag first: git tag -a ${tag} -m "..."`,
201
+ `Create the annotated tag first: git tag -a ${tag} -F <file>`,
111
202
  );
112
203
  process.exit(1);
113
204
  }
114
205
 
206
+ // 3. Validate the annotation — it becomes the public release body verbatim
207
+ const errors = checkTagMessage(
208
+ {
209
+ subject,
210
+ body: run('git', ['for-each-ref', `refs/tags/${tag}`, '--format=%(contents:body)']),
211
+ objectType: run('git', ['for-each-ref', `refs/tags/${tag}`, '--format=%(objecttype)']),
212
+ },
213
+ version,
214
+ );
215
+ if (errors.length > 0) {
216
+ console.error(`Tag ${tag} is not publishable:`);
217
+ for (const error of errors) console.error(` ✗ ${error}`);
218
+ process.exit(1);
219
+ }
220
+ if (CHECK_ONLY) {
221
+ console.log(`Tag ${tag} OK.`);
222
+ return;
223
+ }
224
+
115
225
  const title = `${tag}: ${subject}`;
116
226
  const hasMcpb = existsSync('manifest.json');
117
227
 
118
- // 3. Build the gh release create command
228
+ // 4. Build the gh release create command
119
229
  const createArgs = [
120
230
  'release',
121
231
  'create',
@@ -152,7 +262,7 @@ function main(): void {
152
262
  console.log(' asset: dist/*.mcpb');
153
263
  }
154
264
 
155
- // 4. Try to create the release
265
+ // 5. Try to create the release
156
266
  const createResult = gh(createArgs, { required: false });
157
267
 
158
268
  if (!createResult.startsWith('__ERROR__:')) {
@@ -170,7 +280,7 @@ function main(): void {
170
280
  process.exit(1);
171
281
  }
172
282
 
173
- // 5. Release already exists — repair: upload asset (if applicable) and set title.
283
+ // 6. Release already exists — repair: upload asset (if applicable) and set title.
174
284
  console.log(`Release ${tag} already exists. Repairing…`);
175
285
 
176
286
  if (hasMcpb) {
@@ -184,4 +294,6 @@ function main(): void {
184
294
  console.log(`Release ${tag} repaired.`);
185
295
  }
186
296
 
187
- main();
297
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
298
+ main();
299
+ }
@@ -31,12 +31,16 @@ MCP_SESSION_MODE=stateless # stateful | stateless | auto. Set here, not
31
31
  # MCP_LOG_LEVEL=info # debug | info | notice | warning | error
32
32
  # MCP_LOG_RATE_LIMIT_THRESHOLD=10 # Max emissions per level+message per window; 0 disables
33
33
  # MCP_LOG_RATE_LIMIT_WINDOW_MS=60000
34
+ # LOG_TOOL_FAILURE_PAYLOADS=false # Log failed tool calls' arguments + result (key-name redaction only;
35
+ # secrets in free-form values are kept). Reaches stderr, files, and OTLP
36
+ # LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES=16384 # Per-payload cap, UTF-8 bytes
34
37
 
35
38
  # ── Telemetry ─────────────────────────────────────────────────────────
36
39
  # OTEL_ENABLED=false # Enable OpenTelemetry (default: false)
37
40
  # OTEL_EXPORTER_OTLP_ENDPOINT= # OTLP base URL (e.g., http://localhost:4318); traces → /v1/traces, metrics → /v1/metrics
38
41
  # OTEL_EXPORTER_OTLP_TRACES_ENDPOINT= # Overrides the base for traces; used as-is
39
42
  # OTEL_EXPORTER_OTLP_METRICS_ENDPOINT= # Overrides the base for metrics; used as-is
43
+ # OTEL_EXPORTER_OTLP_LOGS_ENDPOINT= # Opt-in log export (e.g., http://localhost:4318/v1/logs); the base never enables it
40
44
 
41
45
  # ── Server-specific ──────────────────────────────────────────────────
42
46
  # Add your server's environment variables below
@@ -56,8 +56,15 @@ LABEL org.opencontainers.image.licenses="Apache-2.0"
56
56
  LABEL org.opencontainers.image.version="${APP_VERSION}"
57
57
  LABEL org.opencontainers.image.source=""
58
58
 
59
- # Copy dependency manifests
60
- COPY package.json bun.lock ./
59
+ # Copy dependency manifests. `bunfig.toml` rides along so every install below
60
+ # passes its release-age gate and security scanner, as a local install does.
61
+ COPY package.json bun.lock bunfig.toml ./
62
+
63
+ # The scanner bunfig.toml names is a devDependency, and Bun installs a missing
64
+ # scanner through the same production-filtered install, which omits it and
65
+ # aborts. Seed it from the build stage's full install instead. Remove this line
66
+ # together with the scanner if bunfig.toml stops naming one.
67
+ COPY --from=build /usr/src/app/node_modules/@socketsecurity/bun-security-scanner ./node_modules/@socketsecurity/bun-security-scanner
61
68
 
62
69
  # Install only production dependencies, ignoring any lifecycle scripts (like 'prepare')
63
70
  # that are not needed in the final production image.
@@ -72,19 +79,33 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \
72
79
  # Conditionally install OpenTelemetry optional peer dependencies (Tier 3).
73
80
  # Installed by default. Omit them for a leaner image at build time
74
81
  # with: docker build --build-arg OTEL_ENABLED=false
82
+ # Each package is requested at the range the installed framework declares in
83
+ # `peerDependencies`, so the resolution stays inside the framework's tested
84
+ # peer range; a name with no declared range fails the build.
75
85
  ARG OTEL_ENABLED=true
76
86
  RUN --mount=type=cache,target=/root/.bun/install/cache \
77
87
  if [ "$OTEL_ENABLED" = "true" ]; then \
78
- bun add --omit=dev --omit=peer --ignore-scripts @hono/otel \
79
- @opentelemetry/instrumentation-http \
88
+ specs=$(bun -e ' \
89
+ const { peerDependencies: peers } = await Bun.file("node_modules/@cyanheads/mcp-ts-core/package.json").json(); \
90
+ const names = process.argv.slice(1); \
91
+ const missing = names.filter((name) => !peers?.[name]); \
92
+ if (missing.length > 0) throw new Error(`no peerDependencies range for ${missing.join(", ")}`); \
93
+ console.log(names.map((name) => `${name}@${peers[name]}`).join(" ")); \
94
+ ' \
95
+ @hono/otel \
96
+ @opentelemetry/api-logs \
97
+ @opentelemetry/exporter-logs-otlp-http \
80
98
  @opentelemetry/exporter-metrics-otlp-http \
81
99
  @opentelemetry/exporter-trace-otlp-http \
100
+ @opentelemetry/instrumentation-http \
82
101
  @opentelemetry/instrumentation-pino \
83
102
  @opentelemetry/resources \
103
+ @opentelemetry/sdk-logs \
84
104
  @opentelemetry/sdk-metrics \
85
105
  @opentelemetry/sdk-node \
86
106
  @opentelemetry/sdk-trace-node \
87
- @opentelemetry/semantic-conventions; \
107
+ @opentelemetry/semantic-conventions) \
108
+ && bun add --omit=dev --omit=peer --ignore-scripts $specs; \
88
109
  fi
89
110
 
90
111
  # Copy the compiled application code from the build stage
@@ -128,7 +149,6 @@ ENV MCP_TRANSPORT_TYPE="http"
128
149
  ENV MCP_SESSION_MODE="stateless"
129
150
  ENV MCP_LOG_LEVEL="info"
130
151
  ENV LOGS_DIR="/var/log/{{PACKAGE_NAME}}"
131
- ENV MCP_FORCE_CONSOLE_LOGGING="true"
132
152
 
133
153
  # Expose the port the server listens on
134
154
  EXPOSE ${MCP_HTTP_PORT}
@@ -3,6 +3,8 @@
3
3
  /.claude/
4
4
  /.agents/
5
5
  /framework-skills/
6
+ /docs/idea.md
7
+ /logs/
6
8
  /Dockerfile
7
9
  /bun.lock
8
10
  /bunfig.toml
@@ -12,6 +12,7 @@
12
12
  "files": [
13
13
  "changelog/",
14
14
  "dist/",
15
+ "!dist/*.mcpb",
15
16
  "README.md",
16
17
  "LICENSE",
17
18
  "CLAUDE.md",