@cyanheads/mcp-ts-core 0.11.4 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (366) hide show
  1. package/AGENTS.md +28 -24
  2. package/CLAUDE.md +28 -24
  3. package/README.md +10 -10
  4. package/biome.json +1 -1
  5. package/changelog/0.11.x/0.11.5.md +28 -0
  6. package/changelog/0.12.x/0.12.0.md +96 -0
  7. package/changelog/template.md +55 -16
  8. package/dist/cli/init.js +1 -1
  9. package/dist/cli/init.js.map +1 -1
  10. package/dist/config/index.d.ts +0 -18
  11. package/dist/config/index.d.ts.map +1 -1
  12. package/dist/config/index.js +0 -25
  13. package/dist/config/index.js.map +1 -1
  14. package/dist/core/app.d.ts +5 -7
  15. package/dist/core/app.d.ts.map +1 -1
  16. package/dist/core/app.js +18 -27
  17. package/dist/core/app.js.map +1 -1
  18. package/dist/core/context.d.ts +90 -57
  19. package/dist/core/context.d.ts.map +1 -1
  20. package/dist/core/context.js +32 -33
  21. package/dist/core/context.js.map +1 -1
  22. package/dist/core/gcPressure.d.ts.map +1 -1
  23. package/dist/core/gcPressure.js +3 -4
  24. package/dist/core/gcPressure.js.map +1 -1
  25. package/dist/core/index.d.ts +6 -7
  26. package/dist/core/index.d.ts.map +1 -1
  27. package/dist/core/index.js +10 -2
  28. package/dist/core/index.js.map +1 -1
  29. package/dist/core/serverManifest.d.ts +1 -3
  30. package/dist/core/serverManifest.d.ts.map +1 -1
  31. package/dist/core/serverManifest.js +9 -4
  32. package/dist/core/serverManifest.js.map +1 -1
  33. package/dist/core/worker.d.ts.map +1 -1
  34. package/dist/core/worker.js +32 -35
  35. package/dist/core/worker.js.map +1 -1
  36. package/dist/logs/combined.log +10 -0
  37. package/dist/logs/error.log +6 -0
  38. package/dist/logs/interactions.log +0 -0
  39. package/dist/mcp-server/inputRequired.d.ts +52 -0
  40. package/dist/mcp-server/inputRequired.d.ts.map +1 -0
  41. package/dist/mcp-server/inputRequired.js +76 -0
  42. package/dist/mcp-server/inputRequired.js.map +1 -0
  43. package/dist/mcp-server/notifications.d.ts +33 -31
  44. package/dist/mcp-server/notifications.d.ts.map +1 -1
  45. package/dist/mcp-server/notifications.js +34 -26
  46. package/dist/mcp-server/notifications.js.map +1 -1
  47. package/dist/mcp-server/prompts/prompt-registration.d.ts +2 -2
  48. package/dist/mcp-server/prompts/prompt-registration.d.ts.map +1 -1
  49. package/dist/mcp-server/prompts/prompt-registration.js +15 -5
  50. package/dist/mcp-server/prompts/prompt-registration.js.map +1 -1
  51. package/dist/mcp-server/prompts/utils/promptDefinition.d.ts +1 -1
  52. package/dist/mcp-server/prompts/utils/promptDefinition.d.ts.map +1 -1
  53. package/dist/mcp-server/resources/resource-registration.d.ts +3 -3
  54. package/dist/mcp-server/resources/resource-registration.d.ts.map +1 -1
  55. package/dist/mcp-server/resources/resource-registration.js +8 -15
  56. package/dist/mcp-server/resources/resource-registration.js.map +1 -1
  57. package/dist/mcp-server/resources/resourceSubscriptions.d.ts +45 -0
  58. package/dist/mcp-server/resources/resourceSubscriptions.d.ts.map +1 -0
  59. package/dist/mcp-server/resources/resourceSubscriptions.js +51 -0
  60. package/dist/mcp-server/resources/resourceSubscriptions.js.map +1 -0
  61. package/dist/mcp-server/resources/utils/resourceDefinition.d.ts +3 -5
  62. package/dist/mcp-server/resources/utils/resourceDefinition.d.ts.map +1 -1
  63. package/dist/mcp-server/resources/utils/resourceDefinition.js +1 -1
  64. package/dist/mcp-server/resources/utils/resourceDefinition.js.map +1 -1
  65. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts +8 -15
  66. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
  67. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +21 -22
  68. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
  69. package/dist/mcp-server/server.d.ts +12 -21
  70. package/dist/mcp-server/server.d.ts.map +1 -1
  71. package/dist/mcp-server/server.js +38 -28
  72. package/dist/mcp-server/server.js.map +1 -1
  73. package/dist/mcp-server/tools/tool-registration.d.ts +11 -33
  74. package/dist/mcp-server/tools/tool-registration.d.ts.map +1 -1
  75. package/dist/mcp-server/tools/tool-registration.js +17 -243
  76. package/dist/mcp-server/tools/tool-registration.js.map +1 -1
  77. package/dist/mcp-server/tools/utils/toolDefinition.d.ts +1 -3
  78. package/dist/mcp-server/tools/utils/toolDefinition.d.ts.map +1 -1
  79. package/dist/mcp-server/tools/utils/toolDefinition.js +48 -1
  80. package/dist/mcp-server/tools/utils/toolDefinition.js.map +1 -1
  81. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +46 -20
  82. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
  83. package/dist/mcp-server/tools/utils/toolHandlerFactory.js +131 -25
  84. package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
  85. package/dist/mcp-server/transports/ITransport.d.ts +2 -2
  86. package/dist/mcp-server/transports/ITransport.d.ts.map +1 -1
  87. package/dist/mcp-server/transports/auth/authFactory.js +1 -1
  88. package/dist/mcp-server/transports/auth/authFactory.js.map +1 -1
  89. package/dist/mcp-server/transports/auth/lib/authTypes.d.ts +1 -1
  90. package/dist/mcp-server/transports/auth/lib/authTypes.d.ts.map +1 -1
  91. package/dist/mcp-server/transports/auth/lib/authUtils.d.ts.map +1 -1
  92. package/dist/mcp-server/transports/auth/lib/authUtils.js +5 -13
  93. package/dist/mcp-server/transports/auth/lib/authUtils.js.map +1 -1
  94. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js +5 -11
  95. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js.map +1 -1
  96. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.d.ts.map +1 -1
  97. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js +9 -22
  98. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js.map +1 -1
  99. package/dist/mcp-server/transports/heartbeat.d.ts.map +1 -1
  100. package/dist/mcp-server/transports/heartbeat.js +4 -8
  101. package/dist/mcp-server/transports/heartbeat.js.map +1 -1
  102. package/dist/mcp-server/transports/http/httpErrorHandler.d.ts.map +1 -1
  103. package/dist/mcp-server/transports/http/httpErrorHandler.js +6 -24
  104. package/dist/mcp-server/transports/http/httpErrorHandler.js.map +1 -1
  105. package/dist/mcp-server/transports/http/httpServer.d.ts +3 -3
  106. package/dist/mcp-server/transports/http/httpServer.d.ts.map +1 -1
  107. package/dist/mcp-server/transports/http/httpServer.js +15 -32
  108. package/dist/mcp-server/transports/http/httpServer.js.map +1 -1
  109. package/dist/mcp-server/transports/http/httpTransport.d.ts +25 -8
  110. package/dist/mcp-server/transports/http/httpTransport.d.ts.map +1 -1
  111. package/dist/mcp-server/transports/http/httpTransport.js +156 -310
  112. package/dist/mcp-server/transports/http/httpTransport.js.map +1 -1
  113. package/dist/mcp-server/transports/http/landing-page/assets/styles.d.ts.map +1 -1
  114. package/dist/mcp-server/transports/http/landing-page/assets/styles.js +0 -1
  115. package/dist/mcp-server/transports/http/landing-page/assets/styles.js.map +1 -1
  116. package/dist/mcp-server/transports/http/landing-page/handler.d.ts.map +1 -1
  117. package/dist/mcp-server/transports/http/landing-page/handler.js +4 -8
  118. package/dist/mcp-server/transports/http/landing-page/handler.js.map +1 -1
  119. package/dist/mcp-server/transports/http/landing-page/sections/tools.js +0 -2
  120. package/dist/mcp-server/transports/http/landing-page/sections/tools.js.map +1 -1
  121. package/dist/mcp-server/transports/http/protectedResourceMetadata.d.ts.map +1 -1
  122. package/dist/mcp-server/transports/http/protectedResourceMetadata.js +2 -6
  123. package/dist/mcp-server/transports/http/protectedResourceMetadata.js.map +1 -1
  124. package/dist/mcp-server/transports/http/robotsTxt.js +2 -2
  125. package/dist/mcp-server/transports/http/robotsTxt.js.map +1 -1
  126. package/dist/mcp-server/transports/http/serverCard.d.ts.map +1 -1
  127. package/dist/mcp-server/transports/http/serverCard.js +2 -6
  128. package/dist/mcp-server/transports/http/serverCard.js.map +1 -1
  129. package/dist/mcp-server/transports/http/sessionStore.d.ts +48 -45
  130. package/dist/mcp-server/transports/http/sessionStore.d.ts.map +1 -1
  131. package/dist/mcp-server/transports/http/sessionStore.js +141 -185
  132. package/dist/mcp-server/transports/http/sessionStore.js.map +1 -1
  133. package/dist/mcp-server/transports/manager.d.ts +12 -4
  134. package/dist/mcp-server/transports/manager.d.ts.map +1 -1
  135. package/dist/mcp-server/transports/manager.js +35 -33
  136. package/dist/mcp-server/transports/manager.js.map +1 -1
  137. package/dist/mcp-server/transports/stdio/stdioTransport.d.ts +24 -31
  138. package/dist/mcp-server/transports/stdio/stdioTransport.d.ts.map +1 -1
  139. package/dist/mcp-server/transports/stdio/stdioTransport.js +23 -38
  140. package/dist/mcp-server/transports/stdio/stdioTransport.js.map +1 -1
  141. package/dist/mcp-server/types.d.ts +25 -0
  142. package/dist/mcp-server/types.d.ts.map +1 -0
  143. package/dist/mcp-server/types.js +10 -0
  144. package/dist/mcp-server/types.js.map +1 -0
  145. package/dist/services/canvas/core/CanvasInstance.d.ts +2 -2
  146. package/dist/services/canvas/core/CanvasInstance.d.ts.map +1 -1
  147. package/dist/services/canvas/core/CanvasInstance.js.map +1 -1
  148. package/dist/services/canvas/core/CanvasRegistry.d.ts +4 -4
  149. package/dist/services/canvas/core/CanvasRegistry.d.ts.map +1 -1
  150. package/dist/services/canvas/core/CanvasRegistry.js +8 -18
  151. package/dist/services/canvas/core/CanvasRegistry.js.map +1 -1
  152. package/dist/services/canvas/core/DataCanvas.d.ts +5 -5
  153. package/dist/services/canvas/core/DataCanvas.d.ts.map +1 -1
  154. package/dist/services/canvas/core/DataCanvas.js +2 -3
  155. package/dist/services/canvas/core/DataCanvas.js.map +1 -1
  156. package/dist/services/canvas/core/IDataCanvasProvider.d.ts +11 -11
  157. package/dist/services/canvas/core/IDataCanvasProvider.d.ts.map +1 -1
  158. package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts +11 -11
  159. package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
  160. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +4 -10
  161. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
  162. package/dist/services/llm/providers/openrouter.provider.d.ts.map +1 -1
  163. package/dist/services/llm/providers/openrouter.provider.js +2 -5
  164. package/dist/services/llm/providers/openrouter.provider.js.map +1 -1
  165. package/dist/services/mirror/core/defineMirror.js +1 -1
  166. package/dist/services/mirror/core/defineMirror.js.map +1 -1
  167. package/dist/services/mirror/types.d.ts +5 -5
  168. package/dist/services/mirror/types.d.ts.map +1 -1
  169. package/dist/services/speech/providers/elevenlabs.provider.js +1 -1
  170. package/dist/services/speech/providers/elevenlabs.provider.js.map +1 -1
  171. package/dist/services/speech/providers/whisper.provider.js +1 -1
  172. package/dist/services/speech/providers/whisper.provider.js.map +1 -1
  173. package/dist/storage/core/StorageService.d.ts +1 -1
  174. package/dist/storage/core/StorageService.d.ts.map +1 -1
  175. package/dist/storage/core/StorageService.js +12 -20
  176. package/dist/storage/core/StorageService.js.map +1 -1
  177. package/dist/storage/core/storageFactory.d.ts.map +1 -1
  178. package/dist/storage/core/storageFactory.js.map +1 -1
  179. package/dist/storage/core/storageValidation.d.ts +1 -1
  180. package/dist/storage/core/storageValidation.d.ts.map +1 -1
  181. package/dist/storage/core/storageValidation.js +2 -2
  182. package/dist/storage/core/storageValidation.js.map +1 -1
  183. package/dist/storage/providers/cloudflare/d1Provider.d.ts +1 -1
  184. package/dist/storage/providers/cloudflare/d1Provider.d.ts.map +1 -1
  185. package/dist/storage/providers/cloudflare/d1Provider.js +4 -12
  186. package/dist/storage/providers/cloudflare/d1Provider.js.map +1 -1
  187. package/dist/storage/providers/cloudflare/kvProvider.d.ts +1 -1
  188. package/dist/storage/providers/cloudflare/kvProvider.d.ts.map +1 -1
  189. package/dist/storage/providers/cloudflare/kvProvider.js +3 -8
  190. package/dist/storage/providers/cloudflare/kvProvider.js.map +1 -1
  191. package/dist/storage/providers/cloudflare/r2Provider.d.ts +1 -1
  192. package/dist/storage/providers/cloudflare/r2Provider.d.ts.map +1 -1
  193. package/dist/storage/providers/cloudflare/r2Provider.js +3 -8
  194. package/dist/storage/providers/cloudflare/r2Provider.js.map +1 -1
  195. package/dist/storage/providers/inMemory/inMemoryProvider.d.ts +1 -1
  196. package/dist/storage/providers/inMemory/inMemoryProvider.d.ts.map +1 -1
  197. package/dist/storage/providers/inMemory/inMemoryProvider.js +2 -4
  198. package/dist/storage/providers/inMemory/inMemoryProvider.js.map +1 -1
  199. package/dist/testing/index.d.ts +35 -12
  200. package/dist/testing/index.d.ts.map +1 -1
  201. package/dist/testing/index.js +53 -38
  202. package/dist/testing/index.js.map +1 -1
  203. package/dist/testing/vitest.d.ts +1 -1
  204. package/dist/testing/vitest.d.ts.map +1 -1
  205. package/dist/types-global/errors.d.ts +28 -15
  206. package/dist/types-global/errors.d.ts.map +1 -1
  207. package/dist/types-global/errors.js +8 -1
  208. package/dist/types-global/errors.js.map +1 -1
  209. package/dist/utils/formatting/diffFormatter.d.ts +5 -5
  210. package/dist/utils/formatting/diffFormatter.d.ts.map +1 -1
  211. package/dist/utils/formatting/diffFormatter.js +7 -20
  212. package/dist/utils/formatting/diffFormatter.js.map +1 -1
  213. package/dist/utils/formatting/tableFormatter.d.ts +3 -3
  214. package/dist/utils/formatting/tableFormatter.d.ts.map +1 -1
  215. package/dist/utils/formatting/tableFormatter.js +6 -17
  216. package/dist/utils/formatting/tableFormatter.js.map +1 -1
  217. package/dist/utils/formatting/treeFormatter.d.ts +3 -3
  218. package/dist/utils/formatting/treeFormatter.d.ts.map +1 -1
  219. package/dist/utils/formatting/treeFormatter.js +5 -18
  220. package/dist/utils/formatting/treeFormatter.js.map +1 -1
  221. package/dist/utils/index.d.ts +4 -2
  222. package/dist/utils/index.d.ts.map +1 -1
  223. package/dist/utils/index.js +2 -2
  224. package/dist/utils/index.js.map +1 -1
  225. package/dist/utils/internal/error-handler/errorHandler.d.ts +1 -1
  226. package/dist/utils/internal/error-handler/errorHandler.d.ts.map +1 -1
  227. package/dist/utils/internal/error-handler/errorHandler.js +23 -11
  228. package/dist/utils/internal/error-handler/errorHandler.js.map +1 -1
  229. package/dist/utils/internal/error-handler/types.d.ts +10 -13
  230. package/dist/utils/internal/error-handler/types.d.ts.map +1 -1
  231. package/dist/utils/internal/logger.d.ts +3 -3
  232. package/dist/utils/internal/logger.d.ts.map +1 -1
  233. package/dist/utils/internal/logger.js +11 -5
  234. package/dist/utils/internal/logger.js.map +1 -1
  235. package/dist/utils/internal/performance.d.ts +1 -1
  236. package/dist/utils/internal/performance.d.ts.map +1 -1
  237. package/dist/utils/internal/performance.js +57 -13
  238. package/dist/utils/internal/performance.js.map +1 -1
  239. package/dist/utils/internal/requestContext.d.ts +79 -55
  240. package/dist/utils/internal/requestContext.d.ts.map +1 -1
  241. package/dist/utils/internal/requestContext.js +77 -32
  242. package/dist/utils/internal/requestContext.js.map +1 -1
  243. package/dist/utils/metrics/tokenCounter.d.ts +3 -3
  244. package/dist/utils/metrics/tokenCounter.d.ts.map +1 -1
  245. package/dist/utils/metrics/tokenCounter.js.map +1 -1
  246. package/dist/utils/network/fetchWithTimeout.d.ts +2 -2
  247. package/dist/utils/network/fetchWithTimeout.d.ts.map +1 -1
  248. package/dist/utils/network/fetchWithTimeout.js +8 -24
  249. package/dist/utils/network/fetchWithTimeout.js.map +1 -1
  250. package/dist/utils/network/retry.d.ts +2 -2
  251. package/dist/utils/network/retry.d.ts.map +1 -1
  252. package/dist/utils/overflow/outlineOnOverflow.d.ts +1 -1
  253. package/dist/utils/overflow/outlineOnOverflow.d.ts.map +1 -1
  254. package/dist/utils/pagination/pagination.d.ts +3 -3
  255. package/dist/utils/pagination/pagination.d.ts.map +1 -1
  256. package/dist/utils/pagination/pagination.js +3 -3
  257. package/dist/utils/pagination/pagination.js.map +1 -1
  258. package/dist/utils/parsing/csvParser.d.ts +2 -2
  259. package/dist/utils/parsing/csvParser.d.ts.map +1 -1
  260. package/dist/utils/parsing/csvParser.js +4 -8
  261. package/dist/utils/parsing/csvParser.js.map +1 -1
  262. package/dist/utils/parsing/dateParser.d.ts +3 -3
  263. package/dist/utils/parsing/dateParser.d.ts.map +1 -1
  264. package/dist/utils/parsing/dateParser.js +3 -2
  265. package/dist/utils/parsing/dateParser.js.map +1 -1
  266. package/dist/utils/parsing/frontmatterParser.d.ts +2 -2
  267. package/dist/utils/parsing/frontmatterParser.d.ts.map +1 -1
  268. package/dist/utils/parsing/frontmatterParser.js +7 -10
  269. package/dist/utils/parsing/frontmatterParser.js.map +1 -1
  270. package/dist/utils/parsing/htmlExtractor.d.ts +2 -2
  271. package/dist/utils/parsing/htmlExtractor.d.ts.map +1 -1
  272. package/dist/utils/parsing/htmlExtractor.js +6 -11
  273. package/dist/utils/parsing/htmlExtractor.js.map +1 -1
  274. package/dist/utils/parsing/jsonParser.d.ts +2 -2
  275. package/dist/utils/parsing/jsonParser.d.ts.map +1 -1
  276. package/dist/utils/parsing/jsonParser.js +4 -8
  277. package/dist/utils/parsing/jsonParser.js.map +1 -1
  278. package/dist/utils/parsing/pdfParser.d.ts +10 -10
  279. package/dist/utils/parsing/pdfParser.d.ts.map +1 -1
  280. package/dist/utils/parsing/pdfParser.js +26 -89
  281. package/dist/utils/parsing/pdfParser.js.map +1 -1
  282. package/dist/utils/parsing/xmlParser.d.ts +2 -2
  283. package/dist/utils/parsing/xmlParser.d.ts.map +1 -1
  284. package/dist/utils/parsing/xmlParser.js +4 -8
  285. package/dist/utils/parsing/xmlParser.js.map +1 -1
  286. package/dist/utils/parsing/yamlParser.d.ts +2 -2
  287. package/dist/utils/parsing/yamlParser.d.ts.map +1 -1
  288. package/dist/utils/parsing/yamlParser.js +4 -8
  289. package/dist/utils/parsing/yamlParser.js.map +1 -1
  290. package/dist/utils/scheduling/scheduler.d.ts.map +1 -1
  291. package/dist/utils/scheduling/scheduler.js +5 -6
  292. package/dist/utils/scheduling/scheduler.js.map +1 -1
  293. package/dist/utils/security/rateLimiter.d.ts +2 -2
  294. package/dist/utils/security/rateLimiter.d.ts.map +1 -1
  295. package/dist/utils/security/rateLimiter.js +1 -1
  296. package/dist/utils/security/rateLimiter.js.map +1 -1
  297. package/dist/utils/telemetry/attributes.d.ts +23 -7
  298. package/dist/utils/telemetry/attributes.d.ts.map +1 -1
  299. package/dist/utils/telemetry/attributes.js +23 -10
  300. package/dist/utils/telemetry/attributes.js.map +1 -1
  301. package/dist/utils/telemetry/trace.d.ts +3 -3
  302. package/dist/utils/telemetry/trace.d.ts.map +1 -1
  303. package/dist/utils/telemetry/trace.js +4 -2
  304. package/dist/utils/telemetry/trace.js.map +1 -1
  305. package/package.json +22 -26
  306. package/scripts/lint-mcp.ts +1 -3
  307. package/scripts/lint-packaging.ts +1 -1
  308. package/skills/add-service/SKILL.md +2 -2
  309. package/skills/add-test/SKILL.md +42 -20
  310. package/skills/add-tool/SKILL.md +68 -19
  311. package/skills/api-canvas/SKILL.md +2 -2
  312. package/skills/api-config/SKILL.md +1 -11
  313. package/skills/api-context/SKILL.md +175 -111
  314. package/skills/api-linter/SKILL.md +2 -2
  315. package/skills/api-telemetry/SKILL.md +8 -12
  316. package/skills/api-testing/SKILL.md +53 -52
  317. package/skills/code-simplifier/SKILL.md +2 -2
  318. package/skills/design-mcp-server/SKILL.md +34 -18
  319. package/skills/field-test/SKILL.md +4 -2
  320. package/skills/git-wrapup/SKILL.md +3 -3
  321. package/skills/polish-docs-meta/SKILL.md +1 -1
  322. package/skills/polish-docs-meta/references/agent-protocol.md +2 -2
  323. package/skills/polish-docs-meta/references/readme.md +1 -1
  324. package/skills/release-and-publish/SKILL.md +3 -1
  325. package/skills/report-issue-framework/SKILL.md +2 -2
  326. package/skills/report-issue-local/SKILL.md +2 -2
  327. package/skills/security-pass/SKILL.md +16 -14
  328. package/templates/.github/CODE_OF_CONDUCT.md +28 -0
  329. package/templates/.github/CONTRIBUTING.md +50 -0
  330. package/templates/.github/SECURITY.md +24 -0
  331. package/templates/AGENTS.md +6 -6
  332. package/templates/CLAUDE.md +6 -6
  333. package/templates/changelog/template.md +55 -16
  334. package/templates/tests/resources/echo.resource.test.ts +16 -9
  335. package/dist/mcp-server/elicitation.d.ts +0 -64
  336. package/dist/mcp-server/elicitation.d.ts.map +0 -1
  337. package/dist/mcp-server/elicitation.js +0 -81
  338. package/dist/mcp-server/elicitation.js.map +0 -1
  339. package/dist/mcp-server/protocolSession.d.ts +0 -34
  340. package/dist/mcp-server/protocolSession.d.ts.map +0 -1
  341. package/dist/mcp-server/protocolSession.js +0 -13
  342. package/dist/mcp-server/protocolSession.js.map +0 -1
  343. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.d.ts +0 -53
  344. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.d.ts.map +0 -1
  345. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.js +0 -65
  346. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.js.map +0 -1
  347. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.d.ts +0 -70
  348. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.d.ts.map +0 -1
  349. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.js +0 -130
  350. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.js.map +0 -1
  351. package/dist/mcp-server/tasks/core/storageBackedTaskStore.d.ts +0 -109
  352. package/dist/mcp-server/tasks/core/storageBackedTaskStore.d.ts.map +0 -1
  353. package/dist/mcp-server/tasks/core/storageBackedTaskStore.js +0 -209
  354. package/dist/mcp-server/tasks/core/storageBackedTaskStore.js.map +0 -1
  355. package/dist/mcp-server/tasks/core/taskManager.d.ts +0 -91
  356. package/dist/mcp-server/tasks/core/taskManager.d.ts.map +0 -1
  357. package/dist/mcp-server/tasks/core/taskManager.js +0 -210
  358. package/dist/mcp-server/tasks/core/taskManager.js.map +0 -1
  359. package/dist/mcp-server/tasks/core/taskTypes.d.ts +0 -18
  360. package/dist/mcp-server/tasks/core/taskTypes.d.ts.map +0 -1
  361. package/dist/mcp-server/tasks/core/taskTypes.js +0 -20
  362. package/dist/mcp-server/tasks/core/taskTypes.js.map +0 -1
  363. package/dist/mcp-server/tasks/utils/taskToolDefinition.d.ts +0 -101
  364. package/dist/mcp-server/tasks/utils/taskToolDefinition.d.ts.map +0 -1
  365. package/dist/mcp-server/tasks/utils/taskToolDefinition.js +0 -14
  366. package/dist/mcp-server/tasks/utils/taskToolDefinition.js.map +0 -1
