@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
package/AGENTS.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Package:** `@cyanheads/mcp-ts-core`
4
- **Version:** 0.13.7
4
+ **Version:** 0.13.9
5
5
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
6
- **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
6
+ **MCP SDK:** `@modelcontextprotocol/server` ^2.1.0 (protocol revisions 2026-07-28 and 2025-*)
7
7
  **Zod:** ^4.6.5
8
8
  **GitHub:** [cyanheads/mcp-ts-core](https://github.com/cyanheads/mcp-ts-core)
9
9
  **npm:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
@@ -100,9 +100,8 @@ await createApp({
100
100
  tools: allToolDefinitions,
101
101
  resources: allResourceDefinitions,
102
102
  prompts: allPromptDefinitions,
103
- instructions: // server-level orientation, sent on every initialize
104
- 'Pre-configured shortcuts:\n- `default` → production API\n' +
105
- 'Other endpoints reachable via `connect({ baseUrl })`.',
103
+ instructions: // server-level orientation, sent on every initialize — one literal, a few sentences
104
+ 'Calls reach the production API by default. Pass `baseUrl` to `connect` to reach another endpoint.',
106
105
  extensions: { // SEP-2133 extensions advertised in capabilities
107
106
  'vendor/my-extension': { /* extension config */ },
108
107
  },
@@ -168,7 +167,7 @@ interface ServerHandle {
168
167
  }
169
168
  ```
170
169
 
171
- **Exit contract.** `shutdown()` is exit-free and unbounded: it also serves the startup-failure rollback and direct calls from embedders and tests, so ending the process belongs to the handlers. `SIGTERM`, `SIGINT`, and stdin EOF each run that same shutdown and then exit explicitly. A signal exits 0 once the shutdown settles and 1 when the 10 s ceiling fires, after a warning naming the step that never settled; stdin EOF exits 0 either way, unchanged. The ceiling bounds the shutdown as a whole rather than any single await, so a step that settles inside it is never truncated. A second signal mid-shutdown reaches no handler (shutdown detaches them as it starts) and terminates on the OS default, 143 / 130 — the operator's force-kill escape hatch. `uncaughtException` / `unhandledRejection` exit 1.
170
+ **Exit contract.** `shutdown()` is exit-free and unbounded: it also serves the startup-failure rollback and direct calls from embedders and tests, so ending the process belongs to the handlers. `SIGTERM`, `SIGINT`, and stdin EOF each run that same shutdown and then exit explicitly. On stdin EOF the SDK transport has already closed itself, so a request still in flight is aborted (its `ctx.signal` fires) and never answered — the client has hung up. A signal exits 0 once the shutdown settles and 1 when the 10 s ceiling fires, after a warning naming the step that never settled; stdin EOF exits 0 either way, unchanged. The ceiling bounds the shutdown as a whole rather than any single await, so a step that settles inside it is never truncated. A second signal mid-shutdown reaches no handler (shutdown detaches them as it starts) and terminates on the OS default, 143 / 130 — the operator's force-kill escape hatch. `uncaughtException` / `unhandledRejection` exit 1.
172
171
 
173
172
  ---
174
173
 
@@ -238,7 +237,7 @@ export const myTool = tool('my_tool', {
238
237
  });
239
238
  ```
240
239
 
241
- **Steps:** Create `src/mcp-server/tools/definitions/[name].tool.ts` (kebab-case) → use `tool('snake_case', {...})` with Zod `.describe()` on all fields → implement `handler(input, ctx)` (pure, throws on failure) → add `auth`/`format` if needed → register in `definitions/index.ts` → `bun run devcheck` → smoke-test with `bun run rebuild && bun run start:stdio` (or `start:http`).
240
+ **Steps:** Create `src/mcp-server/tools/definitions/[name].tool.ts` (kebab-case) → use `tool('snake_case', {...})` with Zod `.describe()` on all fields → implement `handler(input, ctx)` (pure, throws on failure) → add `auth`/`format` if needed → register in `definitions/index.ts` → `bun run devcheck` → smoke-test with `bun run rebuild && bun run start:stdio < /dev/null` (or `start:http`) and confirm the `Core services constructed` log record lists the tool in its `tools` field (the message text shows only counts).
242
241
 
243
242
  **Schema constraint:** Input/output schemas must use JSON-Schema-serializable Zod types only. The MCP SDK converts schemas to JSON Schema for `tools/list` — non-serializable types (`z.custom()`, `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.void()`, `z.map()`, `z.set()`, `z.function()`, `z.nan()`) cause a hard runtime failure. Use structural equivalents instead (e.g., `z.string()` with `.describe('ISO 8601 date')` instead of `z.date()`). The `schema-serializable` lint rule catches this at build time (`bun run lint:mcp` / `devcheck`).
244
243
 
@@ -255,7 +254,7 @@ export const myTool = tool('my_tool', {
255
254
 
256
255
  **Strict input:** `tool()` stores `input.strict()`, so an unrecognized argument key is rejected by name before the handler runs and `inputSchema` advertises `additionalProperties: false`. Root-level only — a nested `z.object()` still strips unless it is strict itself. An explicit `.passthrough()` / `.catchall()` is honored. Declare `.strict()` **before** `.describe()` / `.meta()` on the root: Zod keys both to the schema instance and `.strict()` clones without it, so a root describe declared after is discarded and never advertised — `lint:mcp` reports that as `schema-root-meta-discarded`.
257
256
 
258
- **Pre-validation:** an ordered step inside `parseToolArguments` rescues calls strict input would otherwise reject — drop client-added root keys (`_meta`, `tool_call_description`, `toolCallId`, any undeclared `_`-prefixed key) → rewrite key aliases (declared `inputAliases`, plus any undeclared key whose case-folded form names exactly one declared key) → parse → on failure, repair a JSON-stringified array and re-parse once, keeping it only if the schema then accepts it. All on by default; nothing changes the advertised `inputSchema`, and a call that still fails throws the identical rejection. A declared key, an author-opened root, and a `headerParam` target are never touched. Server-level switches: `createApp({ input: { ignoreKeys, caseStyleAliases, coerce } })`. Counters: `mcp.input.ignored_key`, `mcp.input.aliased`, `mcp.input.coerced`. Lint: `input-alias-conflict`. See `add-tool` skill.
257
+ **Pre-validation:** an ordered step inside `parseToolArguments` rescues calls strict input would otherwise reject — drop client-added root keys (`_meta`, `tool_call_description`, `toolCallId`, any undeclared `_`-prefixed key) → rewrite key aliases (declared `inputAliases`, plus any undeclared key whose case-folded form names exactly one declared key) → parse → on failure, repair a JSON-stringified array or object, or a safe integer where a string is expected (`8654467` → `"8654467"`), and re-parse once, keeping the repair only if the schema then accepts it. If that still fails and the drop discarded a key, the stages rerun alias-first so `_query` or a declared `_q` alias reaches its target, and the retry (repair included) is kept only if it validates — a call the first order validates resolves exactly as it would without the retry. All on by default, and nothing changes the advertised `inputSchema`. A call that still fails throws the rejection of the last order tried — the retry's when it ran, so a declared `_q` alias with a bad value reports that value's failure and `Validated _q as query.` — the same one it gets under `coerce: false`, reporting that order's rewrites and underscore-rule drops as `data.input` and in the hint. A declared key, an author-opened root, and a `headerParam` target are never touched. Server-level switches: `createApp({ input: { ignoreKeys, caseStyleAliases, coerce } })`. Counters, for the attempt the handler receives: `mcp.input.ignored_key`, `mcp.input.aliased`, `mcp.input.coerced` (once per repair kind). Lint: `input-alias-conflict`. See `add-tool` skill.
259
258
 
260
259
  **Header-mirrored input (2026-07-28):** `headerParam(z.string(), 'Region')` designates an input property with `x-mcp-header`, so its value also rides an `Mcp-Param-Region` request header and an intermediary can read it without parsing the body. Mirroring, not relocation — the handler still reads the argument from the body, and nothing else about the field changes. Only a primitive-typed (`string`/`integer`/`number`/`boolean`) property statically reachable through a chain of `properties` keys qualifies: an array element, a `z.record()` value, and every field of a discriminated-union input root are unreachable, and header names must be RFC 9110 tokens, case-insensitively unique per schema. `tool()` rejects a violation at definition time naming the field path — the SDK only warns, then conforming Streamable HTTP clients drop the tool. Lint rule: `header-param-designation`.
261
260
 
@@ -303,7 +302,7 @@ interface Context {
303
302
  readonly auth?: AuthContext;
304
303
  readonly log: ContextLogger; // auto-correlated: requestId, traceId, tenantId
305
304
  readonly state: ContextState; // tenant-scoped KV storage
306
- readonly requestInput: RequestInputFn; // (spec) => never — suspends and asks the caller for input
305
+ readonly requestInput: RequestInputFn; // (spec, options?) => never — suspends and asks the caller for input
307
306
  readonly inputs: ContextInputs; // reader over a retried request's responses
308
307
  readonly notifyPromptListChanged?: (() => void) | undefined; // prompt list changed
309
308
  readonly notifyResourceListChanged?: (() => void) | undefined; // resource list changed
@@ -368,6 +367,11 @@ useFormat(answer.format);
368
367
  cancelled prompt is terminal, not a round to retry. `inputRequired.elicitUrl({ message, url })`
369
368
  hands the user an external link instead of a form.
370
369
 
370
+ A 2025-era client that declared no matching capability is refused as `client_capability_missing`,
371
+ with a hint that ends at reconnecting. When the tool's own arguments can stand in for the answer,
372
+ say so per call — `ctx.requestInput(spec, { fallbackHint: 'Or call again with noun supplied.' })` —
373
+ and the sentence is appended to that hint. A consent gate passes none: it has no such field.
374
+
371
375
  ### `ctx.content`
372
376
 
373
377
  Accumulates non-text content blocks — image/audio bytes, embedded resources, resource links — onto the response: `ctx.content.image(data, mimeType)`, `ctx.content.audio(data, mimeType)`, or `ctx.content(block)` for a raw `ContentBlock`. Blocks are prepended to `content[]` after `format()` runs and never enter `structuredContent`, so a handler can emit media for the calling model without the base64 duplicating into typed output. Always present (no-op when unused); callable from handler and service layer.
@@ -416,9 +420,9 @@ Available factories: `invalidParams`, `invalidRequest`, `notFound`, `forbidden`,
416
420
 
417
421
  For HTTP responses from upstream APIs, use `httpErrorFromResponse(response, { service, data })` from `/utils` — maps the full status table (401/403/408/422/429/5xx) and captures body + `Retry-After`.
418
422
 
419
- **Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: request signal already aborted (→ `RequestCancelled`, outranking the thrown value's own code, `McpError` included) → `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → JS constructor name (`TypeError` → `ValidationError`) → provider patterns (HTTP status codes, AWS errors, DB errors) → common message patterns → `AbortError` name (→ `Timeout`) → `InternalError` fallback.
423
+ **Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: request signal already aborted (→ `RequestCancelled`, outranking the thrown value's own code, `McpError` included) → `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → engine resource-limit `RangeError` by whole message — stack overflow, maximum string size (→ `InternalError`) → JS constructor name (`SyntaxError` → `ValidationError`; `TypeError` is excluded) → provider patterns (HTTP status codes, AWS errors, DB errors) → common message patterns → `AbortError` name (→ `Timeout`) → `InternalError` fallback. A result that breaks the definition's own `output` or `enrichment` schema fails as `InternalError` naming that contract, not `ValidationError`.
420
424
 
421
- **Error-path parity.** Tool errors: `content[]` carries `Error: <message>`, then `Recovery: <hint>` when the hint says something the message does not already contain, then a closing `(reason … · not retryable)` for whichever of `data.reason` / `data.retryable` is present; the numeric code and `data.issues` stay JSON-only. `structuredContent.error` carries `{ code, message, data? }`. No `_meta.error`. Resources re-throw via JSON-RPC error envelope. An argument rejection is one of them: `-32602` with `data.issues`, plus `data.reason: 'invalid_arguments'` and a hint synthesized from the issues and the root schema — never a tool-declared `reason`, since the handler never ran. `client_capability_missing` is the second framework-owned reason: a `ctx.requestInput` return a 2025-era connection cannot serve is refused before any wire traffic as `-32600` carrying that reason and a hint naming the capability.
425
+ **Error-path parity.** Tool errors: `content[]` carries `Error: <message>`, then `Recovery: <hint>` when the hint says something the message does not already contain, then a closing `(reason … · not retryable)` for whichever of `data.reason` / `data.retryable` is present; the numeric code and `data.issues` stay JSON-only. `structuredContent.error` carries `{ code, message, data? }`. No `_meta.error`. Resources re-throw via JSON-RPC error envelope. An argument rejection is one of them: `-32602` with `data.issues`, plus `data.reason: 'invalid_arguments'` and a hint synthesized from the issues and the root schema — never a tool-declared `reason`, since the handler never ran. When pre-validation rewrote or dropped a key the caller wrote, `data.input` (`{ aliased: [{ alias, target }], ignored }`) names it and the hint closes with `Validated … as ….` / `Dropped undeclared key ….`; an ignore-list drop is never reported. `client_capability_missing` is the second framework-owned reason: a `ctx.requestInput` return a 2025-era connection cannot serve is refused before any wire traffic as `-32600` carrying that reason and a hint naming the capability.
422
426
 
423
427
  **Lint rules** (all warnings, surfaced in `devcheck`): `prefer-mcp-error-in-handler`, `prefer-error-factory`, `preserve-cause-on-rethrow`, `no-stringify-upstream-error`, `error-contract-conformance`, `error-contract-prefer-fail`, `error-contract-unthrown` (a declared reason no literal `ctx.fail`/`ctx.recoveryFor` in the handler names, unless marked `thrownBy: 'service'`), `error-contract-recovery-unforwarded` (a `ctx.fail` site carrying neither `ctx.recoveryFor('<reason>')` nor its own `recovery` key, so the declared hint reaches neither client surface). See `api-linter` skill.
424
428
 
@@ -520,7 +524,7 @@ Detailed method signatures, options, and examples live in skill files. Read the
520
524
 
521
525
  Each `framework-skills/<name>/SKILL.md` carries `metadata.version` in frontmatter. The `maintenance` skill's Phase A uses this to sync consumer copies — replaces the **entire skill directory** as one unit. Without a version bump, Phase A skips the skill (content-hash backstop catches drift, but noisier).
522
526
 
523
- **Policy:** Bump `metadata.version` when changing any file under `framework-skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt. One bump per release cycle suffices. Enforced by `bun run devcheck` (`scripts/check-skill-versions.ts`): a SKILL.md body change vs `HEAD` without a `metadata.version` bump surfaces as a warning; whitespace-only edits never trigger it, and a genuine typo fix opts out via `devcheck.config.json` `skillVersions.ignore`.
527
+ **Policy:** Bump `metadata.version` when changing any file under `framework-skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt. **Exactly one step per release, however many edits land:** before bumping, compare the version against the last release tag (`git show $(git describe --tags --abbrev=0):framework-skills/<name>/SKILL.md`) and skip the bump when it has already moved. Enforced by `bun run devcheck` (`scripts/check-skill-versions.ts`), both directions as warnings: a SKILL.md body change vs `HEAD` without a `metadata.version` bump while the version still matches the last release tag, and a version more than one step (next minor, or next major at `.0`) past the last release tag. Whitespace-only edits never trigger the first, and a genuine typo fix opts out via `devcheck.config.json` `skillVersions.ignore`.
524
528
 
525
529
  Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable via the agent's skill registry at session start. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, and these are development-time skills, not skills for the agents that use a server. `skills/` stays free for that second kind.
526
530
 
@@ -540,10 +544,10 @@ Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable
540
544
  - **Auth:** via `auth: ['scope']` on definitions (not HOF wrapper)
541
545
  - **Missing input:** read `ctx.inputs` first, then `return ctx.requestInput(...)`
542
546
  - **Pagination:** large resource lists use `extractCursor`/`paginateArray`
543
- - **Registration:** definitions exported in `definitions/index.ts` barrel
547
+ - **Registration:** definitions collected in the `definitions/index.ts` barrel's array passed to `createApp()` — an `export` line alone registers nothing
544
548
  - **Tests:** `createMockContext()`, `.handler()` tested directly
545
549
  - **Gate:** `bun run devcheck` passes (includes MCP definition linting)
546
- - **Smoke-test:** `bun run rebuild && bun run start:stdio` (or `start:http`)
550
+ - **Smoke-test:** `bun run rebuild && bun run start:stdio < /dev/null` (or `start:http`); the `Core services constructed` log record lists every definition in its `tools` / `resources` / `prompts` fields
547
551
 
548
552
  ---
549
553
 
@@ -566,7 +570,7 @@ Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable
566
570
  | `bun run test:leak-gate` | The retention gate's own sentinel suite. Each case spawns a full Vitest run, so it is excluded from the `unit` project |
567
571
  | `bun run test:coverage` | Root projects with coverage thresholds enforced |
568
572
  | `bun run test:integration` | Real server subprocesses over stdio and HTTP |
569
- | `bun run test:worker` | The framework under real `workerd`, then a standalone Worker bundle through the Wrangler toolchain — real Node on both legs |
573
+ | `bun run test:worker` | The framework under real `workerd`, then a standalone Worker bundle through the Wrangler toolchain — real Node on both legs. The `workerd` leg enforces its own coverage thresholds over the Worker entry and Cloudflare storage providers, reported to `reports/coverage-worker/` |
570
574
  | `bun run test:package` | Rebuilds, packs the tarball, and consumes it as an external project would (exports, declarations, both runtimes) |
571
575
  | `bun run test:node` | Root projects + integration under real Node via `scripts/with-node.ts`, which bypasses Bun's `node` PATH shim |
572
576
  | `bun run test:order` | Root projects on real Node in shuffled file order under a pinned seed — catches inter-file state leakage |
package/CLAUDE.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Package:** `@cyanheads/mcp-ts-core`
4
- **Version:** 0.13.7
4
+ **Version:** 0.13.9
5
5
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
6
- **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
6
+ **MCP SDK:** `@modelcontextprotocol/server` ^2.1.0 (protocol revisions 2026-07-28 and 2025-*)
7
7
  **Zod:** ^4.6.5
8
8
  **GitHub:** [cyanheads/mcp-ts-core](https://github.com/cyanheads/mcp-ts-core)
9
9
  **npm:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
@@ -100,9 +100,8 @@ await createApp({
100
100
  tools: allToolDefinitions,
101
101
  resources: allResourceDefinitions,
102
102
  prompts: allPromptDefinitions,
103
- instructions: // server-level orientation, sent on every initialize
104
- 'Pre-configured shortcuts:\n- `default` → production API\n' +
105
- 'Other endpoints reachable via `connect({ baseUrl })`.',
103
+ instructions: // server-level orientation, sent on every initialize — one literal, a few sentences
104
+ 'Calls reach the production API by default. Pass `baseUrl` to `connect` to reach another endpoint.',
106
105
  extensions: { // SEP-2133 extensions advertised in capabilities
107
106
  'vendor/my-extension': { /* extension config */ },
108
107
  },
@@ -168,7 +167,7 @@ interface ServerHandle {
168
167
  }
169
168
  ```
170
169
 
171
- **Exit contract.** `shutdown()` is exit-free and unbounded: it also serves the startup-failure rollback and direct calls from embedders and tests, so ending the process belongs to the handlers. `SIGTERM`, `SIGINT`, and stdin EOF each run that same shutdown and then exit explicitly. A signal exits 0 once the shutdown settles and 1 when the 10 s ceiling fires, after a warning naming the step that never settled; stdin EOF exits 0 either way, unchanged. The ceiling bounds the shutdown as a whole rather than any single await, so a step that settles inside it is never truncated. A second signal mid-shutdown reaches no handler (shutdown detaches them as it starts) and terminates on the OS default, 143 / 130 — the operator's force-kill escape hatch. `uncaughtException` / `unhandledRejection` exit 1.
170
+ **Exit contract.** `shutdown()` is exit-free and unbounded: it also serves the startup-failure rollback and direct calls from embedders and tests, so ending the process belongs to the handlers. `SIGTERM`, `SIGINT`, and stdin EOF each run that same shutdown and then exit explicitly. On stdin EOF the SDK transport has already closed itself, so a request still in flight is aborted (its `ctx.signal` fires) and never answered — the client has hung up. A signal exits 0 once the shutdown settles and 1 when the 10 s ceiling fires, after a warning naming the step that never settled; stdin EOF exits 0 either way, unchanged. The ceiling bounds the shutdown as a whole rather than any single await, so a step that settles inside it is never truncated. A second signal mid-shutdown reaches no handler (shutdown detaches them as it starts) and terminates on the OS default, 143 / 130 — the operator's force-kill escape hatch. `uncaughtException` / `unhandledRejection` exit 1.
172
171
 
173
172
  ---
174
173
 
@@ -238,7 +237,7 @@ export const myTool = tool('my_tool', {
238
237
  });
239
238
  ```
240
239
 
241
- **Steps:** Create `src/mcp-server/tools/definitions/[name].tool.ts` (kebab-case) → use `tool('snake_case', {...})` with Zod `.describe()` on all fields → implement `handler(input, ctx)` (pure, throws on failure) → add `auth`/`format` if needed → register in `definitions/index.ts` → `bun run devcheck` → smoke-test with `bun run rebuild && bun run start:stdio` (or `start:http`).
240
+ **Steps:** Create `src/mcp-server/tools/definitions/[name].tool.ts` (kebab-case) → use `tool('snake_case', {...})` with Zod `.describe()` on all fields → implement `handler(input, ctx)` (pure, throws on failure) → add `auth`/`format` if needed → register in `definitions/index.ts` → `bun run devcheck` → smoke-test with `bun run rebuild && bun run start:stdio < /dev/null` (or `start:http`) and confirm the `Core services constructed` log record lists the tool in its `tools` field (the message text shows only counts).
242
241
 
243
242
  **Schema constraint:** Input/output schemas must use JSON-Schema-serializable Zod types only. The MCP SDK converts schemas to JSON Schema for `tools/list` — non-serializable types (`z.custom()`, `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.void()`, `z.map()`, `z.set()`, `z.function()`, `z.nan()`) cause a hard runtime failure. Use structural equivalents instead (e.g., `z.string()` with `.describe('ISO 8601 date')` instead of `z.date()`). The `schema-serializable` lint rule catches this at build time (`bun run lint:mcp` / `devcheck`).
244
243
 
@@ -255,7 +254,7 @@ export const myTool = tool('my_tool', {
255
254
 
256
255
  **Strict input:** `tool()` stores `input.strict()`, so an unrecognized argument key is rejected by name before the handler runs and `inputSchema` advertises `additionalProperties: false`. Root-level only — a nested `z.object()` still strips unless it is strict itself. An explicit `.passthrough()` / `.catchall()` is honored. Declare `.strict()` **before** `.describe()` / `.meta()` on the root: Zod keys both to the schema instance and `.strict()` clones without it, so a root describe declared after is discarded and never advertised — `lint:mcp` reports that as `schema-root-meta-discarded`.
257
256
 
258
- **Pre-validation:** an ordered step inside `parseToolArguments` rescues calls strict input would otherwise reject — drop client-added root keys (`_meta`, `tool_call_description`, `toolCallId`, any undeclared `_`-prefixed key) → rewrite key aliases (declared `inputAliases`, plus any undeclared key whose case-folded form names exactly one declared key) → parse → on failure, repair a JSON-stringified array and re-parse once, keeping it only if the schema then accepts it. All on by default; nothing changes the advertised `inputSchema`, and a call that still fails throws the identical rejection. A declared key, an author-opened root, and a `headerParam` target are never touched. Server-level switches: `createApp({ input: { ignoreKeys, caseStyleAliases, coerce } })`. Counters: `mcp.input.ignored_key`, `mcp.input.aliased`, `mcp.input.coerced`. Lint: `input-alias-conflict`. See `add-tool` skill.
257
+ **Pre-validation:** an ordered step inside `parseToolArguments` rescues calls strict input would otherwise reject — drop client-added root keys (`_meta`, `tool_call_description`, `toolCallId`, any undeclared `_`-prefixed key) → rewrite key aliases (declared `inputAliases`, plus any undeclared key whose case-folded form names exactly one declared key) → parse → on failure, repair a JSON-stringified array or object, or a safe integer where a string is expected (`8654467` → `"8654467"`), and re-parse once, keeping the repair only if the schema then accepts it. If that still fails and the drop discarded a key, the stages rerun alias-first so `_query` or a declared `_q` alias reaches its target, and the retry (repair included) is kept only if it validates — a call the first order validates resolves exactly as it would without the retry. All on by default, and nothing changes the advertised `inputSchema`. A call that still fails throws the rejection of the last order tried — the retry's when it ran, so a declared `_q` alias with a bad value reports that value's failure and `Validated _q as query.` — the same one it gets under `coerce: false`, reporting that order's rewrites and underscore-rule drops as `data.input` and in the hint. A declared key, an author-opened root, and a `headerParam` target are never touched. Server-level switches: `createApp({ input: { ignoreKeys, caseStyleAliases, coerce } })`. Counters, for the attempt the handler receives: `mcp.input.ignored_key`, `mcp.input.aliased`, `mcp.input.coerced` (once per repair kind). Lint: `input-alias-conflict`. See `add-tool` skill.
259
258
 
260
259
  **Header-mirrored input (2026-07-28):** `headerParam(z.string(), 'Region')` designates an input property with `x-mcp-header`, so its value also rides an `Mcp-Param-Region` request header and an intermediary can read it without parsing the body. Mirroring, not relocation — the handler still reads the argument from the body, and nothing else about the field changes. Only a primitive-typed (`string`/`integer`/`number`/`boolean`) property statically reachable through a chain of `properties` keys qualifies: an array element, a `z.record()` value, and every field of a discriminated-union input root are unreachable, and header names must be RFC 9110 tokens, case-insensitively unique per schema. `tool()` rejects a violation at definition time naming the field path — the SDK only warns, then conforming Streamable HTTP clients drop the tool. Lint rule: `header-param-designation`.
261
260
 
@@ -303,7 +302,7 @@ interface Context {
303
302
  readonly auth?: AuthContext;
304
303
  readonly log: ContextLogger; // auto-correlated: requestId, traceId, tenantId
305
304
  readonly state: ContextState; // tenant-scoped KV storage
306
- readonly requestInput: RequestInputFn; // (spec) => never — suspends and asks the caller for input
305
+ readonly requestInput: RequestInputFn; // (spec, options?) => never — suspends and asks the caller for input
307
306
  readonly inputs: ContextInputs; // reader over a retried request's responses
308
307
  readonly notifyPromptListChanged?: (() => void) | undefined; // prompt list changed
309
308
  readonly notifyResourceListChanged?: (() => void) | undefined; // resource list changed
@@ -368,6 +367,11 @@ useFormat(answer.format);
368
367
  cancelled prompt is terminal, not a round to retry. `inputRequired.elicitUrl({ message, url })`
369
368
  hands the user an external link instead of a form.
370
369
 
370
+ A 2025-era client that declared no matching capability is refused as `client_capability_missing`,
371
+ with a hint that ends at reconnecting. When the tool's own arguments can stand in for the answer,
372
+ say so per call — `ctx.requestInput(spec, { fallbackHint: 'Or call again with noun supplied.' })` —
373
+ and the sentence is appended to that hint. A consent gate passes none: it has no such field.
374
+
371
375
  ### `ctx.content`
372
376
 
373
377
  Accumulates non-text content blocks — image/audio bytes, embedded resources, resource links — onto the response: `ctx.content.image(data, mimeType)`, `ctx.content.audio(data, mimeType)`, or `ctx.content(block)` for a raw `ContentBlock`. Blocks are prepended to `content[]` after `format()` runs and never enter `structuredContent`, so a handler can emit media for the calling model without the base64 duplicating into typed output. Always present (no-op when unused); callable from handler and service layer.
@@ -416,9 +420,9 @@ Available factories: `invalidParams`, `invalidRequest`, `notFound`, `forbidden`,
416
420
 
417
421
  For HTTP responses from upstream APIs, use `httpErrorFromResponse(response, { service, data })` from `/utils` — maps the full status table (401/403/408/422/429/5xx) and captures body + `Retry-After`.
418
422
 
419
- **Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: request signal already aborted (→ `RequestCancelled`, outranking the thrown value's own code, `McpError` included) → `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → JS constructor name (`TypeError` → `ValidationError`) → provider patterns (HTTP status codes, AWS errors, DB errors) → common message patterns → `AbortError` name (→ `Timeout`) → `InternalError` fallback.
423
+ **Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: request signal already aborted (→ `RequestCancelled`, outranking the thrown value's own code, `McpError` included) → `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → engine resource-limit `RangeError` by whole message — stack overflow, maximum string size (→ `InternalError`) → JS constructor name (`SyntaxError` → `ValidationError`; `TypeError` is excluded) → provider patterns (HTTP status codes, AWS errors, DB errors) → common message patterns → `AbortError` name (→ `Timeout`) → `InternalError` fallback. A result that breaks the definition's own `output` or `enrichment` schema fails as `InternalError` naming that contract, not `ValidationError`.
420
424
 
421
- **Error-path parity.** Tool errors: `content[]` carries `Error: <message>`, then `Recovery: <hint>` when the hint says something the message does not already contain, then a closing `(reason … · not retryable)` for whichever of `data.reason` / `data.retryable` is present; the numeric code and `data.issues` stay JSON-only. `structuredContent.error` carries `{ code, message, data? }`. No `_meta.error`. Resources re-throw via JSON-RPC error envelope. An argument rejection is one of them: `-32602` with `data.issues`, plus `data.reason: 'invalid_arguments'` and a hint synthesized from the issues and the root schema — never a tool-declared `reason`, since the handler never ran. `client_capability_missing` is the second framework-owned reason: a `ctx.requestInput` return a 2025-era connection cannot serve is refused before any wire traffic as `-32600` carrying that reason and a hint naming the capability.
425
+ **Error-path parity.** Tool errors: `content[]` carries `Error: <message>`, then `Recovery: <hint>` when the hint says something the message does not already contain, then a closing `(reason … · not retryable)` for whichever of `data.reason` / `data.retryable` is present; the numeric code and `data.issues` stay JSON-only. `structuredContent.error` carries `{ code, message, data? }`. No `_meta.error`. Resources re-throw via JSON-RPC error envelope. An argument rejection is one of them: `-32602` with `data.issues`, plus `data.reason: 'invalid_arguments'` and a hint synthesized from the issues and the root schema — never a tool-declared `reason`, since the handler never ran. When pre-validation rewrote or dropped a key the caller wrote, `data.input` (`{ aliased: [{ alias, target }], ignored }`) names it and the hint closes with `Validated … as ….` / `Dropped undeclared key ….`; an ignore-list drop is never reported. `client_capability_missing` is the second framework-owned reason: a `ctx.requestInput` return a 2025-era connection cannot serve is refused before any wire traffic as `-32600` carrying that reason and a hint naming the capability.
422
426
 
423
427
  **Lint rules** (all warnings, surfaced in `devcheck`): `prefer-mcp-error-in-handler`, `prefer-error-factory`, `preserve-cause-on-rethrow`, `no-stringify-upstream-error`, `error-contract-conformance`, `error-contract-prefer-fail`, `error-contract-unthrown` (a declared reason no literal `ctx.fail`/`ctx.recoveryFor` in the handler names, unless marked `thrownBy: 'service'`), `error-contract-recovery-unforwarded` (a `ctx.fail` site carrying neither `ctx.recoveryFor('<reason>')` nor its own `recovery` key, so the declared hint reaches neither client surface). See `api-linter` skill.
424
428
 
@@ -520,7 +524,7 @@ Detailed method signatures, options, and examples live in skill files. Read the
520
524
 
521
525
  Each `framework-skills/<name>/SKILL.md` carries `metadata.version` in frontmatter. The `maintenance` skill's Phase A uses this to sync consumer copies — replaces the **entire skill directory** as one unit. Without a version bump, Phase A skips the skill (content-hash backstop catches drift, but noisier).
522
526
 
523
- **Policy:** Bump `metadata.version` when changing any file under `framework-skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt. One bump per release cycle suffices. Enforced by `bun run devcheck` (`scripts/check-skill-versions.ts`): a SKILL.md body change vs `HEAD` without a `metadata.version` bump surfaces as a warning; whitespace-only edits never trigger it, and a genuine typo fix opts out via `devcheck.config.json` `skillVersions.ignore`.
527
+ **Policy:** Bump `metadata.version` when changing any file under `framework-skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt. **Exactly one step per release, however many edits land:** before bumping, compare the version against the last release tag (`git show $(git describe --tags --abbrev=0):framework-skills/<name>/SKILL.md`) and skip the bump when it has already moved. Enforced by `bun run devcheck` (`scripts/check-skill-versions.ts`), both directions as warnings: a SKILL.md body change vs `HEAD` without a `metadata.version` bump while the version still matches the last release tag, and a version more than one step (next minor, or next major at `.0`) past the last release tag. Whitespace-only edits never trigger the first, and a genuine typo fix opts out via `devcheck.config.json` `skillVersions.ignore`.
524
528
 
525
529
  Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable via the agent's skill registry at session start. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, and these are development-time skills, not skills for the agents that use a server. `skills/` stays free for that second kind.
526
530
 
@@ -540,10 +544,10 @@ Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable
540
544
  - **Auth:** via `auth: ['scope']` on definitions (not HOF wrapper)
541
545
  - **Missing input:** read `ctx.inputs` first, then `return ctx.requestInput(...)`
542
546
  - **Pagination:** large resource lists use `extractCursor`/`paginateArray`
543
- - **Registration:** definitions exported in `definitions/index.ts` barrel
547
+ - **Registration:** definitions collected in the `definitions/index.ts` barrel's array passed to `createApp()` — an `export` line alone registers nothing
544
548
  - **Tests:** `createMockContext()`, `.handler()` tested directly
545
549
  - **Gate:** `bun run devcheck` passes (includes MCP definition linting)
546
- - **Smoke-test:** `bun run rebuild && bun run start:stdio` (or `start:http`)
550
+ - **Smoke-test:** `bun run rebuild && bun run start:stdio < /dev/null` (or `start:http`); the `Core services constructed` log record lists every definition in its `tools` / `resources` / `prompts` fields
547
551
 
548
552
  ---
549
553
 
@@ -566,7 +570,7 @@ Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable
566
570
  | `bun run test:leak-gate` | The retention gate's own sentinel suite. Each case spawns a full Vitest run, so it is excluded from the `unit` project |
567
571
  | `bun run test:coverage` | Root projects with coverage thresholds enforced |
568
572
  | `bun run test:integration` | Real server subprocesses over stdio and HTTP |
569
- | `bun run test:worker` | The framework under real `workerd`, then a standalone Worker bundle through the Wrangler toolchain — real Node on both legs |
573
+ | `bun run test:worker` | The framework under real `workerd`, then a standalone Worker bundle through the Wrangler toolchain — real Node on both legs. The `workerd` leg enforces its own coverage thresholds over the Worker entry and Cloudflare storage providers, reported to `reports/coverage-worker/` |
570
574
  | `bun run test:package` | Rebuilds, packs the tarball, and consumes it as an external project would (exports, declarations, both runtimes) |
571
575
  | `bun run test:node` | Root projects + integration under real Node via `scripts/with-node.ts`, which bypasses Bun's `node` PATH shim |
572
576
  | `bun run test:order` | Root projects on real Node in shuffled file order under a pinned seed — catches inter-file state leakage |
package/README.md CHANGED
@@ -6,9 +6,9 @@
6
6
 
7
7
  <div align="center">
8
8
 
9
- [![Version](https://img.shields.io/badge/Version-0.13.7-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![MCP Spec](https://img.shields.io/badge/MCP%20Spec-2026--07--28-8A2BE2.svg?style=flat-square)](https://modelcontextprotocol.io/specification/2026-07-28)
9
+ [![Version](https://img.shields.io/badge/Version-0.13.9-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![MCP Spec](https://img.shields.io/badge/MCP%20Spec-2026--07--28-8A2BE2.svg?style=flat-square)](https://modelcontextprotocol.io/specification/2026-07-28)
10
10
 
11
- [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0%2B-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
+ [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.1.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0%2B-blueviolet.svg?style=flat-square)](https://bun.sh/)
12
12
 
13
13
  [Quick start](#quick-start) · [Capabilities](#what-comes-with-it) · [API reference](#api-overview) · [Examples](#examples)
14
14
 
@@ -239,6 +239,8 @@ Core config comes from environment variables, validated with Zod. Server-specifi
239
239
  | `STORAGE_PROVIDER_TYPE` | `in-memory`, `filesystem`, `supabase`, `cloudflare-d1`/`kv`/`r2` | `in-memory` |
240
240
  | `CANVAS_PROVIDER_TYPE` | `none` or `duckdb` (optional peer dependency `@duckdb/node-api`) | `none` |
241
241
  | `OTEL_ENABLED` | Enable OpenTelemetry | `false` |
242
+ | `LOG_TOOL_FAILURE_PAYLOADS` | Log each failed tool call's arguments and result, redacted by key name (a secret inside a free-form value is not caught) | `false` |
243
+ | `LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES` | Cap per logged payload, in UTF-8 bytes | `16384` |
242
244
  | `OPENROUTER_API_KEY` | API key for the optional OpenRouter LLM provider (`/services`) | — |
243
245
 
244
246
  See [CLAUDE.md/AGENTS.md](CLAUDE.md) for the full configuration reference.
@@ -0,0 +1,101 @@
1
+ ---
2
+ summary: "Request context and elicited answers no longer reach client error data, error counters and wire codes agree per failure, and OTLP log export and failed-call payload logging arrive as opt-ins."
3
+ breaking: true
4
+ security: true
5
+ agent-notes: |
6
+ Adoption steps for a consumer upgrading from 0.13.7.
7
+
8
+ 1. Error codes callers can see change in three places; update tests and
9
+ any client logic keyed on them. A result that breaks a tool's `output`,
10
+ its `enrichment` block, or a resource's `output` now fails as `-32603`
11
+ (was `-32007`) with no `data.issues`. An engine resource-limit
12
+ `RangeError` (stack overflow, maximum string size) is `-32603` (was
13
+ `-32007`). A `fetchWithTimeout` abort whose signal reason is a
14
+ `TimeoutError` is `-32004` `Timeout` (was `-32011`).
15
+ 2. `McpError.data` from `ErrorHandler.handleError` / `tryCatch` no longer
16
+ carries the `context` fields (`requestId`, `tenantId`, `operation`,
17
+ `extra`, …), and framework storage, parser, formatter, fetch, and
18
+ provider errors no longer carry a context either. Put anything the
19
+ caller should act on in the thrown `McpError`'s own `data`; code or
20
+ tests that read `requestId` or an `extra` field off error data read the
21
+ log record instead.
22
+ 3. `selectSections` now throws `InvalidParams` on a requested name that is
23
+ not a key of the document. Catch it only if the tool must degrade; the
24
+ default path returns the valid names to the caller.
25
+ 4. `InMemoryProvider.set` / `list` reject instead of throwing
26
+ synchronously. Tests written as `expect(() => provider.set(…)).toThrow()`
27
+ become `await expect(provider.set(…)).rejects…`.
28
+ 5. `prefer-mcp-error-in-handler` now fires under Bun and on a bare
29
+ `throw Error(…)`. Expect new warnings on the next `devcheck`; replace
30
+ each with an error factory or `ctx.fail`.
31
+ 6. A server with `manifest.json` adds `"!dist/*.mcpb"` to `package.json`
32
+ `files`. `lint:packaging` fails without it once the framework scripts
33
+ are resynced.
34
+ 7. Port the scaffold `Dockerfile` production-stage changes: copy
35
+ `bunfig.toml` with `package.json` and `bun.lock`, seed
36
+ `@socketsecurity/bun-security-scanner` from the build stage, replace the
37
+ OTEL `bun add` with the step that reads each package's range from the
38
+ installed framework's `peerDependencies` (the list now includes the
39
+ three log packages), and drop `ENV MCP_FORCE_CONSOLE_LOGGING`.
40
+ 8. Optional: `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` turns on OTLP log export
41
+ and needs `@opentelemetry/sdk-logs`, `@opentelemetry/exporter-logs-otlp-http`,
42
+ and `@opentelemetry/api-logs`. `LOG_TOOL_FAILURE_PAYLOADS` /
43
+ `LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES` add the failed-call payload record.
44
+ Document whichever the server exposes in `.env.example` and the README.
45
+ 9. Dashboards: `mcp.tool.calls` / `mcp.tool.errors` gain
46
+ `mcp.tool.outcome`, `mcp.errors.classified` gains `mcp.error.category`,
47
+ and `mcp.tool.rejections` is new. Existing `sum()` queries are
48
+ unchanged; group `mcp.errors.classified` by `mcp.error.category` rather
49
+ than decoding the code. New `data.reason` values are listed under
50
+ Changed.
51
+ ---
52
+
53
+ # 0.13.8 — 2026-09-26
54
+
55
+ ## Added
56
+
57
+ - **OTLP log export** ([#547](https://github.com/cyanheads/mcp-ts-core/issues/547)) — `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` attaches a `BatchLogRecordProcessor`, and the framework logger forwards each record that passes the level filter and rate limit, redacted, with its MCP level as the severity. The base `OTEL_EXPORTER_OTLP_ENDPOINT` never enables it; `interactions.log` is never exported.
58
+ - **Failed-call payload logging** ([#291](https://github.com/cyanheads/mcp-ts-core/issues/291)) — `LOG_TOOL_FAILURE_PAYLOADS=true` writes one `Tool failure payload: <tool>` record per failed call with `toolInput` and `toolResult` as redacted JSON strings, capped by `LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES` (default `16384`). Cancellations and `input_required` write nothing; redaction is by key name only. Backed by the new `sanitization.serializeForLogging(value, maxBytes)`.
59
+ - **`mcp.tool.rejections` counter and `mcp.tool.outcome` attribute** ([#546](https://github.com/cyanheads/mcp-ts-core/issues/546)) — a call refused by argument validation or the inline `auth` check counts once on `mcp.tool.rejections` (`mcp.tool.error_code`, `mcp.tool.error_category`); `mcp.tool.calls` and `mcp.tool.errors` carry `ok` / `error` / `cancelled`.
60
+ - **`mcp.error.category` on `mcp.errors.classified`** ([#481](https://github.com/cyanheads/mcp-ts-core/issues/481)) — the same `upstream` / `server` / `client` bucket as the per-surface counters, so the canvas tenant-cap refusal reads `-32003` + `server`.
61
+ - **`lint:packaging` check 13** ([#469](https://github.com/cyanheads/mcp-ts-core/issues/469)) — with `manifest.json` present, a `files` allowlist covering `dist/` must carry `"!dist/*.mcpb"`. The scaffold's `package.json` now does.
62
+ - **Skill-version overshoot check** — in the framework repo, `devcheck` warns when a skill's `metadata.version` is more than one step past its version at the last release tag, and a skill already bumped since that tag takes further body edits without another bump.
63
+
64
+ ## Changed
65
+
66
+ - **Output-contract failures are `InternalError`, and error counters follow the wire code** ([#480](https://github.com/cyanheads/mcp-ts-core/issues/480)) — a tool `output`, `enrichment`, or resource `output` violation returns `-32603` naming the definition and contract, with no `data`, in production and `runToolContract` alike. A non-`McpError` from a tool or prompt handler is bucketed by the code it classifies to, so `Error('Request timed out')` is `upstream` on every counter.
67
+ - **Engine resource-limit `RangeError`s are `InternalError`** ([#482](https://github.com/cyanheads/mcp-ts-core/issues/482)) — a whole message of `Maximum call stack size exceeded` (with or without the period), `Invalid string length`, or `Out of memory` maps to `-32603`; other `RangeError`s stay `ValidationError`.
68
+ - **`selectSections` rejects unknown names** ([#477](https://github.com/cyanheads/mcp-ts-core/issues/477)) — throws `InvalidParams` naming the unmatched and available keys, with both in `data` (`unmatched`, `available`). An absent `alwaysKeep` key is still ignored.
69
+ - **`InMemoryProvider.set` and `list` reject** ([#544](https://github.com/cyanheads/mcp-ts-core/issues/544)) — the encode, capacity, and cursor failures return a rejected promise instead of throwing synchronously.
70
+ - **`prefer-mcp-error-in-handler` reads Bun's handler source** ([#465](https://github.com/cyanheads/mcp-ts-core/issues/465)) — `throw Error(…)` counts as a plain-`Error` throw, so the rule now fires under Bun and on a bare `throw Error(…)`. `preserve-cause-on-rethrow` accepts `{ cause }` when the catch binding is `cause`.
71
+ - **Stable `data.reason` on framework validation errors** ([#552](https://github.com/cyanheads/mcp-ts-core/issues/552), [#504](https://github.com/cyanheads/mcp-ts-core/issues/504)) — with a `recovery.hint` where the caller can change the input: `sanitization` (`invalid_url`, `invalid_path`, `path_traversal`, `absolute_path_disallowed`, `invalid_json`, `json_too_large`, `invalid_number`, `unsupported_sanitize_context`), `fetchWithTimeout` (`invalid_url`, `private_address_blocked`, `too_many_redirects`), `IdGenerator` (`invalid_charset`, `unknown_entity_type`, `invalid_id_format`), `decodeCursor` / `paginateArray` (`invalid_cursor`), and the parsers (`parser_input_empty`, `csv_parse_failed`).
72
+ - **The startup line separates disabled tools** ([#514](https://github.com/cyanheads/mcp-ts-core/issues/514)) — `N tool(s) (+M disabled: …)`, with a `disabledTools` array beside `tools`; `manifest.definitionCounts` still counts every definition.
73
+ - **Docker production stage** ([#549](https://github.com/cyanheads/mcp-ts-core/issues/549)) — the OTEL step installs each package at its `peerDependencies` range (see Security), and the framework image installs them instead of skipping its own peers. `MCP_FORCE_CONSOLE_LOGGING`, which nothing read, is gone from both Dockerfiles.
74
+ - **`git-wrapup` orders `chore(deps)` first** ([#485](https://github.com/cyanheads/mcp-ts-core/issues/485)) — a dependency bump lands before the commits that use it and builds on its own, and every work commit must pass its tests at its own SHA, checked from a `git archive` snapshot. `maintenance` notes that a Bun pin moves `packageManager`, the Docker base tags, and the README badge together.
75
+ - Skill versions: `api-auth` 1.3 → 1.4, `api-canvas` 2.4 → 2.5, `api-config` 1.20 → 1.21, `api-context` 2.6 → 2.7, `api-errors` 1.16 → 1.17, `api-linter` 1.18 → 1.19, `api-telemetry` 1.13 → 1.14, `api-testing` 1.11 → 1.12, `api-utils` 2.12 → 2.13, `git-wrapup` 1.25 → 1.26, `maintenance` 2.9 → 2.10, `polish-docs-meta` 2.18 → 2.19, `release-and-publish` 2.20 → 2.21, `techniques` 0.3 → 0.4.
76
+
77
+ ## Fixed
78
+
79
+ - **A caller-side deadline is `Timeout`** ([#551](https://github.com/cyanheads/mcp-ts-core/issues/551)) — `fetchWithTimeout` reports an external-signal abort whose reason is a `TimeoutError` as `-32004` with `data.errorSource: 'FetchSignalTimeout'`; any other abort stays `RequestCancelled`. `defaultIsTransient` keeps the deadline out of `withRetry`, as it kept the `RequestCancelled` out: every retry would reuse the fired signal.
80
+ - **Canvas `missing_table` means a missing table** ([#484](https://github.com/cyanheads/mcp-ts-core/issues/484)) — an unknown function, table function, type, collation, or schema is `invalid_sql` with `data.binderMessage`; `tableName` carries the full name, spaces included, and a `FROM`-first query counts as a `SELECT`. A `DROP`, `DELETE`, `INSERT`, `UPDATE`, or `ALTER` naming a missing table is `non_select_statement` (`-32007`), not `missing_table`, in `query()` and `registerView()` alike.
81
+ - **`describe-on-fields` survives a self-referential schema** ([#491](https://github.com/cyanheads/mcp-ts-core/issues/491)) — the walk stops when a schema re-enters its own path, so the rest of the lint report survives.
82
+ - **`OUTLINE_VARIANT`'s documented shape lints clean** ([#271](https://github.com/cyanheads/mcp-ts-core/issues/271)) — `enrichment-prefer-block` skips a `notice` beside a `sections` array, and the JSDoc and `techniques` snippet describe `kind` and the section item.
83
+ - **`MarkdownBuilder.inlineCode` sizes its code span** ([#505](https://github.com/cyanheads/mcp-ts-core/issues/505)) — the delimiter outgrows any backtick run in the value, so it reads back as one span; `formatOutline` renders section names the same way.
84
+ - **`ctx.log` keeps its line on the wire** ([#502](https://github.com/cyanheads/mcp-ts-core/issues/502)) — `message` and `error` are written after the call's data, so a `message` key no longer replaces the log line in `notifications/message`.
85
+ - **Canonical context fields win in log records** ([#550](https://github.com/cyanheads/mcp-ts-core/issues/550)) — an `extra` key named `requestId`, `traceId`, `spanId`, `sessionId`, `tenantId`, `operation`, or `timestamp` no longer replaces the context's own value.
86
+ - **An unwritable logs directory no longer kills the process** ([#497](https://github.com/cyanheads/mcp-ts-core/issues/497)) — each file sink is probed at startup; one that cannot be opened is dropped with a single `warning` naming it, and stderr keeps logging.
87
+ - **`MCP_LOG_LEVEL=debug` reaches stderr** ([#511](https://github.com/cyanheads/mcp-ts-core/issues/511)) — the stderr, stdout, pretty, and `combined.log` targets take `level: 'debug'`, so the logger's level is the only gate, after `setLevel()` too; `error.log` keeps `error`.
88
+ - **One OTel diag logger, on stderr** ([#545](https://github.com/cyanheads/mcp-ts-core/issues/545)) — `NodeSDK` no longer registers a second one from `OTEL_LOG_LEVEL`, so the overwrite notices and the `Unknown log level` warning on aliases are gone and stdio's stdout carries only JSON-RPC. The metrics reader's export timeout matches its 15 s interval, ending the clamp notice.
89
+ - **`createFetchMock` static routes settle on Node** ([#503](https://github.com/cyanheads/mcp-ts-core/issues/503)) — the body is read once and served as a fresh `Response` per call instead of a `clone()` tee, so a cancelled body resolves.
90
+ - **`runToolContract` settles cancellation like production** ([#513](https://github.com/cyanheads/mcp-ts-core/issues/513)) — once `context.signal` has fired, anything the handler or success pipeline throws becomes `RequestCancelled`; argument parsing stays outside the settle.
91
+ - **The pre-commit hook no longer rewrites files** ([#543](https://github.com/cyanheads/mcp-ts-core/issues/543)) — `.husky/pre-commit` passes `--no-fix` instead of the removed `--no-format`, so Biome checks without writing.
92
+
93
+ ## Security
94
+
95
+ - **Request context no longer reaches client error data** ([#548](https://github.com/cyanheads/mcp-ts-core/issues/548)) — `ErrorHandler.handleError` and every framework `McpError` site (storage validation and providers, parsers, formatters, `fetchWithTimeout`, speech and LLM providers, auth, sessions, canvas) pass explicit fields only, so `ctx.inputs.responses` and request metadata stay in the log. Formatter failures no longer put a stack in `data`, and the no-auth-context `-32006` carries no scope names.
96
+ - **The Docker OTEL install passes the supply-chain guards** ([#475](https://github.com/cyanheads/mcp-ts-core/issues/475)) — both Dockerfiles copy `bunfig.toml` into the production stage and seed the Socket scanner from the build stage, so the OTEL packages resolve through the release-age gate and the scanner, inside the framework's tested peer ranges.
97
+
98
+ ## Dependencies
99
+
100
+ - New optional peers and devDependencies for OTLP log export: `@opentelemetry/sdk-logs` ^0.222.0, `@opentelemetry/exporter-logs-otlp-http` ^0.222.0, `@opentelemetry/api-logs` ^0.222.0.
101
+ - Dev: `openai` ^7.21.0 → ^7.23.0, `@supabase/supabase-js` ^2.117.0 → ^2.117.1.
@@ -0,0 +1,113 @@
1
+ ---
2
+ summary: "Canvas scratch files move into a private directory, a JSON-stringified object or an integer sent for a string is repaired before the schema rejects it, and argument rejections name the field paths and key rewrites behind them."
3
+ breaking: false
4
+ security: true
5
+ agent-notes: |
6
+ Adoption steps for a consumer upgrading from 0.13.8.
7
+
8
+ 1. `lint:mcp` (and so `devcheck`) now fails on three conditions it used to
9
+ pass: a definition file that fails to import (`definition-import-failed`),
10
+ a `server.json` that exists but does not parse (`server-json-parse`), and
11
+ an `inputAliases` entry whose target is a `headerParam` field
12
+ (`input-alias-conflict`). The first two arrive with the maintenance
13
+ resync of `scripts/lint-mcp.ts`, the third with the package itself. Run
14
+ `bun run lint:mcp` once both are in: a server that passes needs no
15
+ change. Otherwise, for an import failure install the missing optional
16
+ peer where lint runs (a devDependency is enough) or make the import lazy
17
+ (`await import()` inside the handler or service, with module-load work
18
+ moved into `setup()` or a service's init); fix the `server.json` syntax;
19
+ and remove the dead alias, or drop the header designation.
20
+ 2. A `runToolContract` / `toolContractSuite` case that expects
21
+ `InvalidParams` for an integer sent to a string field, or for a
22
+ JSON-stringified object sent to an object field, now succeeds: the
23
+ framework repairs both before the handler runs. Change such a case to
24
+ send a boolean so it keeps its wrong-type intent.
25
+ 3. `CANVAS_TEMP_PATH` now names a parent directory. The DuckDB provider
26
+ creates a private `mcp-canvas-XXXXXX` directory inside it (or inside the
27
+ OS temp directory when unset), so a value pointing at a dedicated mount
28
+ keeps working. A leftover `<tmpdir>/mcp-canvas` from earlier versions is
29
+ never used again and can be deleted once no older server shares the
30
+ host. A process that exits abnormally leaves its `mcp-canvas-XXXXXX`
31
+ behind, still private; nothing sweeps it on the next start.
32
+ 4. A tool whose handler has a non-elicitation path, where its own
33
+ arguments can stand in for what `ctx.requestInput` asks, passes one:
34
+ `ctx.requestInput(spec, { fallbackHint: 'Or call again with <field> supplied.' })`.
35
+ Without it, a 2025-era client lacking the capability is told only to
36
+ reconnect. A consent gate passes none.
37
+ 5. MCP SDK 2.1.0 changes what callers see on the transports. A 2026-07-28
38
+ POST without `MCP-Protocol-Version` is refused `400` / `-32020`, as is a
39
+ 2026-07-28 `tasks/get`, `tasks/update`, or `tasks/cancel` without an
40
+ `Mcp-Name` matching `params.taskId`. A JSON-RPC batch of more than 100
41
+ messages is refused whole, `400` / `-32600`. `notifications/cancelled`
42
+ with `requestId: 0` now aborts the handler. On stdio, stdin EOF aborts
43
+ requests still in flight, and they are never answered.
44
+ `MCP_HTTP_MAX_BODY_BYTES` remains the only request-body limit. Update
45
+ any client code or test that relied on the old answers.
46
+ 6. The pre-validation repair's debug log field `coercion` is now
47
+ `coercions`, an array of repair kinds (`stringified_array`,
48
+ `stringified_object`, `integer_as_string`). Update any log query that
49
+ reads it.
50
+ 7. A server with a `.mcpbignore` adds `/docs/idea.md` and `/logs/` to it,
51
+ as the scaffold now does.
52
+ 8. `lint:packaging` (and so `devcheck`) now fails two conditions it used to
53
+ pass: a `manifest.json` whose `version` differs from `package.json`'s —
54
+ set it to the package version — and a Dockerfile stage that runs
55
+ `bun run build` without `FROM --platform=$BUILDPLATFORM` — start that
56
+ stage with `FROM --platform=$BUILDPLATFORM` and copy `dist/` into a
57
+ separate runtime stage, as the scaffold's `Dockerfile` does. Both arrive
58
+ with the maintenance resync of `scripts/lint-packaging.ts`. Run
59
+ `bun run lint:packaging` once it is in: a server that passes needs no
60
+ change.
61
+ 9. `bun run release:github`, once `scripts/release-github.ts` is resynced,
62
+ refuses a tag that breaks the `release-and-publish` step 4 format before
63
+ any `gh` call. Run `bun run release:github -- --check` after
64
+ `git tag -a` and before pushing the tag, and recreate a tag it rejects.
65
+ ---
66
+
67
+ # 0.13.9 — 2026-09-26
68
+
69
+ ## Added
70
+
71
+ - **Stringified objects and integers sent for strings are repaired** ([#479](https://github.com/cyanheads/mcp-ts-core/issues/479), [#487](https://github.com/cyanheads/mcp-ts-core/issues/487)) — after a failed parse, a JSON-stringified object is decoded and a safe integer sent for a string becomes its digits (`8654467` → `"8654467"`), each kept only if the re-parse passes. `mcp.input.coercion` gains `stringified_object` and `integer_as_string`.
72
+ - **Argument rejections name pre-validation's rewrites** ([#468](https://github.com/cyanheads/mcp-ts-core/issues/468)) — when an alias rewrite or an underscore-rule drop touched a rejected call's keys, `data.input` names them (`{ aliased: [{ alias, target }], ignored }`) and the hint closes with `Validated q as query.` or `Dropped undeclared key _search.` (ignore-list drops never appear).
73
+ - **`pacer.cooldown`** ([#467](https://github.com/cyanheads/mcp-ts-core/issues/467)) — samples the shared gate as `PacerCooldownState` `{ remainingMs, consecutive }`, exported from `/utils`, so a task's rejection handler can report the wait on its own error. Both stay 0 without a `cooldown` option.
74
+ - **`lint:packaging` checks the manifest version and the Dockerfile build platform** — a `manifest.json` `version` other than `package.json`'s fails, as does a Dockerfile stage that runs `bun run build` without `FROM --platform=$BUILDPLATFORM`. `devcheck` runs the step when a `Dockerfile` is its only input.
75
+ - **`release:github` validates the tag before publishing** — a lightweight tag, a subject over 72 characters or carrying the version or a `;`, a section header, a leaked signature block, or a final line other than the changelog link is refused before any `gh` call; `-- --check` runs only that validation.
76
+
77
+ ## Changed
78
+
79
+ - **Capability refusals end at reconnecting** ([#495](https://github.com/cyanheads/mcp-ts-core/issues/495)) — the `client_capability_missing` hint no longer offers to take the answer as an argument, which a consent gate cannot. A handler whose arguments can stand in passes `ctx.requestInput(spec, { fallbackHint })`, a sentence appended to the hint.
80
+ - **`CanvasIdSchema` says what a canvas ID is** ([#483](https://github.com/cyanheads/mcp-ts-core/issues/483)) — a rejected `canvas_id` reads "Expected a canvas ID exactly as an earlier response on this server returned it…" in the message and hint instead of the bare regex. The advertised JSON Schema is unchanged.
81
+ - **MCP SDK 2.1.0 transport behavior** — a 2026-07-28 POST without `MCP-Protocol-Version` is refused `400` / `-32020`, and a JSON-RPC batch over 100 messages `400` / `-32600`. `notifications/cancelled` with `requestId: 0` now cancels, and stdin EOF on stdio aborts in-flight requests unanswered.
82
+ - **Release workflow skills** — `git-wrapup`'s gate adds `rebuild`; `release-pr-review` waits on code scanning in bounded calls, reads the PR's merge-ref alerts, and reports `finished` or `halted`; the greenfield workflow disables CodeQL while the repo is private, then reopens the release PR once it is public so the first scan runs; `security-pass` flags catching `client_capability_missing` to run a side effect. `release-and-publish` runs `release:github -- --check` before the push, and the orchestration workflows defer the tag format to it and drop the tag-moving protocol.
83
+ - **Skill corrections** — issue reads fetch the body and the thread separately, since `gh issue view --comments` prints no body without a TTY; smoke tests run `start:stdio < /dev/null` and check the `Core services constructed` record's `tools` / `resources` / `prompts` fields; the `add-resource` and `add-prompt` barrel snippets add to the `createApp()` array; `security-pass` greps use `grep -E`.
84
+ - **Scaffold `.mcpbignore`** — excludes `docs/idea.md` and `logs/` from the bundle.
85
+ - Skill versions: `add-app-tool` 1.6 → 1.7, `add-export` 1.1 → 1.2, `add-prompt` 1.4 → 1.5, `add-resource` 1.7 → 1.8, `add-tool` 2.30 → 2.31, `api-auth` 1.4 → 1.5, `api-canvas` 2.5 → 2.6, `api-config` 1.21 → 1.22, `api-context` 2.7 → 2.8, `api-errors` 1.17 → 1.18, `api-linter` 1.19 → 1.20, `api-mirror` 1.2 → 1.3, `api-telemetry` 1.14 → 1.15, `api-utils` 2.13 → 2.14, `design-mcp-server` 2.29 → 2.30, `field-test` 2.16 → 2.17, `git-wrapup` 1.26 → 1.27, `orchestrations` 1.11 → 1.12 (workflows `field-test-fix` 1.1 → 1.2, `fix-wrapup-release` 1.1 → 1.2, `greenfield-build` 1.2 → 1.3, `maintenance-release` 1.2 → 1.3), `polish-docs-meta` 2.19 → 2.20, `release-and-publish` 2.21 → 2.22, `release-pr-review` 1.5 → 1.6, `report-issue-framework` 1.13 → 1.14, `report-issue-local` 1.11 → 1.12, `security-pass` 1.10 → 1.11.
86
+
87
+ ## Fixed
88
+
89
+ - **Underscore-prefixed keys reach their alias** ([#563](https://github.com/cyanheads/mcp-ts-core/issues/563)) — when a call fails after the drop stage discarded a key, pre-validation retries alias-first and keeps the retry only if it validates, so a declared `_q` alias or an undeclared `_query` reaches `query`. A call that fails both ways is rejected as the retry saw it: the value's own failure, closed by `Validated _q as query.`
90
+ - **One-or-many unions render like the field they wrap** ([#492](https://github.com/cyanheads/mcp-ts-core/issues/492)) — a bad element in `z.union([z.array(Item), Item])` reports `items.1.name: …` with the typed hint `Send items.1.name as a string, not a boolean.`, as a plain array field does.
91
+ - **Argument hints keep field paths** ([#493](https://github.com/cyanheads/mcp-ts-core/issues/493)) — a constraint or refinement restated in the hint carries its path (`start: Must be …, end: Must be …`) and repeats collapse, so a consumer test pinning a path-less hint moves.
92
+ - **A fractional number on an integer field names the fix** ([#499](https://github.com/cyanheads/mcp-ts-core/issues/499)) — `Send rows as an integer, not a fractional number.` instead of `as an int, not a number`.
93
+ - **Nested unknown keys are hinted from their own object** ([#566](https://github.com/cyanheads/mcp-ts-core/issues/566)) — `Unknown key opts.b. opts accepts: a.` instead of the root's key list.
94
+ - **The pacer's cooldown streak decays** ([#471](https://github.com/cyanheads/mcp-ts-core/issues/471)) — a rate limit arriving once the gate has stood open for `cooldown.maxMs` restarts the doubling at `baseMs`.
95
+ - **Pacer sheds report their kind and a usable `retryAfter`** ([#472](https://github.com/cyanheads/mcp-ts-core/issues/472)) — sheds carry `data.shedKind` (`queue_full` / `wait_projected` / `wait_elapsed`, type `PacerShedKind`), and `retryAfter` counts every remaining waiter and, under a saturated `maxConcurrent`, is at least the longest queued wait and 1. A due waiter starts before a new arrival meets `maxQueueDepth`.
96
+ - **A nested pacer's shed leaves the outer gate open** ([#559](https://github.com/cyanheads/mcp-ts-core/issues/559)) — a task error with `data.reason: 'pacer_shed'` neither closes the cooldown gate nor touches the streak.
97
+ - **`maxQueueDepth` bounds waiters, not arrivals** ([#560](https://github.com/cyanheads/mcp-ts-core/issues/560)) — an arrival whose slot is open starts at once, so `maxQueueDepth: 0` means "run when a slot is free, never wait".
98
+ - **A `baseMs: 0` cooldown keeps pacing** ([#564](https://github.com/cyanheads/mcp-ts-core/issues/564)) — the doubling exponent is capped, so a streak past 1,024 rate limits no longer computes `NaN` and switches off every window, gap, and gate.
99
+ - **Concurrent canvas spills no longer corrupt each other** ([#561](https://github.com/cyanheads/mcp-ts-core/issues/561)) — each canvas spills into its own `temp_directory`, removed when the canvas is dropped or evicted, once the calls still running on it settle.
100
+ - **DuckDB errors classify by DuckDB's error class and redact host paths** ([#565](https://github.com/cyanheads/mcp-ts-core/issues/565)) — `sql_read_only` needs a `Permission Error` or a read-only-mode message and `sql_parse_error` a `Parser Error`, so an `IO Error` such as an unwritable export directory is `DatabaseError` (`-32010`) without a reason. The export root and scratch paths read `[path]` in engine error messages.
101
+ - **`lint:mcp` fails on definitions it cannot load** ([#516](https://github.com/cyanheads/mcp-ts-core/issues/516)) — a definition file whose `import()` rejects is a `definition-import-failed` error and an unparseable `server.json` a `server-json-parse` error, both exiting 1, while the remaining files are still linted.
102
+ - **`input-alias-conflict` covers `headerParam` targets** ([#569](https://github.com/cyanheads/mcp-ts-core/issues/569)) — an alias onto a header-mirrored field, which the rewrite never targets, now fails lint.
103
+ - **The CORS preflight allows the 2026-07-28 request headers** ([#571](https://github.com/cyanheads/mcp-ts-core/issues/571)) — an origin the Origin guard admits may also send `Mcp-Method`, `Mcp-Name`, `Last-Event-ID`, and each registered tool's `Mcp-Param-<Name>` (the manifest's new `headerParams`); other origins keep the 2025-era set. Every CORS response carries `Vary: Origin`.
104
+ - **Mirror examples pass `table`** — the `api-mirror` skill and the `defineMirror` JSDoc give `sqliteMirrorStore` its required `table` option.
105
+
106
+ ## Security
107
+
108
+ - **Canvas scratch files live in a private directory** ([#554](https://github.com/cyanheads/mcp-ts-core/issues/554)) — the DuckDB provider no longer uses the fixed `<tmpdir>/mcp-canvas`, which other local users could read or pre-create. It creates a private `mkdtemp` directory (`0700` on POSIX) inside `CANVAS_TEMP_PATH`, now a parent directory, and removes it at shutdown; one it cannot create fails canvas creation as `ConfigurationError`.
109
+
110
+ ## Dependencies
111
+
112
+ - `@modelcontextprotocol/server` ^2.0.0 → ^2.1.0 (transport behavior under Changed).
113
+ - Dev: `@modelcontextprotocol/client` ^2.0.0 → ^2.1.0.
@@ -26,6 +26,8 @@ declare const ConfigSchema: z.ZodObject<{
26
26
  }>, unknown>>;
27
27
  logsPath: z.ZodOptional<z.ZodString>;
28
28
  logLlmInteractions: z.ZodPreprocess<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>, unknown>;
29
+ logToolFailurePayloads: z.ZodPreprocess<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>, unknown>;
30
+ logToolFailurePayloadMaxBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
29
31
  logRateLimitThreshold: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
30
32
  logRateLimitWindowMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
31
33
  environment: z.ZodDefault<z.ZodPreprocess<z.ZodEnum<{
@@ -121,6 +123,7 @@ declare const ConfigSchema: z.ZodObject<{
121
123
  serviceVersion: z.ZodString;
122
124
  tracesEndpoint: z.ZodOptional<z.ZodURL>;
123
125
  metricsEndpoint: z.ZodOptional<z.ZodURL>;
126
+ logsEndpoint: z.ZodOptional<z.ZodURL>;
124
127
  samplingRatio: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
125
128
  logLevel: z.ZodDefault<z.ZodPreprocess<z.ZodEnum<{
126
129
  ALL: "ALL";
@@ -171,6 +174,8 @@ declare const parseConfig: (envOverrides?: Record<string, string | undefined>) =
171
174
  logLevel: "alert" | "crit" | "debug" | "emerg" | "error" | "info" | "notice" | "warning";
172
175
  logsPath?: string | undefined;
173
176
  logLlmInteractions: boolean;
177
+ logToolFailurePayloads: boolean;
178
+ logToolFailurePayloadMaxBytes: number;
174
179
  logRateLimitThreshold: number;
175
180
  logRateLimitWindowMs: number;
176
181
  environment: "development" | "production" | "testing";
@@ -241,6 +246,7 @@ declare const parseConfig: (envOverrides?: Record<string, string | undefined>) =
241
246
  serviceVersion: string;
242
247
  tracesEndpoint?: string | undefined;
243
248
  metricsEndpoint?: string | undefined;
249
+ logsEndpoint?: string | undefined;
244
250
  samplingRatio: number;
245
251
  logLevel: "ALL" | "DEBUG" | "ERROR" | "INFO" | "NONE" | "VERBOSE" | "WARN";
246
252
  };
@@ -296,6 +302,8 @@ declare const config: {
296
302
  logLevel: "alert" | "crit" | "debug" | "emerg" | "error" | "info" | "notice" | "warning";
297
303
  logsPath?: string | undefined;
298
304
  logLlmInteractions: boolean;
305
+ logToolFailurePayloads: boolean;
306
+ logToolFailurePayloadMaxBytes: number;
299
307
  logRateLimitThreshold: number;
300
308
  logRateLimitWindowMs: number;
301
309
  environment: "development" | "production" | "testing";
@@ -366,6 +374,7 @@ declare const config: {
366
374
  serviceVersion: string;
367
375
  tracesEndpoint?: string | undefined;
368
376
  metricsEndpoint?: string | undefined;
377
+ logsEndpoint?: string | undefined;
369
378
  samplingRatio: number;
370
379
  logLevel: "ALL" | "DEBUG" | "ERROR" | "INFO" | "NONE" | "VERBOSE" | "WARN";
371
380
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,wEAAwE;AACxE,eAAO,MAAM,cAAc,2BAA2B,CAAC;AACvD,eAAO,MAAM,iBAAiB,QAAkC,CAAC;AA6DjE,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsWd,CAAC;AAGL,QAAA,MAAM,WAAW,kBAAmB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2MrE,CAAC;AAIF;;;;;;;;GAQG;AACH,QAAA,MAAM,WAAW,kBAAmB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAG,IAExE,CAAC;AAEF;;;;;;GAMG;AACH,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuBV,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAErD;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,wEAAwE;AACxE,eAAO,MAAM,cAAc,2BAA2B,CAAC;AACvD,eAAO,MAAM,iBAAiB,QAAkC,CAAC;AA6DjE,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiYd,CAAC;AAGL,QAAA,MAAM,WAAW,kBAAmB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8MrE,CAAC;AAIF;;;;;;;;GAQG;AACH,QAAA,MAAM,WAAW,kBAAmB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAG,IAExE,CAAC;AAEF;;;;;;GAMG;AACH,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuBV,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAErD;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC"}