@@ -4,14 +4,14 @@ description: >
4
4
  Catalog of OpenTelemetry instrumentation built into framework `@cyanheads/mcp-ts-core` — spans, metrics, completion logs, env config, runtime caveats, custom instrumentation patterns, and cardinality rules. Use when enabling OTel export, adding custom spans or metrics in services, debugging missing telemetry, looking up attribute names, or deciding what's safe to put on a metric attribute vs. a span.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.2"
7
+ version: "1.4"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
11
11
 
12
12
  ## Overview
13
13
 
14
- The framework auto-instruments every tool, resource, prompt, storage, LLM, speech, and graph call — each gets its own span and the standard counters/histograms. HTTP server requests pick up spans from `HttpInstrumentation` (all Node.js HTTP traffic, skips `/healthz`) plus `httpInstrumentationMiddleware` from `@hono/otel` on the MCP HTTP endpoint when installed (optional Tier 3 peer — `bun add @hono/otel`). On Bun, `HttpInstrumentation` silently no-ops and `@hono/otel` is the only HTTP coverage. Auth checks, session lifecycle, and task lifecycle are tracked as **metrics only** — auth decorates the active HTTP span with attributes, sessions and tasks emit counters.
14
+ The framework auto-instruments every tool, resource, prompt, storage, LLM, speech, and graph call — each gets its own span and the standard counters/histograms. HTTP server requests pick up spans from `HttpInstrumentation` (all Node.js HTTP traffic, skips `/healthz`) plus `httpInstrumentationMiddleware` from `@hono/otel` on the MCP HTTP endpoint when installed (optional Tier 3 peer — `bun add @hono/otel`). On Bun, `HttpInstrumentation` silently no-ops and `@hono/otel` is the only HTTP coverage. Auth checks and session lifecycle are tracked as **metrics only** — auth decorates the active HTTP span with attributes, sessions emit counters.
15
15
 
16
16
  `requestId`, `traceId`, and `tenantId` correlate automatically across spans, metrics, and logs. Pino logs get `trace_id`/`span_id` injected when a span is active.
17
17
 
@@ -61,15 +61,17 @@ Every handler call gets a span. Nested operations (storage, graph, LLM) become c
61
61
 
62
62
  | Span name | Source | Key attributes |
63
63
  |:----------|:-------|:---------------|
64
- | `tool_execution:<tool>` | every tool call | `mcp.tool.input_bytes`, `mcp.tool.output_bytes`, `mcp.tool.duration_ms`, `mcp.tool.success`, `mcp.tool.error_code`, `mcp.tool.partial_success`, `mcp.tool.batch.{succeeded,failed}_count` |
65
- | `resource_read:<resource>` | every resource handler | `mcp.resource.uri`, `mcp.resource.mime_type`, `mcp.resource.size_bytes`, `mcp.resource.duration_ms`, `mcp.resource.success`, `mcp.resource.error_code` |
66
- | `prompt_generation:<prompt>` | every prompt handler | `mcp.prompt.input_bytes`, `mcp.prompt.output_bytes`, `mcp.prompt.message_count`, `mcp.prompt.duration_ms`, `mcp.prompt.success`, `mcp.prompt.error_code` |
64
+ | `tool_execution:<tool>` | every tool call | `mcp.tool.input_bytes`, `mcp.tool.output_bytes`, `mcp.tool.duration_ms`, `mcp.tool.success`, `mcp.tool.error_code`, `mcp.tool.input_required`, `mcp.tool.partial_success`, `mcp.tool.batch.{succeeded,failed}_count` |
65
+ | `resource_read:<resource>` | every resource handler | `mcp.resource.uri`, `mcp.resource.mime_type`, `mcp.resource.size_bytes`, `mcp.resource.duration_ms`, `mcp.resource.success`, `mcp.resource.error_code`, `mcp.resource.input_required` |
66
+ | `prompt_generation:<prompt>` | every prompt handler | `mcp.prompt.input_bytes`, `mcp.prompt.output_bytes`, `mcp.prompt.message_count`, `mcp.prompt.duration_ms`, `mcp.prompt.success`, `mcp.prompt.error_code`, `mcp.prompt.input_required` |
67
67
  | `storage:<op>` | `StorageService` (every call) | `mcp.storage.operation`, `mcp.storage.duration_ms`, `mcp.storage.success`, `mcp.storage.key_count` (batch ops) |
68
68
  | `graph:<op>` | `GraphService` (every call) | `mcp.graph.operation`, `mcp.graph.duration_ms`, `mcp.graph.success` |
69
69
  | `gen_ai.chat_completion` | OpenRouter LLM provider | `gen_ai.system=openrouter`, `gen_ai.request.model`, `gen_ai.request.{max_tokens,temperature,top_p,streaming}`, `gen_ai.response.model`, `gen_ai.usage.{input,output,total}_tokens` |
70
70
  | `speech:tts` | ElevenLabs provider | `mcp.speech.provider`, `mcp.speech.operation`, `mcp.speech.input_bytes`, `mcp.speech.output_bytes`, `mcp.speech.duration_ms`, `mcp.speech.success` |
71
71
  | `speech:stt` | Whisper provider | same as `speech:tts` |
72
72
 
73
+ A handler that ends its round with `ctx.requestInput(...)` closes its span `OK` with `mcp.*.input_required` set — no recorded exception, no error-counter increment. Multi-round-trip input is protocol control flow, so it never inflates error rates; split on that attribute to tell an incomplete round from a completed call.
74
+
73
75
  Trace context propagates across boundaries via W3C `traceparent` headers. See `api-utils` → `telemetry/trace` for `withSpan`, `buildTraceparent`, `extractTraceparent`, `createContextWithParentTrace`, `injectCurrentContextInto`, `runInContext` signatures.
74
76
 
75
77
  ---
@@ -118,7 +120,7 @@ All custom metrics are namespaced `mcp.*` (or `process.*` / `http.client.*` wher
118
120
  | `mcp.graph.duration` | histogram | `ms` | `mcp.graph.operation`, `mcp.graph.success` |
119
121
  | `mcp.graph.errors` | counter | `{errors}` | `mcp.graph.operation` |
120
122
 
121
- ### Transport, auth, sessions, tasks
123
+ ### Transport, auth, sessions
122
124
 
123
125
  | Metric | Type | Unit | Attributes |
124
126
  |:-------|:-----|:-----|:-----------|
@@ -128,12 +130,6 @@ All custom metrics are namespaced `mcp.*` (or `process.*` / `http.client.*` wher
128
130
  | `mcp.session.duration` | histogram | `s` | — |
129
131
  | `mcp.sessions.active` | observable gauge | `{sessions}` | — |
130
132
  | `mcp.heartbeat.failures` | counter | `{failures}` | `mcp.connection.transport` (`stdio`/`http`) |
131
- | `mcp.http.close_failures` | counter | `{failures}` | `surface` (`transport`/`server`), `trigger` (`success`/`error`/`sse-abort`) — per-request close threw or timed out |
132
- | `mcp.http.per_request.created` | counter | `{instances}` | `kind` (`server`/`transport`) — per-request `McpServer` and `McpSessionTransport` instances created |
133
- | `mcp.http.per_request.finalized` | counter | `{instances}` | `kind` (`server`/`transport`) — per-request instances reclaimed by GC; persistent gap vs `created` indicates a leak |
134
- | `mcp.tasks.created` | counter | `{tasks}` | `mcp.task.store_type` (`in-memory`/`storage`) |
135
- | `mcp.tasks.status_changes` | counter | `{transitions}` | `mcp.task.status`, `mcp.task.store_type` |
136
- | `mcp.tasks.active` | observable gauge | `{tasks}` | — (in-memory store only) |
137
133
 
138
134
  ### Errors, rate limits, HTTP client
139
135
 
@@ -4,7 +4,7 @@ description: >
4
4
  Testing patterns for MCP tool/resource handlers using `createMockContext` and Vitest. Covers mock context options, handler testing, McpError assertions, format testing, Vitest config setup, and test isolation conventions.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.8"
7
+ version: "1.9"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -134,8 +134,8 @@ import { createMockContext } from '@cyanheads/mcp-ts-core/testing';
134
134
  createMockContext() // working ctx.state on tenant 'default'
135
135
  createMockContext({ tenantId: 'test-tenant' }) // explicit tenant scope for ctx.state
136
136
  createMockContext({ errors: myTool.errors }) // attaches typed ctx.fail keyed by the contract reasons
137
- createMockContext({ elicit: vi.fn().mockResolvedValue(...) }) // with elicitation
138
- createMockContext({ progress: true }) // with task progress (ctx.progress populated)
137
+ createMockContext({ inputResponses: { confirm: { action: 'accept', content: { ok: true } } } }) // second round of a multi-round-trip handler
138
+ createMockContext({ requestState: 'opaque-state' }) // seeds ctx.inputs.state()
139
139
  createMockContext({ requestId: 'my-id' }) // override request ID (default: 'test-request-id')
140
140
  createMockContext({ notifyResourceListChanged: () => {} }) // with resource-list change notifier
141
141
  createMockContext({ notifyResourceUpdated: (_uri) => {} }) // with resource update notifier
@@ -149,15 +149,15 @@ createMockContext({ uri: new URL('myscheme://item/123') }) // for resource han
149
149
  ```ts
150
150
  interface MockContextOptions<TErrors extends readonly ErrorContract[] | undefined> {
151
151
  auth?: AuthContext;
152
- elicit?: (message: string, schema: z.ZodObject<z.ZodRawShape>) => Promise<ElicitResult>;
153
152
  errors?: TErrors | undefined;
153
+ inputResponses?: InputResponses | Record<string, unknown>;
154
154
  notifyPromptListChanged?: () => void;
155
155
  notifyResourceListChanged?: () => void;
156
156
  notifyResourceUpdated?: (uri: string) => void;
157
157
  notifyToolListChanged?: () => void;
158
- progress?: boolean;
159
- sessionId?: string;
160
158
  requestId?: string;
159
+ requestState?: unknown;
160
+ sessionId?: string;
161
161
  signal?: AbortSignal;
162
162
  tenantId?: string;
163
163
  uri?: URL;
@@ -166,17 +166,17 @@ interface MockContextOptions<TErrors extends readonly ErrorContract[] | undefine
166
166
 
167
167
  | Option | Effect |
168
168
  |:-------|:-------|
169
- | _(none)_ | Working `ctx.state` on tenant `'default'`; `ctx.elicit`/`ctx.progress` are `undefined` |
169
+ | _(none)_ | Working `ctx.state` on tenant `'default'`; `ctx.inputs` is empty (first round) |
170
170
  | `auth` | Sets `ctx.auth` for scope-checking tests |
171
- | `elicit` | Assigns a function to `ctx.elicit` for testing elicitation calls |
172
171
  | `errors` | Attaches a typed `ctx.fail` against the contract — same wiring the production handler factory uses. Pass `myTool.errors` directly; the return type narrows to `HandlerContext<ReasonOf<…>>`, so the context is assignable to that definition's handler parameter. |
172
+ | `inputResponses` | Seeds `ctx.inputs` with the responses a retried request would carry, keyed by the identifiers the handler's `ctx.requestInput(...)` assigned (see below) |
173
173
  | `notifyPromptListChanged` | Assigns `ctx.notifyPromptListChanged` for prompt-list change notification tests |
174
174
  | `notifyResourceListChanged` | Assigns `ctx.notifyResourceListChanged` for resource notification tests |
175
175
  | `notifyResourceUpdated` | Assigns `ctx.notifyResourceUpdated` for resource update notification tests |
176
176
  | `notifyToolListChanged` | Assigns `ctx.notifyToolListChanged` for tool-list change notification tests |
177
- | `sessionId` | Sets `ctx.sessionId` for handlers that branch on session ID |
178
- | `progress` | Populates `ctx.progress` with real state-tracking implementation (see below) |
179
177
  | `requestId` | Overrides `ctx.requestId` (default: `'test-request-id'`) |
178
+ | `requestState` | Seeds `ctx.inputs.state()` — the opaque state a prior round attached |
179
+ | `sessionId` | Sets `ctx.sessionId` for handlers that branch on session ID |
180
180
  | `signal` | Overrides `ctx.signal` — useful for cancellation testing |
181
181
  | `tenantId` | Scopes `ctx.state` to a specific tenant. Defaults to `'default'` — the value stdio (and HTTP with `MCP_AUTH_MODE=none`) resolves |
182
182
  | `uri` | Sets `ctx.uri` for resource handler testing |
@@ -200,28 +200,54 @@ await expect(ctx.state.set('cache:v1:abc', {})).rejects.toThrow(McpError);
200
200
 
201
201
  Reach for `createInMemoryStorage()` when a service takes a `StorageService` directly — it builds the same pair.
202
202
 
203
- ### Mock progress
203
+ ### Mock inputs
204
204
 
205
- When `progress: true`, `ctx.progress` is a real state-tracking object — not `vi.fn()` spies. It maintains internal state accessible via inspection properties:
205
+ `ctx.requestInput` is the real implementation: it throws an `InputRequiredSignal` the production handler factories convert into an `input_required` result. In a unit test the handler is called directly, so that signal surfaces as a thrown value — which is exactly how you assert the first round.
206
206
 
207
207
  ```ts
208
- const ctx = createMockContext({ progress: true });
209
- // ctx.progress is typed as ContextProgress, but the mock exposes internal state:
210
- const progress = ctx.progress as ContextProgress & {
211
- _total: number;
212
- _completed: number;
213
- _messages: string[];
214
- };
215
-
216
- await ctx.progress!.setTotal(10);
217
- await ctx.progress!.increment(3);
218
- await ctx.progress!.update('step message');
219
-
220
- expect(progress._total).toBe(10);
221
- expect(progress._completed).toBe(3);
222
- expect(progress._messages).toContain('step message');
208
+ import { isInputRequiredSignal } from '@cyanheads/mcp-ts-core';
209
+
210
+ it('asks for confirmation on the first round', async () => {
211
+ const ctx = createMockContext();
212
+ await expect(myTool.handler(myTool.input.parse({ path: '/tmp/x' }), ctx))
213
+ .rejects.toSatisfy(isInputRequiredSignal);
214
+ });
223
215
  ```
224
216
 
217
+ To assert on *what* was requested, catch it and read `error.result` — the `input_required` result the handler factory would have returned:
218
+
219
+ ```ts
220
+ async function requestedInput(input: ToolInput, options: MockContextOptions = {}) {
221
+ try {
222
+ await myTool.handler(input, createMockContext(options));
223
+ } catch (error) {
224
+ if (isInputRequiredSignal(error)) return error.result;
225
+ throw error;
226
+ }
227
+ throw new Error('Expected the handler to request input.');
228
+ }
229
+ ```
230
+
231
+ `inputResponses` drives the second round. `ctx.inputs.accepted(key, schema)` and `.view(key)` read it with the same helpers production uses, so a wrong response shape fails in the test:
232
+
233
+ ```ts
234
+ it('proceeds once the user accepts', async () => {
235
+ const ctx = createMockContext({
236
+ inputResponses: { confirm: { action: 'accept', content: { confirm: true } } },
237
+ });
238
+ await expect(myTool.handler(input, ctx)).resolves.toMatchObject({ deleted: '/tmp/x' });
239
+ });
240
+
241
+ it('stops when the user declines', async () => {
242
+ const ctx = createMockContext({
243
+ inputResponses: { confirm: { action: 'decline' } },
244
+ });
245
+ await expect(myTool.handler(input, ctx)).rejects.toThrow(McpError);
246
+ });
247
+ ```
248
+
249
+ `ctx.inputs.dropped` is always `[]` on a mock context — the drop only happens in the SDK's wire decoding, so cover it in an integration test rather than a unit one.
250
+
225
251
  ### Mock logger
226
252
 
227
253
  `ctx.log` captures all log calls for inspection. Import `MockContextLogger` from `@cyanheads/mcp-ts-core/testing` and cast `ctx.log` to access the `.calls` array (the cast is necessary because `createMockContext` returns `Context`, which types `log` as `ContextLogger`):
@@ -273,31 +299,6 @@ Parse input through `myTool.input.parse(...)` to validate against the Zod schema
273
299
 
274
300
  ---
275
301
 
276
- ## Testing with optional capabilities
277
-
278
- ```ts
279
- it('uses elicitation when available', async () => {
280
- const elicit = vi.fn().mockResolvedValue({
281
- action: 'accept',
282
- content: { format: 'json' },
283
- });
284
- const ctx = createMockContext({ elicit });
285
- const input = myTool.input.parse({ query: 'hello' });
286
- await myTool.handler(input, ctx);
287
- expect(elicit).toHaveBeenCalledOnce();
288
- });
289
-
290
- it('handles missing elicitation gracefully', async () => {
291
- // ctx.elicit is undefined — handler must check before calling
292
- const ctx = createMockContext();
293
- const input = myTool.input.parse({ query: 'hello' });
294
- // Should not throw even when ctx.elicit is absent
295
- await expect(myTool.handler(input, ctx)).resolves.toBeDefined();
296
- });
297
- ```
298
-
299
- ---
300
-
301
302
  ## Testing with form-based client payloads
302
303
 
303
304
  LLM clients only send populated fields. **Form-based clients** (MCP Inspector, web UIs) submit the full schema shape — optional object fields arrive with empty-string inner values instead of `undefined`. Both are valid MCP usage. Test that handlers handle both gracefully.
@@ -4,7 +4,7 @@ description: >
4
4
  Post-session code review and cleanup against a working tree of changes. Analyzes `git diff` to simplify, consolidate, and align changed code with the existing codebase — modernize syntax, remove unnecessary complexity, consolidate duplicated logic, catch efficiency issues. Use after a substantive working session, or when asked to clean up, simplify, reduce slop, consolidate, modernize, tighten up, or de-slop code. For `@cyanheads/mcp-ts-core` projects, includes specific transformations for tool/resource/prompt definitions, the ctx pattern, error factories, and framework idioms.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.2"
7
+ version: "1.3"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -62,7 +62,7 @@ Evaluate the changes across these dimensions. Not every dimension applies to eve
62
62
 
63
63
  - **Error throwing patterns** — Prefer framework error factories (`McpError`, `validationError`, `notFound`, `httpErrorFromResponse`) over raw `throw new Error()`. Tool handlers should throw — the framework catches, classifies, and instruments.
64
64
  - **Error codes** — `InvalidParams` only for malformed JSON-RPC params shape. `ValidationError` for domain validation. `NotFound` for missing entities. Don't conflate them.
65
- - **Ctx usage** — Use `ctx.log`, `ctx.state`, `ctx.elicit` — don't reach for global loggers or request-scoped storage directly. The `ctx` pattern carries tenant scope and OTel context.
65
+ - **Ctx usage** — Use `ctx.log`, `ctx.state`, `ctx.enrich` — don't reach for global loggers or request-scoped storage directly. The `ctx` pattern carries tenant scope and OTel context.
66
66
  - **Zod schemas** — Every tool input/output field needs `.describe()`. Zod 4 requires `z.record(z.string(), z.string())` not `z.record(z.string())`. Use `.optional()` rather than `.nullish()` unless null is semantically distinct from absent.
67
67
  - **Tool annotations** — `readOnlyHint`, `idempotentHint`, `openWorldHint` should reflect reality. A read-only tool with `readOnlyHint: false` gives clients the wrong picture.
68
68
  - **`exactOptionalPropertyTypes` boundaries** — If a downstream type insists on the field being present-or-not-present (not present-as-undefined), use a mapped widening type at the boundary. The pattern is documented in the framework.
@@ -4,7 +4,7 @@ description: >
4
4
  Design the tool surface, resources, and service layer for a new MCP server. Use when starting a new server, planning a major feature expansion, or when the user describes a domain/API they want to expose via MCP. Produces a design doc at docs/design.md that drives implementation.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "2.20"
7
+ version: "2.22"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -52,7 +52,9 @@ The server name (repo name, npm package, public identity) must communicate what
52
52
 
53
53
  - **Use the canonical platform/brand name, not abbreviations.** `libofcongress-mcp-server` not `loc-mcp-server` ("loc" reads as lines-of-code or location). `federal-reserve-mcp-server` not `fred-mcp-server` ("fred" reads as a person's name).
54
54
  - **Add a descriptive suffix when the base name is a non-obvious acronym.** Pattern: `{acronym}-{domain}-mcp-server` — e.g., `eia-energy-mcp-server`, `bls-labor-mcp-server`, `nhtsa-vehicle-safety-mcp-server`. Skip when the name is already self-descriptive (`earthquake-mcp-server`, `wikidata-mcp-server`).
55
- - **The name becomes the tool prefix.** Every tool is `{prefix}_{verb}_{noun}`, so the server name shows up in every tool call an agent sees. A descriptive name gives agents domain context without reading the server's instructions.
55
+ - **Don't overclaim scope.** A name asserts breadth on two independent axes, and must be honest on both. *Source breadth:* is this a first-party wrapper around one provider's API, or genuine aggregation across several independent sources behind one normalized surface? *Domain or jurisdiction breadth:* one, or many? A generic name claims breadth on whichever axis it leaves unqualified. `threat-intel-mcp-server` earns its generic name by aggregating independent sources for one workflow; that same name over a single vendor's API would be a defect — name that for the vendor. When the real scope is one jurisdiction, put it in the name (`uk-legislation-mcp-server`, `statistics-canada-mcp-server`) rather than letting a generic noun imply worldwide coverage a user will only discover is absent after installing. Note that aggregating several bodies *within* one jurisdiction earns a cross-source name, not a cross-jurisdiction one.
56
+ - **Aggregation is a claim about entities, not endpoints.** One provider publishing five APIs is still first-party. Wrapping several endpoints of the same upstream does not make a server an aggregator and does not earn a generic name.
57
+ - **The tool prefix derives from the name but is a separate identifier.** Every tool is `{prefix}_{verb}_{noun}`, so the prefix shows up in every tool call an agent sees, and a descriptive one gives agents domain context without reading the server's instructions. The prefix names the *source*, dropping the descriptive qualifier the repo name carries for human browsing: `eia-energy-mcp-server` → `eia_`, `nhtsa-vehicle-safety-mcp-server` → `nhtsa_`. Because it names the source rather than the repo, the two can diverge — a later repo rename does not have to move the prefix, and usually shouldn't: renaming an advertised tool surface is a breaking change for every existing client, while renaming a package is not.
56
58
 
57
59
  ## Steps
58
60
 
@@ -139,7 +141,7 @@ Most tools follow the `{server}_{verb}_{noun}` default — one focused responsib
139
141
 
140
142
  | Shape | Purpose | Typical form | Examples |
141
143
  |:------|:--------|:-------------|:---------|
142
- | **Workflow** | Multi-step orchestration that replaces a common agent chain | N upstream calls (often parallelized); may elicit confirmation; may need mid-flow cleanup | `clinicaltrials_find_studies` (search → filter → rank) |
144
+ | **Workflow** | Multi-step orchestration that replaces a common agent chain | N upstream calls (often parallelized); may request confirmation; may need mid-flow cleanup | `clinicaltrials_find_studies` (search → filter → rank) |
143
145
  | **Instruction** | State-aware procedural guidance — advice, not action | Static markdown + a few live-state fetches, `readOnlyHint: true`, outputs `nextToolSuggestions` pre-filling the recommended follow-up. No writes. | `git_wrapup_instructions` |
144
146
  | **Reference** | Decode opaque domain vocabulary — codes, enums, identifier formats, coverage windows — so agents can build valid inputs for the rest of the surface | Static tables or one cached fetch (often zero upstream calls); consolidate N lists under one `topic` enum; `readOnlyHint: true`, `openWorldHint: false` when offline | `medcode_list_systems`, `osv_list_ecosystems` |
145
147
 
@@ -267,20 +269,34 @@ A reference tool is the surface's decoder ring: which codes exist, what they mea
267
269
 
268
270
  Tools that perform multi-step mutations (the Workflow shape) have two safety considerations beyond single-call tools. Both are about giving the agent — and the human behind it — a chance to catch a bad invocation before it commits.
269
271
 
270
- **Elicit-guarded destructive modes with annotation fallback.** When a workflow's `mode` parameter switches between safe and destructive arms (`draft` vs `send`, `plan` vs `apply`), gate the destructive arm behind `ctx.elicit` when the client supports it, so a human confirms before the irreversible step fires. Elicitation isn't universally available — headless stdio sessions and many non-interactive clients don't expose it. Fall back on `destructiveHint: true` in annotations so those clients' approval flows still surface the risk. Document the fallback in the handler so maintainers don't assume elicit always runs:
272
+ **Confirmation-gated destructive modes, with an annotation fallback.** When a workflow's `mode` parameter switches between safe and destructive arms (`draft` vs `send`, `plan` vs `apply`), gate the destructive arm on a confirmation the handler asks for via `ctx.requestInput(...)`, so a human approves before the irreversible step fires. The handler is re-entered with the answer on `ctx.inputs`; it does not `await` mid-call.
273
+
274
+ The gate is always *reachable* — `ctx.requestInput` is present on every transport and both protocol eras — but it is not always *answerable*: a client that never fulfils the `input_required` result simply doesn't retry, and the destructive step never runs. Keep `destructiveHint: true` in annotations so those clients' own approval flows still surface the risk.
271
275
 
272
276
  ```ts
273
- annotations: { destructiveHint: true }, // fallback for clients without elicit
277
+ annotations: { destructiveHint: true }, // client-side approval flows still see the risk
274
278
  // ...
275
- async handler(input, ctx) {
276
- if (input.mode === 'apply' && ctx.elicit) {
277
- const confirm = await ctx.elicit(
278
- `Apply migration affecting ${affectedRowCount} rows in production? Cannot be rolled back automatically.`,
279
- z.object({ confirmed: z.literal(true).describe('Type true to apply.') }),
280
- );
281
- if (confirm.action !== 'accept') throw new Error('Migration cancelled by user.');
279
+ const Confirm = z.object({ confirmed: z.literal(true).describe('Type true to apply.') });
280
+
281
+ handler(input, ctx) {
282
+ if (input.mode === 'apply') {
283
+ // A decline is terminal — re-asking would loop until the round budget runs out.
284
+ const view = ctx.inputs.view('confirm');
285
+ if (view.kind === 'elicit' && view.action !== 'accept') {
286
+ throw validationError('Migration cancelled by user.');
287
+ }
288
+ if (!ctx.inputs.accepted('confirm', Confirm)) {
289
+ return ctx.requestInput({
290
+ inputRequests: {
291
+ confirm: inputRequired.elicit({
292
+ message: `Apply migration affecting ${affectedRowCount} rows in production? Cannot be rolled back automatically.`,
293
+ requestedSchema: Confirm,
294
+ }),
295
+ },
296
+ });
297
+ }
282
298
  }
283
- // destructive step proceeds; destructiveHint covers clients that skipped elicit
299
+ // destructive step proceeds
284
300
  }
285
301
  ```
286
302
 
@@ -620,13 +636,13 @@ Each step is independently testable.
620
636
 
621
637
  Keep it concise. The design doc is a working reference, not a spec document — enough to orient a developer (or agent) implementing the server, not more.
622
638
 
623
- **Workflow Analysis example.** For multi-step workflow tools, document the upstream call sequence in a table — it drives several downstream decisions during implementation: the service-layer method shape, retry boundaries, where cleanup or elicit belongs, and what post-action state to fetch for the response.
639
+ **Workflow Analysis example.** For multi-step workflow tools, document the upstream call sequence in a table — it drives several downstream decisions during implementation: the service-layer method shape, retry boundaries, where cleanup or the confirmation round belongs, and what post-action state to fetch for the response.
624
640
 
625
- `deploy_release` (5–8 upstream calls, plus elicit):
641
+ `deploy_release` (5–8 upstream calls, plus a confirmation round):
626
642
 
627
643
  | # | Call | Purpose | Mode gate |
628
644
  |:--|:-----|:--------|:----------|
629
- | 0 | `ctx.elicit` confirmation | Human approval before promote | `promote` (when available) |
645
+ | 0 | `ctx.requestInput` confirmation | Human approval before promote | `promote` |
630
646
  | 1 | `POST /releases` | Create release record | always |
631
647
  | 2 | `PUT /releases/{id}/artifacts` | Attach build artifacts | always |
632
648
  | 3 | `GET /releases/{id}/preflight` | Health checks, smoke tests | always |
@@ -636,7 +652,7 @@ Keep it concise. The design doc is a working reference, not a spec document —
636
652
  | 7 | `GET /releases/{id}` | Post-action state for response | always |
637
653
  | — | `DELETE /releases/{id}/canary-traffic` | Cleanup canary if mid-flow error | on error + `cleanupOnError` |
638
654
 
639
- The table surfaces design questions early: should the elicit happen before or after the artifacts are attached? Does cleanup drop the canary on any failure, or only failures past the promote step? What does the response body need from the final GET — version, traffic percentage, health summary? Answering these during design is far cheaper than mid-implementation.
655
+ The table surfaces design questions early: should the confirmation round happen before or after the artifacts are attached? Does cleanup drop the canary on any failure, or only failures past the promote step? What does the response body need from the final GET — version, traffic percentage, health summary? Answering these during design is far cheaper than mid-implementation.
640
656
 
641
657
  ### 9. Confirm and Proceed
642
658
 
@@ -685,7 +701,7 @@ Items without an `If …:` prefix apply to every design. Conditional items only
685
701
  - [ ] **If an upstream API has no native search but the relevant set is bounded:** MCP-side list filtering considered — a distinct local filter param (`filter`/`nameContains`, not `query`), filtering the full set, strict token match (fuzzy only when a caller needs typo tolerance)
686
702
  - [ ] **If the server has workflow tools:** call-flow documented (upstream sequence + mode arms) in design doc's Workflow Analysis
687
703
  - [ ] **If state-aware procedural guidance adds value:** instruction tool considered with `nextToolSuggestions` pre-filled from diagnostics
688
- - [ ] **If workflow tools have destructive modes:** destructive arm guarded by `ctx.elicit` when available, with `destructiveHint` annotation as fallback for non-interactive clients
704
+ - [ ] **If workflow tools have destructive modes:** destructive arm gated on a `ctx.requestInput` confirmation read back from `ctx.inputs`, with `destructiveHint` annotation so clients that never fulfil the round still surface the risk
689
705
  - [ ] **If a parameter determines blast radius:** safe default set (e.g., `mode: 'preview'`, `dryRun: true`, `confirmCount` required)
690
706
  - [ ] **App tools default to no.** If one was proposed, verified there's a real human-in-the-loop in an MCP Apps-capable client justifying the iframe/CSP/`format()`-twin maintenance cost — otherwise dropped in favor of a standard tool
691
707
  - [ ] **If the server exposes resources:** URIs use `{param}` templates, pagination planned for large lists
@@ -4,7 +4,7 @@ description: >
4
4
  Exercise tools, resources, and prompts against a live HTTP server via MCP JSON-RPC over curl. Starts the server, surfaces the catalog, runs real and adversarial inputs, and produces a tight report with concrete findings and numbered follow-up options. Use after adding or modifying definitions, or when the user asks to test, try out, or verify their MCP surface.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "2.8"
7
+ version: "2.9"
8
8
  audience: external
9
9
  type: debug
10
10
  ---
@@ -243,7 +243,9 @@ mcp_init <url-from-mcp_start>
243
243
 
244
244
  Runs `initialize`, sends `notifications/initialized`, prints `sid=<id>` to capture for `mcp_call`, plus the protocol version the server negotiated.
245
245
 
246
- The helper requests the SDK's current protocol version. **If `protocol=` comes back older than `requested=`, the server capped it** — every call after that exercises an older protocol than a current client would negotiate. Note it as a `bug` finding and check the pinned `@modelcontextprotocol/sdk` version; don't quietly test the downgraded surface. To deliberately test an older version, set `MCP_FIELD_TEST_PROTOCOL`.
246
+ The helper requests the newest `initialize`-negotiated revision the SDK supports (`2025-11-25`). **If `protocol=` comes back older than `requested=`, the server capped it** — every call after that exercises an older protocol than a current client would negotiate. Note it as a `bug` finding and check the pinned `@modelcontextprotocol/server` version; don't quietly test the downgraded surface. To deliberately test an older version, set `MCP_FIELD_TEST_PROTOCOL`.
247
+
248
+ This exercises the **2025-era arm**: `initialize` negotiates the revision, the server mints an `Mcp-Session-Id`, and every later call rides that session. The [2026-07-28 revision](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports) is not in the `initialize` ladder at all — it is selected per request by the `io.modelcontextprotocol/protocolVersion` key in the request's own `_meta` envelope, and carries no session. Curl-based field testing therefore covers the sessionful leg; exercise the per-request leg from a real 2026-era client or an integration test.
247
249
 
248
250
  ### 3. Surface the catalog
249
251
 
@@ -4,7 +4,7 @@ description: >
4
4
  Land working-tree changes as logical commits — the work grouped by concern, topped by a release commit (version bump, changelog, regenerated artifacts) and an annotated tag. Verify, commit, tag. Stops at "committed and tagged locally" — no push, no publish. The release-and-publish skill picks up from here. Distilled from the git_wrapup_instructions protocol.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.9"
7
+ version: "1.10"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -95,13 +95,13 @@ security: false # true ONLY for a security fix in this server's own source
95
95
  ---
96
96
  ```
97
97
 
98
- **Write `summary:` LAST, derived from the body you just wrote — never independently.** It is the line most readers see, and it propagates unedited to three further surfaces: the `CHANGELOG.md` rollup, the GitHub Release body, and the annotated tag (which cannot be edited once pushed). Written from recollection rather than from the body, it reliably names a mechanism that was never built or a target that was never fixed, while the body beside it stays correct. After writing it, re-read the body and confirm every claim in the summary appears there.
98
+ **Write `summary:` LAST, derived from the body you just wrote — never independently.** It is the line most readers see, and it propagates unedited to three further surfaces: the `CHANGELOG.md` rollup, the GitHub Release body, and the annotated tag (which cannot be edited once pushed). Written from recollection rather than from the body, it reliably names a mechanism that was never built or a target that was never fixed, while the body beside it stays correct. After writing it, re-read the body and confirm every claim in the summary appears there. Derived-from-the-body means the facts come from the body — not that every body item appears: the summary is the tag's theme line, and comma-stitching every change into an inventory near the 350-char cap is the failure mode.
99
99
 
100
100
  **`security:` is a source-code signal — not a dependency-CVE signal.** Set `security: true` only when this release fixes a vulnerability or adds hardening in code *this server ships*. A dependency or transitive CVE bump — even one that clears an advisory (`bun audit` going 1 → 0) — is routine maintenance: record it under `## Dependencies` with the advisory ID and leave the flag `false`. The `🛡️ Security` badge answers "does the server itself have a vuln"; a dep bump must not trip it.
101
101
 
102
102
  **Body:** Section order follows Keep a Changelog — Added / Changed / Deprecated / Removed / Fixed / Security. Include only sections with entries. Delete empty sections.
103
103
 
104
- **Tone:** Terse, fact-dense. Lead each bullet with the symbol or concept in **bold**. One sentence per bullet by default. See the authoring guide in `changelog/template.md` for full conventions.
104
+ **Tone:** Terse, fact-dense. Bullet = **symbol** + what changed + at most one consumer-facing caveat; one sentence by default, two max — a bullet past ~40 words or three sentences is wrong. The linked issue carries the why and the commit diff the how; the changelog names what changed and what a consumer does about it. Cut: history/justification narration, design-rationale defense, "X unchanged" clauses (short parenthetical only where a misread is likely), edge-case inventories. **Verified ≠ included** — the diff-is-source-of-truth rule bounds the truth of what you write, never the amount. Model length on `changelog/template.md`'s authoring guide, never on the previous entry (entries modeled on entries compound). `agent-notes` carries adoption steps only, never a second rendering of the body; a consequence shared by many bullets is stated once, not per bullet. Full conventions: the authoring guide in `changelog/template.md`.
105
105
 
106
106
  ### 5. Regenerate derived artifacts
107
107
 
@@ -4,7 +4,7 @@ description: >
4
4
  Finalize documentation and project metadata for a ship-ready MCP server. Use after implementation is complete, tests pass, and devcheck is clean. Safe to run at any stage — each step checks current state and only acts on what still needs work.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "2.11"
7
+ version: "2.12"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -31,7 +31,7 @@ Also check for stale framework references — servers migrated from `mcp-ts-temp
31
31
  Pick examples that:
32
32
 
33
33
  - Show the most common or important capability
34
- - Demonstrate any non-trivial patterns the server uses (e.g., `ctx.state`, `ctx.elicit`, `task: true`, services)
34
+ - Demonstrate any non-trivial patterns the server uses (e.g., `ctx.state`, `ctx.requestInput` / `ctx.inputs`, `ctx.enrich`, services)
35
35
  - Include a handler with real business logic, not just passthrough
36
36
 
37
37
  Keep 1-2 examples per primitive type (tool, resource, prompt). Don't list every definition — the README handles that.
@@ -46,7 +46,7 @@ Compare the structure diagram against the actual directory layout. If it still r
46
46
 
47
47
  ### 4. Update the Context Table
48
48
 
49
- Review the `ctx` feature table. Remove rows for features the server doesn't use (e.g., `ctx.elicit` if no tools call it). Add any custom context usage that's become important. The table should reflect what this server actually uses, not the full framework surface.
49
+ Review the `ctx` feature table. Remove rows for features the server doesn't use (e.g., `ctx.requestInput` / `ctx.inputs` if no handler collects extra input). Add any custom context usage that's become important. The table should reflect what this server actually uses, not the full framework surface.
50
50
 
51
51
  ### 5. Update Server Config Example
52
52
 
@@ -78,7 +78,7 @@ The header tagline must match the `package.json` `description`.
78
78
  | Version | info | Always — link to `CHANGELOG.md` |
79
79
  | License | info | Always |
80
80
  | Docker | info | Published to ghcr.io or Docker Hub |
81
- | MCP SDK | info | Always — show the `@modelcontextprotocol/sdk` version |
81
+ | MCP SDK | info | Always — show the `@modelcontextprotocol/server` version |
82
82
  | npm | info | Published to npm |
83
83
  | TypeScript | info | Always |
84
84
  | Bun | info | If using Bun (standard for this framework) |
@@ -4,7 +4,7 @@ description: >
4
4
  Ship a release end-to-end across every registry the project targets (npm, MCP Registry, GitHub Releases for `.mcpb` bundles, GHCR). Runs the final verification gate, pushes commits and tags, then publishes to each applicable destination. Assumes git wrapup (version bumps, changelog, commit, annotated tag) is already complete — this skill is the post-wrapup publish workflow. Retries transient network failures on publish steps; halts with a partial-state report when retries are exhausted or the failure is terminal.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "2.11"
7
+ version: "2.12"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -199,6 +199,8 @@ Skip any destination that was skipped in its step.
199
199
 
200
200
  Confirm each published artifact is actually live — don't rely on a successful push exit code alone. For each destination that succeeded:
201
201
 
202
+ **Never disable the sandbox to complete a verification.** A verification `curl` — most often the MCP Registry one — can come back blocked by a sandbox network restriction while npm and GHCR pass. Do **not** set `dangerouslyDisableSandbox` or otherwise route around the restriction; report the block and let the orchestrator verify from its own session. **A blocked verification is not evidence of a failed publish** — when the publish step itself reported success, report the two facts separately rather than treating the block as something to defeat.
203
+
202
204
  - **npm**: `npm view <package.json#name>@<version> version` — must return the version string
203
205
  - **MCP Registry**: `curl -s "https://registry.modelcontextprotocol.io/v0.1/servers/<mcpName>/versions/<version>"` — must return HTTP 200 with `server.version` matching `<version>` (`mcpName` is the `name` field from `server.json`; URL-encode `/` as `%2F`). The search endpoint (`/v0.1/servers?search=`) paginates and may not include the latest version for packages with many releases — always use the direct version lookup.
204
206
  - **GitHub Release**: `gh release view v<VERSION> -R <OWNER>/<REPO> --json assets --jq '.assets[].name'` — must list the `.mcpb` file
@@ -4,7 +4,7 @@ description: >
4
4
  File a bug or feature request against @cyanheads/mcp-ts-core when you hit a framework issue. Use when a builder, utility, context method, or config behaves contrary to the documented API — not for server-specific application bugs.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.8"
7
+ version: "1.9"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -15,7 +15,7 @@ You've isolated a problem to `@cyanheads/mcp-ts-core` itself — not your server
15
15
 
16
16
  - Framework builder (`tool()`, `resource()`, `prompt()`) rejects valid input or produces incorrect output
17
17
  - `createApp()` or `createWorkerHandler()` fails on a valid config
18
- - `Context` properties (`ctx.log`, `ctx.state`, `ctx.elicit`, etc.) behave contrary to docs
18
+ - `Context` properties (`ctx.log`, `ctx.state`, `ctx.inputs`, etc.) behave contrary to docs
19
19
  - A utility from `/utils`, `/errors`, `/auth`, `/storage`, `/services` returns wrong results or throws unexpectedly
20
20
  - Type exports are incorrect or missing (compile error on documented usage)
21
21
  - The definition linter (`bun run lint:mcp`) produces false positives or misses real violations
@@ -4,7 +4,7 @@ description: >
4
4
  File a bug or feature request against this MCP server's own repo. Use for server-specific issues — tool logic, service integrations, config problems, or domain bugs that aren't caused by the framework.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.6"
7
+ version: "1.7"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -277,7 +277,7 @@ Not sure where the bug lives? Quick checks:
277
277
  | Error in `src/mcp-server/tools/` or `src/services/` | | Yes |
278
278
  | Same bug reproduces with a bare `tool()` definition (no services) | Yes | |
279
279
  | Bug disappears when you swap in a dummy handler | | Yes |
280
- | `ctx.state`, `ctx.log`, `ctx.elicit` behave wrong on any tool | Yes | |
280
+ | `ctx.state`, `ctx.log`, `ctx.inputs` behave wrong on any tool | Yes | |
281
281
  | Only one specific tool/resource is affected | | Yes |
282
282
 
283
283
  When genuinely ambiguous, file against this server's repo and note that it might be a framework issue. The maintainer can transfer it upstream.
@@ -4,7 +4,7 @@ description: >
4
4
  Review an MCP server for common security gaps: LLM-facing surfaces as injection vector (tools, resources, prompts, descriptions), scope blast radius, destructive ops without consent, upstream auth shape, input sinks (URL / path / roots / shell / schema strictness / ReDoS), tenant isolation, leakage through errors and telemetry, unbounded resources, and HTTP-mode deployment surface. Use before a release, after a batch of handler changes, or when the user asks for a security review, audit, or hardening pass. Produces grouped findings and a numbered options list.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.6"
7
+ version: "1.7"
8
8
  audience: external
9
9
  type: audit
10
10
  ---
@@ -44,7 +44,7 @@ find src/mcp-server/prompts/definitions -name "*.prompt.ts" 2>/dev/null | sort
44
44
  find src/services -maxdepth 1 -mindepth 1 -type d | sort
45
45
  ```
46
46
 
47
- Note: tool / resource / prompt counts, auth mode, storage provider, upstream APIs, which tools have `destructiveHint`, which handlers use `ctx.elicit`, which services hold module-scope state, whether the server reads `roots`.
47
+ Note: tool / resource / prompt counts, auth mode, storage provider, upstream APIs, which tools have `destructiveHint`, which handlers request a consent round via `ctx.requestInput`, which services hold module-scope state, whether the server reads `roots`.
48
48
 
49
49
  **If transport is streamable HTTP or SSE**, also capture:
50
50
 
@@ -108,25 +108,27 @@ grep -rn "auth: \[" src/mcp-server/tools/definitions/
108
108
 
109
109
  **Smell:** every tool shares the same scope string. Or: `MCP_AUTH_DISABLE_SCOPE_CHECKS=true` set without a documented compensating ACL — confirm the deployment relies on a meaningful access control layer below the framework before approving.
110
110
 
111
- #### Axis 3 — Destructive ops without elicit
111
+ #### Axis 3 — Destructive ops without a consent round
112
112
 
113
- `ctx.elicit` moves consent off the LLM and onto the user. Destructive tools without it trust the LLM not to be tricked.
113
+ `ctx.requestInput` moves consent off the LLM and onto the user: the handler returns an `input_required` result and only runs the side effect once it is re-entered with an accepted response. Destructive tools without that round trust the LLM not to be tricked.
114
114
 
115
115
  **Look in:** handlers with `destructiveHint: true` or side-effecting verbs in names (`delete_*`, `send_*`, `pay_*`, `publish_*`, `drop_*`).
116
116
 
117
117
  ```bash
118
118
  grep -rn "destructiveHint" src/mcp-server/tools/definitions/
119
- grep -rn "ctx.elicit" src/mcp-server/tools/definitions/
119
+ grep -rn "ctx.requestInput\|ctx.inputs" src/mcp-server/tools/definitions/
120
120
  ```
121
121
 
122
122
  **Check:**
123
123
 
124
- - Each destructive handler calls `ctx.elicit` before the side effect?
125
- - Fallback when elicit is unavailable — **proceeds on the tool annotations, and says so in the tool description?** Do NOT require a refusal here: `ctx.elicit` is `undefined` on *every* Streamable HTTP request (the transport builds a fresh `McpServer` per request, so the SDK never records the client's capabilities — mcp-ts-core#312), so a refusing fallback makes the gated tools permanently unusable on the hosted transport. That is an outage, not a hardening. What IS in scope: a **declined or unparseable** elicit response must never proceed, and any doc claiming the gate protects "clients that support elicitation" must name the HTTP limitation or it overstates the control.
126
- - Elicit **response** validated against a Zod schema before use? The returned payload is LLM-mediated, not user-direct — "user confirmed" does not mean "user authored these exact fields."
127
- - Consent is scoped to the specific target (e.g., record ID rendered in the prompt), not a generic "proceed?"
124
+ - Each destructive handler reads `ctx.inputs` for the confirmation and returns `ctx.requestInput(...)` before the side effect?
125
+ - The confirmation **response** is validated — `ctx.inputs.accepted(key, Schema)` with a schema, not the bare overload. The SDK never re-validates a response against the schema its request advertised, and the payload is LLM-mediated: "user confirmed" does not mean "user authored these exact fields."
126
+ - A **declined or cancelled** response is terminal — checked via `ctx.inputs.view(key)` and thrown on, never re-asked (a re-ask loops until the round budget runs out) and never treated as consent.
127
+ - Consent is scoped to the specific target (e.g., record ID rendered in the message), not a generic "proceed?"
128
+ - Any `requestState` carried across rounds is integrity-protected if it influences authorization, resource access, or which target gets mutated. It round-trips through the client and comes back attacker-controlled; the SDK does not sign or verify it.
129
+ - **The weak point is answerability, not availability.** `ctx.requestInput` is present on every transport and both protocol eras — the 2025-era shim issues the real `elicitation/create` round trip, the 2026-07-28 client fulfils the embedded request directly. A client that never retries simply leaves the destructive step un-run, which fails safe. Keep `destructiveHint: true` so client-side approval flows still surface the risk, and do not accept "proceed anyway when the round is unavailable" as a fallback — there is no such state to detect.
128
130
 
129
- **Smell:** `destructiveHint: true` file with no `ctx.elicit?.(...)` in it. Or: `const { confirmed } = await ctx.elicit(...)` without a schema — `confirmed` could be anything.
131
+ **Smell:** `destructiveHint: true` file with no `ctx.requestInput` in it. Or `ctx.inputs.accepted('confirm')` with no schema argument — the content could be anything. Or a handler that re-issues the same request after a `decline`.
130
132
 
131
133
  #### Axis 4 — Upstream auth shape
132
134
 
@@ -295,7 +297,7 @@ Group by severity. Each 3–5 lines.
295
297
  | Severity | Meaning |
296
298
  |:---------|:--------|
297
299
  | **critical** | Exploitable now: auth bypass, exfiltration, arbitrary code/file/network access |
298
- | **high** | Structural gap with clear attacker benefit even without immediate PoC (destructive op without elicit, admin scope on read tool, SSRF-capable URL input) |
300
+ | **high** | Structural gap with clear attacker benefit even without immediate PoC (destructive op without a consent round, admin scope on read tool, SSRF-capable URL input) |
299
301
  | **medium** | Defense-in-depth gap weakening a boundary (missing per-tenant rate limit, error carries upstream response) |
300
302
  | **low** | Hardening / polish (tighter output schema, narrower error data, minor comment) |
301
303
 
@@ -314,7 +316,7 @@ Numbered, cherry-pickable.
314
316
 
315
317
  ```
316
318
  1. Add SSRF guard to `fetch_url.tool.ts` — block private IPs + non-http schemes (critical, #1)
317
- 2. Gate `delete_record.tool.ts` behind `ctx.elicit` (high, #3)
319
+ 2. Gate `delete_record.tool.ts` behind a `ctx.requestInput` confirmation round (high, #3)
318
320
  3. Split `admin` into `record:read` + `record:write` across 4 tools (high, #4)
319
321
  4. Move `const tokenCache = new Map()` out of module scope in `auth-service.ts` (medium, #7)
320
322
  5. Cap pagination loop in `list_all_tickets` at 1000 items (medium, #9)
@@ -328,12 +330,12 @@ End with:
328
330
  ## Checklist
329
331
 
330
332
  - [ ] Scope confirmed (whole server / module / diff)
331
- - [ ] Map built: tools / resources / prompts, services, upstream APIs, auth mode, elicit / roots usage
333
+ - [ ] Map built: tools / resources / prompts, services, upstream APIs, auth mode, consent-round / roots usage
332
334
  - [ ] Deployment surface reviewed (if HTTP): bind address, Origin allowlist, session ID, unauth routes, auth-spec compliance
333
335
  - [ ] `fuzzTool` started in parallel
334
336
  - [ ] Axis 1 — LLM-facing surfaces (tool / resource / prompt output + descriptions) framed and static
335
337
  - [ ] Axis 2 — scope granularity audited
336
- - [ ] Axis 3 — destructive ops verified to elicit, elicit response schema-validated
338
+ - [ ] Axis 3 — destructive ops verified to gate on a `ctx.requestInput` round, the response schema-validated, decline/cancel terminal
337
339
  - [ ] Axis 4 — upstream auth + token passthrough reviewed
338
340
  - [ ] Axis 5 — input sinks (URL / path / roots / shell / proto / schema strictness / ReDoS) checked
339
341
  - [ ] Axis 6 — tenant isolation: module-scope state swept