@cyanheads/mcp-ts-core 0.11.5 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (389) hide show
  1. package/AGENTS.md +27 -23
  2. package/CLAUDE.md +27 -23
  3. package/README.md +10 -10
  4. package/biome.json +1 -1
  5. package/changelog/0.12.x/0.12.0.md +96 -0
  6. package/changelog/0.12.x/0.12.1.md +26 -0
  7. package/dist/cli/init.js +1 -1
  8. package/dist/cli/init.js.map +1 -1
  9. package/dist/config/index.d.ts +9 -18
  10. package/dist/config/index.d.ts.map +1 -1
  11. package/dist/config/index.js +21 -28
  12. package/dist/config/index.js.map +1 -1
  13. package/dist/core/app.d.ts +27 -7
  14. package/dist/core/app.d.ts.map +1 -1
  15. package/dist/core/app.js +28 -28
  16. package/dist/core/app.js.map +1 -1
  17. package/dist/core/context.d.ts +90 -57
  18. package/dist/core/context.d.ts.map +1 -1
  19. package/dist/core/context.js +32 -33
  20. package/dist/core/context.js.map +1 -1
  21. package/dist/core/gcPressure.d.ts.map +1 -1
  22. package/dist/core/gcPressure.js +3 -4
  23. package/dist/core/gcPressure.js.map +1 -1
  24. package/dist/core/index.d.ts +6 -7
  25. package/dist/core/index.d.ts.map +1 -1
  26. package/dist/core/index.js +10 -2
  27. package/dist/core/index.js.map +1 -1
  28. package/dist/core/serverManifest.d.ts +6 -4
  29. package/dist/core/serverManifest.d.ts.map +1 -1
  30. package/dist/core/serverManifest.js +37 -14
  31. package/dist/core/serverManifest.js.map +1 -1
  32. package/dist/core/worker.d.ts.map +1 -1
  33. package/dist/core/worker.js +34 -37
  34. package/dist/core/worker.js.map +1 -1
  35. package/dist/linter/rules/enrichment-rules.d.ts.map +1 -1
  36. package/dist/linter/rules/enrichment-rules.js +5 -2
  37. package/dist/linter/rules/enrichment-rules.js.map +1 -1
  38. package/dist/linter/rules/portability-rules.d.ts +4 -2
  39. package/dist/linter/rules/portability-rules.d.ts.map +1 -1
  40. package/dist/linter/rules/portability-rules.js +33 -4
  41. package/dist/linter/rules/portability-rules.js.map +1 -1
  42. package/dist/linter/rules/schema-rules.d.ts +10 -3
  43. package/dist/linter/rules/schema-rules.d.ts.map +1 -1
  44. package/dist/linter/rules/schema-rules.js +52 -25
  45. package/dist/linter/rules/schema-rules.js.map +1 -1
  46. package/dist/linter/rules/tool-rules.d.ts.map +1 -1
  47. package/dist/linter/rules/tool-rules.js +5 -2
  48. package/dist/linter/rules/tool-rules.js.map +1 -1
  49. package/dist/linter/types.d.ts +2 -1
  50. package/dist/linter/types.d.ts.map +1 -1
  51. package/dist/logs/combined.log +10 -22
  52. package/dist/logs/error.log +6 -18
  53. package/dist/mcp-server/inputRequired.d.ts +52 -0
  54. package/dist/mcp-server/inputRequired.d.ts.map +1 -0
  55. package/dist/mcp-server/inputRequired.js +76 -0
  56. package/dist/mcp-server/inputRequired.js.map +1 -0
  57. package/dist/mcp-server/notifications.d.ts +103 -34
  58. package/dist/mcp-server/notifications.d.ts.map +1 -1
  59. package/dist/mcp-server/notifications.js +104 -29
  60. package/dist/mcp-server/notifications.js.map +1 -1
  61. package/dist/mcp-server/prompts/prompt-registration.d.ts +2 -2
  62. package/dist/mcp-server/prompts/prompt-registration.d.ts.map +1 -1
  63. package/dist/mcp-server/prompts/prompt-registration.js +15 -5
  64. package/dist/mcp-server/prompts/prompt-registration.js.map +1 -1
  65. package/dist/mcp-server/prompts/utils/promptDefinition.d.ts +1 -1
  66. package/dist/mcp-server/prompts/utils/promptDefinition.d.ts.map +1 -1
  67. package/dist/mcp-server/resources/resource-registration.d.ts +3 -3
  68. package/dist/mcp-server/resources/resource-registration.d.ts.map +1 -1
  69. package/dist/mcp-server/resources/resource-registration.js +9 -15
  70. package/dist/mcp-server/resources/resource-registration.js.map +1 -1
  71. package/dist/mcp-server/resources/resourceSubscriptions.d.ts +45 -0
  72. package/dist/mcp-server/resources/resourceSubscriptions.d.ts.map +1 -0
  73. package/dist/mcp-server/resources/resourceSubscriptions.js +51 -0
  74. package/dist/mcp-server/resources/resourceSubscriptions.js.map +1 -0
  75. package/dist/mcp-server/resources/utils/resourceDefinition.d.ts +3 -5
  76. package/dist/mcp-server/resources/utils/resourceDefinition.d.ts.map +1 -1
  77. package/dist/mcp-server/resources/utils/resourceDefinition.js +1 -1
  78. package/dist/mcp-server/resources/utils/resourceDefinition.js.map +1 -1
  79. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts +18 -17
  80. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
  81. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +39 -39
  82. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
  83. package/dist/mcp-server/server.d.ts +20 -21
  84. package/dist/mcp-server/server.d.ts.map +1 -1
  85. package/dist/mcp-server/server.js +42 -28
  86. package/dist/mcp-server/server.js.map +1 -1
  87. package/dist/mcp-server/tools/tool-registration.d.ts +11 -33
  88. package/dist/mcp-server/tools/tool-registration.d.ts.map +1 -1
  89. package/dist/mcp-server/tools/tool-registration.js +18 -243
  90. package/dist/mcp-server/tools/tool-registration.js.map +1 -1
  91. package/dist/mcp-server/tools/utils/schemaShape.d.ts +27 -0
  92. package/dist/mcp-server/tools/utils/schemaShape.d.ts.map +1 -0
  93. package/dist/mcp-server/tools/utils/schemaShape.js +43 -0
  94. package/dist/mcp-server/tools/utils/schemaShape.js.map +1 -0
  95. package/dist/mcp-server/tools/utils/toolDefinition.d.ts +28 -8
  96. package/dist/mcp-server/tools/utils/toolDefinition.d.ts.map +1 -1
  97. package/dist/mcp-server/tools/utils/toolDefinition.js +63 -1
  98. package/dist/mcp-server/tools/utils/toolDefinition.js.map +1 -1
  99. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +54 -20
  100. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
  101. package/dist/mcp-server/tools/utils/toolHandlerFactory.js +156 -44
  102. package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
  103. package/dist/mcp-server/transports/ITransport.d.ts +2 -2
  104. package/dist/mcp-server/transports/ITransport.d.ts.map +1 -1
  105. package/dist/mcp-server/transports/auth/authFactory.js +1 -1
  106. package/dist/mcp-server/transports/auth/authFactory.js.map +1 -1
  107. package/dist/mcp-server/transports/auth/lib/authTypes.d.ts +1 -1
  108. package/dist/mcp-server/transports/auth/lib/authTypes.d.ts.map +1 -1
  109. package/dist/mcp-server/transports/auth/lib/authUtils.d.ts.map +1 -1
  110. package/dist/mcp-server/transports/auth/lib/authUtils.js +5 -13
  111. package/dist/mcp-server/transports/auth/lib/authUtils.js.map +1 -1
  112. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js +5 -11
  113. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js.map +1 -1
  114. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.d.ts.map +1 -1
  115. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js +9 -22
  116. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js.map +1 -1
  117. package/dist/mcp-server/transports/heartbeat.d.ts.map +1 -1
  118. package/dist/mcp-server/transports/heartbeat.js +4 -8
  119. package/dist/mcp-server/transports/heartbeat.js.map +1 -1
  120. package/dist/mcp-server/transports/http/eventStore.d.ts +57 -0
  121. package/dist/mcp-server/transports/http/eventStore.d.ts.map +1 -0
  122. package/dist/mcp-server/transports/http/eventStore.js +114 -0
  123. package/dist/mcp-server/transports/http/eventStore.js.map +1 -0
  124. package/dist/mcp-server/transports/http/httpErrorHandler.d.ts.map +1 -1
  125. package/dist/mcp-server/transports/http/httpErrorHandler.js +6 -24
  126. package/dist/mcp-server/transports/http/httpErrorHandler.js.map +1 -1
  127. package/dist/mcp-server/transports/http/httpServer.d.ts +4 -3
  128. package/dist/mcp-server/transports/http/httpServer.d.ts.map +1 -1
  129. package/dist/mcp-server/transports/http/httpServer.js +16 -33
  130. package/dist/mcp-server/transports/http/httpServer.js.map +1 -1
  131. package/dist/mcp-server/transports/http/httpTransport.d.ts +30 -8
  132. package/dist/mcp-server/transports/http/httpTransport.d.ts.map +1 -1
  133. package/dist/mcp-server/transports/http/httpTransport.js +180 -374
  134. package/dist/mcp-server/transports/http/httpTransport.js.map +1 -1
  135. package/dist/mcp-server/transports/http/landing-page/assets/styles.d.ts.map +1 -1
  136. package/dist/mcp-server/transports/http/landing-page/assets/styles.js +0 -1
  137. package/dist/mcp-server/transports/http/landing-page/assets/styles.js.map +1 -1
  138. package/dist/mcp-server/transports/http/landing-page/handler.d.ts.map +1 -1
  139. package/dist/mcp-server/transports/http/landing-page/handler.js +4 -8
  140. package/dist/mcp-server/transports/http/landing-page/handler.js.map +1 -1
  141. package/dist/mcp-server/transports/http/landing-page/sections/tools.js +22 -4
  142. package/dist/mcp-server/transports/http/landing-page/sections/tools.js.map +1 -1
  143. package/dist/mcp-server/transports/http/protectedResourceMetadata.d.ts.map +1 -1
  144. package/dist/mcp-server/transports/http/protectedResourceMetadata.js +2 -6
  145. package/dist/mcp-server/transports/http/protectedResourceMetadata.js.map +1 -1
  146. package/dist/mcp-server/transports/http/robotsTxt.js +2 -2
  147. package/dist/mcp-server/transports/http/robotsTxt.js.map +1 -1
  148. package/dist/mcp-server/transports/http/serverCard.d.ts.map +1 -1
  149. package/dist/mcp-server/transports/http/serverCard.js +2 -6
  150. package/dist/mcp-server/transports/http/serverCard.js.map +1 -1
  151. package/dist/mcp-server/transports/http/sessionStore.d.ts +46 -66
  152. package/dist/mcp-server/transports/http/sessionStore.d.ts.map +1 -1
  153. package/dist/mcp-server/transports/http/sessionStore.js +140 -238
  154. package/dist/mcp-server/transports/http/sessionStore.js.map +1 -1
  155. package/dist/mcp-server/transports/manager.d.ts +21 -4
  156. package/dist/mcp-server/transports/manager.d.ts.map +1 -1
  157. package/dist/mcp-server/transports/manager.js +40 -34
  158. package/dist/mcp-server/transports/manager.js.map +1 -1
  159. package/dist/mcp-server/transports/stdio/stdioTransport.d.ts +24 -31
  160. package/dist/mcp-server/transports/stdio/stdioTransport.d.ts.map +1 -1
  161. package/dist/mcp-server/transports/stdio/stdioTransport.js +23 -38
  162. package/dist/mcp-server/transports/stdio/stdioTransport.js.map +1 -1
  163. package/dist/mcp-server/types.d.ts +25 -0
  164. package/dist/mcp-server/types.d.ts.map +1 -0
  165. package/dist/mcp-server/types.js +10 -0
  166. package/dist/mcp-server/types.js.map +1 -0
  167. package/dist/services/canvas/core/CanvasInstance.d.ts +2 -2
  168. package/dist/services/canvas/core/CanvasInstance.d.ts.map +1 -1
  169. package/dist/services/canvas/core/CanvasInstance.js.map +1 -1
  170. package/dist/services/canvas/core/CanvasRegistry.d.ts +4 -4
  171. package/dist/services/canvas/core/CanvasRegistry.d.ts.map +1 -1
  172. package/dist/services/canvas/core/CanvasRegistry.js +8 -18
  173. package/dist/services/canvas/core/CanvasRegistry.js.map +1 -1
  174. package/dist/services/canvas/core/DataCanvas.d.ts +5 -5
  175. package/dist/services/canvas/core/DataCanvas.d.ts.map +1 -1
  176. package/dist/services/canvas/core/DataCanvas.js +2 -3
  177. package/dist/services/canvas/core/DataCanvas.js.map +1 -1
  178. package/dist/services/canvas/core/IDataCanvasProvider.d.ts +11 -11
  179. package/dist/services/canvas/core/IDataCanvasProvider.d.ts.map +1 -1
  180. package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts +11 -11
  181. package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
  182. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +4 -10
  183. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
  184. package/dist/services/llm/providers/openrouter.provider.d.ts.map +1 -1
  185. package/dist/services/llm/providers/openrouter.provider.js +2 -5
  186. package/dist/services/llm/providers/openrouter.provider.js.map +1 -1
  187. package/dist/services/mirror/core/defineMirror.js +1 -1
  188. package/dist/services/mirror/core/defineMirror.js.map +1 -1
  189. package/dist/services/mirror/types.d.ts +5 -5
  190. package/dist/services/mirror/types.d.ts.map +1 -1
  191. package/dist/services/speech/providers/elevenlabs.provider.js +1 -1
  192. package/dist/services/speech/providers/elevenlabs.provider.js.map +1 -1
  193. package/dist/services/speech/providers/whisper.provider.js +1 -1
  194. package/dist/services/speech/providers/whisper.provider.js.map +1 -1
  195. package/dist/storage/core/StorageService.d.ts +1 -1
  196. package/dist/storage/core/StorageService.d.ts.map +1 -1
  197. package/dist/storage/core/StorageService.js +12 -20
  198. package/dist/storage/core/StorageService.js.map +1 -1
  199. package/dist/storage/core/storageFactory.d.ts.map +1 -1
  200. package/dist/storage/core/storageFactory.js.map +1 -1
  201. package/dist/storage/core/storageValidation.d.ts +1 -1
  202. package/dist/storage/core/storageValidation.d.ts.map +1 -1
  203. package/dist/storage/core/storageValidation.js +2 -2
  204. package/dist/storage/core/storageValidation.js.map +1 -1
  205. package/dist/storage/providers/cloudflare/d1Provider.d.ts +1 -1
  206. package/dist/storage/providers/cloudflare/d1Provider.d.ts.map +1 -1
  207. package/dist/storage/providers/cloudflare/d1Provider.js +4 -12
  208. package/dist/storage/providers/cloudflare/d1Provider.js.map +1 -1
  209. package/dist/storage/providers/cloudflare/kvProvider.d.ts +1 -1
  210. package/dist/storage/providers/cloudflare/kvProvider.d.ts.map +1 -1
  211. package/dist/storage/providers/cloudflare/kvProvider.js +3 -8
  212. package/dist/storage/providers/cloudflare/kvProvider.js.map +1 -1
  213. package/dist/storage/providers/cloudflare/r2Provider.d.ts +1 -1
  214. package/dist/storage/providers/cloudflare/r2Provider.d.ts.map +1 -1
  215. package/dist/storage/providers/cloudflare/r2Provider.js +3 -8
  216. package/dist/storage/providers/cloudflare/r2Provider.js.map +1 -1
  217. package/dist/storage/providers/inMemory/inMemoryProvider.d.ts +1 -1
  218. package/dist/storage/providers/inMemory/inMemoryProvider.d.ts.map +1 -1
  219. package/dist/storage/providers/inMemory/inMemoryProvider.js +2 -4
  220. package/dist/storage/providers/inMemory/inMemoryProvider.js.map +1 -1
  221. package/dist/testing/fuzz.d.ts +7 -4
  222. package/dist/testing/fuzz.d.ts.map +1 -1
  223. package/dist/testing/fuzz.js +13 -5
  224. package/dist/testing/fuzz.js.map +1 -1
  225. package/dist/testing/index.d.ts +35 -12
  226. package/dist/testing/index.d.ts.map +1 -1
  227. package/dist/testing/index.js +53 -38
  228. package/dist/testing/index.js.map +1 -1
  229. package/dist/testing/vitest.d.ts +1 -1
  230. package/dist/testing/vitest.d.ts.map +1 -1
  231. package/dist/types-global/errors.d.ts +28 -15
  232. package/dist/types-global/errors.d.ts.map +1 -1
  233. package/dist/types-global/errors.js +8 -1
  234. package/dist/types-global/errors.js.map +1 -1
  235. package/dist/utils/formatting/diffFormatter.d.ts +5 -5
  236. package/dist/utils/formatting/diffFormatter.d.ts.map +1 -1
  237. package/dist/utils/formatting/diffFormatter.js +7 -20
  238. package/dist/utils/formatting/diffFormatter.js.map +1 -1
  239. package/dist/utils/formatting/tableFormatter.d.ts +3 -3
  240. package/dist/utils/formatting/tableFormatter.d.ts.map +1 -1
  241. package/dist/utils/formatting/tableFormatter.js +6 -17
  242. package/dist/utils/formatting/tableFormatter.js.map +1 -1
  243. package/dist/utils/formatting/treeFormatter.d.ts +3 -3
  244. package/dist/utils/formatting/treeFormatter.d.ts.map +1 -1
  245. package/dist/utils/formatting/treeFormatter.js +5 -18
  246. package/dist/utils/formatting/treeFormatter.js.map +1 -1
  247. package/dist/utils/index.d.ts +4 -2
  248. package/dist/utils/index.d.ts.map +1 -1
  249. package/dist/utils/index.js +2 -2
  250. package/dist/utils/index.js.map +1 -1
  251. package/dist/utils/internal/error-handler/errorHandler.d.ts +1 -1
  252. package/dist/utils/internal/error-handler/errorHandler.d.ts.map +1 -1
  253. package/dist/utils/internal/error-handler/errorHandler.js +23 -11
  254. package/dist/utils/internal/error-handler/errorHandler.js.map +1 -1
  255. package/dist/utils/internal/error-handler/types.d.ts +10 -13
  256. package/dist/utils/internal/error-handler/types.d.ts.map +1 -1
  257. package/dist/utils/internal/logger.d.ts +3 -3
  258. package/dist/utils/internal/logger.d.ts.map +1 -1
  259. package/dist/utils/internal/logger.js +11 -5
  260. package/dist/utils/internal/logger.js.map +1 -1
  261. package/dist/utils/internal/performance.d.ts +13 -5
  262. package/dist/utils/internal/performance.d.ts.map +1 -1
  263. package/dist/utils/internal/performance.js +76 -17
  264. package/dist/utils/internal/performance.js.map +1 -1
  265. package/dist/utils/internal/requestContext.d.ts +92 -55
  266. package/dist/utils/internal/requestContext.d.ts.map +1 -1
  267. package/dist/utils/internal/requestContext.js +120 -40
  268. package/dist/utils/internal/requestContext.js.map +1 -1
  269. package/dist/utils/metrics/tokenCounter.d.ts +3 -3
  270. package/dist/utils/metrics/tokenCounter.d.ts.map +1 -1
  271. package/dist/utils/metrics/tokenCounter.js.map +1 -1
  272. package/dist/utils/network/fetchWithTimeout.d.ts +2 -2
  273. package/dist/utils/network/fetchWithTimeout.d.ts.map +1 -1
  274. package/dist/utils/network/fetchWithTimeout.js +8 -24
  275. package/dist/utils/network/fetchWithTimeout.js.map +1 -1
  276. package/dist/utils/network/retry.d.ts +2 -2
  277. package/dist/utils/network/retry.d.ts.map +1 -1
  278. package/dist/utils/overflow/outlineOnOverflow.d.ts +1 -1
  279. package/dist/utils/overflow/outlineOnOverflow.d.ts.map +1 -1
  280. package/dist/utils/pagination/pagination.d.ts +3 -3
  281. package/dist/utils/pagination/pagination.d.ts.map +1 -1
  282. package/dist/utils/pagination/pagination.js +3 -3
  283. package/dist/utils/pagination/pagination.js.map +1 -1
  284. package/dist/utils/parsing/csvParser.d.ts +2 -2
  285. package/dist/utils/parsing/csvParser.d.ts.map +1 -1
  286. package/dist/utils/parsing/csvParser.js +4 -8
  287. package/dist/utils/parsing/csvParser.js.map +1 -1
  288. package/dist/utils/parsing/dateParser.d.ts +3 -3
  289. package/dist/utils/parsing/dateParser.d.ts.map +1 -1
  290. package/dist/utils/parsing/dateParser.js +3 -2
  291. package/dist/utils/parsing/dateParser.js.map +1 -1
  292. package/dist/utils/parsing/frontmatterParser.d.ts +2 -2
  293. package/dist/utils/parsing/frontmatterParser.d.ts.map +1 -1
  294. package/dist/utils/parsing/frontmatterParser.js +7 -10
  295. package/dist/utils/parsing/frontmatterParser.js.map +1 -1
  296. package/dist/utils/parsing/htmlExtractor.d.ts +2 -2
  297. package/dist/utils/parsing/htmlExtractor.d.ts.map +1 -1
  298. package/dist/utils/parsing/htmlExtractor.js +6 -11
  299. package/dist/utils/parsing/htmlExtractor.js.map +1 -1
  300. package/dist/utils/parsing/jsonParser.d.ts +2 -2
  301. package/dist/utils/parsing/jsonParser.d.ts.map +1 -1
  302. package/dist/utils/parsing/jsonParser.js +4 -8
  303. package/dist/utils/parsing/jsonParser.js.map +1 -1
  304. package/dist/utils/parsing/pdfParser.d.ts +10 -10
  305. package/dist/utils/parsing/pdfParser.d.ts.map +1 -1
  306. package/dist/utils/parsing/pdfParser.js +26 -89
  307. package/dist/utils/parsing/pdfParser.js.map +1 -1
  308. package/dist/utils/parsing/xmlParser.d.ts +2 -2
  309. package/dist/utils/parsing/xmlParser.d.ts.map +1 -1
  310. package/dist/utils/parsing/xmlParser.js +4 -8
  311. package/dist/utils/parsing/xmlParser.js.map +1 -1
  312. package/dist/utils/parsing/yamlParser.d.ts +2 -2
  313. package/dist/utils/parsing/yamlParser.d.ts.map +1 -1
  314. package/dist/utils/parsing/yamlParser.js +4 -8
  315. package/dist/utils/parsing/yamlParser.js.map +1 -1
  316. package/dist/utils/scheduling/scheduler.d.ts.map +1 -1
  317. package/dist/utils/scheduling/scheduler.js +5 -6
  318. package/dist/utils/scheduling/scheduler.js.map +1 -1
  319. package/dist/utils/security/rateLimiter.d.ts +2 -2
  320. package/dist/utils/security/rateLimiter.d.ts.map +1 -1
  321. package/dist/utils/security/rateLimiter.js +1 -1
  322. package/dist/utils/security/rateLimiter.js.map +1 -1
  323. package/dist/utils/telemetry/attributes.d.ts +23 -7
  324. package/dist/utils/telemetry/attributes.d.ts.map +1 -1
  325. package/dist/utils/telemetry/attributes.js +23 -10
  326. package/dist/utils/telemetry/attributes.js.map +1 -1
  327. package/dist/utils/telemetry/trace.d.ts +3 -3
  328. package/dist/utils/telemetry/trace.d.ts.map +1 -1
  329. package/dist/utils/telemetry/trace.js +4 -2
  330. package/dist/utils/telemetry/trace.js.map +1 -1
  331. package/package.json +22 -26
  332. package/scripts/lint-mcp.ts +1 -3
  333. package/scripts/lint-packaging.ts +1 -1
  334. package/skills/add-service/SKILL.md +2 -2
  335. package/skills/add-test/SKILL.md +42 -20
  336. package/skills/add-tool/SKILL.md +103 -19
  337. package/skills/api-config/SKILL.md +4 -11
  338. package/skills/api-context/SKILL.md +216 -120
  339. package/skills/api-linter/SKILL.md +16 -3
  340. package/skills/api-telemetry/SKILL.md +10 -12
  341. package/skills/api-testing/SKILL.md +53 -52
  342. package/skills/api-workers/SKILL.md +9 -1
  343. package/skills/code-simplifier/SKILL.md +2 -2
  344. package/skills/design-mcp-server/SKILL.md +31 -17
  345. package/skills/field-test/SKILL.md +4 -2
  346. package/skills/polish-docs-meta/SKILL.md +1 -1
  347. package/skills/polish-docs-meta/references/agent-protocol.md +2 -2
  348. package/skills/polish-docs-meta/references/readme.md +1 -1
  349. package/skills/release-and-publish/SKILL.md +3 -1
  350. package/skills/report-issue-framework/SKILL.md +2 -2
  351. package/skills/report-issue-local/SKILL.md +2 -2
  352. package/skills/security-pass/SKILL.md +16 -14
  353. package/skills/tool-defs-analysis/SKILL.md +15 -5
  354. package/templates/.env.example +4 -0
  355. package/templates/AGENTS.md +6 -6
  356. package/templates/CLAUDE.md +6 -6
  357. package/templates/tests/resources/echo.resource.test.ts +16 -9
  358. package/dist/mcp-server/elicitation.d.ts +0 -64
  359. package/dist/mcp-server/elicitation.d.ts.map +0 -1
  360. package/dist/mcp-server/elicitation.js +0 -81
  361. package/dist/mcp-server/elicitation.js.map +0 -1
  362. package/dist/mcp-server/protocolSession.d.ts +0 -34
  363. package/dist/mcp-server/protocolSession.d.ts.map +0 -1
  364. package/dist/mcp-server/protocolSession.js +0 -13
  365. package/dist/mcp-server/protocolSession.js.map +0 -1
  366. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.d.ts +0 -53
  367. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.d.ts.map +0 -1
  368. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.js +0 -65
  369. package/dist/mcp-server/tasks/core/evictingTaskMessageQueue.js.map +0 -1
  370. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.d.ts +0 -70
  371. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.d.ts.map +0 -1
  372. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.js +0 -130
  373. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.js.map +0 -1
  374. package/dist/mcp-server/tasks/core/storageBackedTaskStore.d.ts +0 -109
  375. package/dist/mcp-server/tasks/core/storageBackedTaskStore.d.ts.map +0 -1
  376. package/dist/mcp-server/tasks/core/storageBackedTaskStore.js +0 -209
  377. package/dist/mcp-server/tasks/core/storageBackedTaskStore.js.map +0 -1
  378. package/dist/mcp-server/tasks/core/taskManager.d.ts +0 -91
  379. package/dist/mcp-server/tasks/core/taskManager.d.ts.map +0 -1
  380. package/dist/mcp-server/tasks/core/taskManager.js +0 -210
  381. package/dist/mcp-server/tasks/core/taskManager.js.map +0 -1
  382. package/dist/mcp-server/tasks/core/taskTypes.d.ts +0 -18
  383. package/dist/mcp-server/tasks/core/taskTypes.d.ts.map +0 -1
  384. package/dist/mcp-server/tasks/core/taskTypes.js +0 -20
  385. package/dist/mcp-server/tasks/core/taskTypes.js.map +0 -1
  386. package/dist/mcp-server/tasks/utils/taskToolDefinition.d.ts +0 -101
  387. package/dist/mcp-server/tasks/utils/taskToolDefinition.d.ts.map +0 -1
  388. package/dist/mcp-server/tasks/utils/taskToolDefinition.js +0 -14
  389. package/dist/mcp-server/tasks/utils/taskToolDefinition.js.map +0 -1
@@ -1,17 +1,17 @@
1
1
  ---
2
2
  name: api-context
3
3
  description: >
4
- Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.progress`, `ctx.enrich`, `ctx.content`), and when to use each.
4
+ Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, its `RequestContext` base, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.requestInput`, `ctx.inputs`, `ctx.enrich`, `ctx.content`), and when to use each.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.11"
7
+ version: "2.1"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
11
11
 
12
12
  ## Overview
13
13
 
14
- Every tool and resource handler receives a single `Context` (`ctx`) argument. It provides request identity, structured logging, tenant-scoped storage, optional protocol capabilities (elicitation), cancellation, and task progress — all auto-correlated to the current request.
14
+ Every tool and resource handler receives a single `Context` (`ctx`) argument. It provides request identity, structured logging, tenant-scoped storage, multi-round-trip input collection, and cancellation — all auto-correlated to the current request.
15
15
 
16
16
  The framework auto-instruments every handler call (OTel span, duration, payload metrics). Use `ctx.log` for domain-specific logging and `ctx.state` for storage inside handlers. Use the global `logger` and `StorageService` directly only in lifecycle/background code (`setup()`, services).
17
17
 
@@ -22,24 +22,28 @@ The framework auto-instruments every handler call (OTel span, duration, payload
22
22
  ```ts
23
23
  import type { Context } from '@cyanheads/mcp-ts-core';
24
24
 
25
- interface Context {
26
- // Identity & tracing
25
+ interface Context extends RequestContext {
26
+ // Identity & tracing (inherited from RequestContext — see § RequestContext)
27
27
  readonly requestId: string; // Unique per request, auto-generated
28
28
  readonly timestamp: string; // ISO 8601 request start time
29
29
  readonly tenantId?: string; // JWT 'tid' claim; 'default' for stdio and HTTP+MCP_AUTH_MODE=none
30
30
  readonly sessionId?: string; // Mcp-Session-Id (HTTP stateful/auto); undefined elsewhere unless opted in
31
- readonly traceId?: string; // OTEL trace ID (present when OTEL enabled)
32
- readonly spanId?: string; // OTEL span ID (present when OTEL enabled)
31
+ readonly traceId?: string; // Trace containing this handler execution
32
+ readonly spanId?: string; // The handler's own execution span
33
33
  readonly auth?: AuthContext; // Parsed auth claims (clientId, scopes, sub)
34
+ readonly operation?: string; // Label for the operation this context belongs to
35
+ readonly extra?: Readonly<Record<string, unknown>>; // Correlation bag — the one open field
34
36
 
35
- // Structured logging — auto-includes requestId, traceId, tenantId
37
+ // Structured logging — auto-includes requestId, traceId, tenantId.
38
+ // Dual-sink: Pino on the server, plus notifications/message to the client.
36
39
  readonly log: ContextLogger;
37
40
 
38
41
  // Tenant-scoped key-value storage
39
42
  readonly state: ContextState;
40
43
 
41
- // Optional protocol capabilities (undefined when client doesn't support them)
42
- readonly elicit?: ElicitFn; // callable (message, schema) for form mode + .url(message, url) — see § ctx.elicit
44
+ // Multi-round-trip input — always present, both eras (see § ctx.requestInput)
45
+ readonly requestInput: RequestInputFn; // (spec) => never — suspends and asks the caller
46
+ readonly inputs: ContextInputs; // reader over a retried request's responses
43
47
 
44
48
  // List-changed / resource-updated notifications — wired in every handler ctx;
45
49
  // delivery is request-scoped (see § list-changed notifications)
@@ -51,9 +55,6 @@ interface Context {
51
55
  // Cancellation
52
56
  readonly signal: AbortSignal;
53
57
 
54
- // Task progress — present only when tool is defined with task: true
55
- readonly progress?: ContextProgress;
56
-
57
58
  // Raw URI — present only for resource handlers
58
59
  readonly uri?: URL;
59
60
 
@@ -85,16 +86,71 @@ interface Context {
85
86
  | `timestamp` | Yes | ISO 8601, request start |
86
87
  | `tenantId` | Stdio and HTTP+`MCP_AUTH_MODE=none` (as `'default'`); JWT `tid` claim in HTTP+`jwt`/`oauth` | JWT / single-tenant default |
87
88
  | `sessionId` | HTTP `stateful` / `auto` mode; undefined for stdio and stateless HTTP unless opted in | `Mcp-Session-Id` header (or server-minted) — see [§ `ctx.sessionId`](#ctxsessionid) |
88
- | `traceId` | When OTEL enabled | OTEL trace context |
89
- | `spanId` | When OTEL enabled | OTEL trace context |
89
+ | `traceId` | When OTEL enabled | Trace containing this handler execution |
90
+ | `spanId` | When OTEL enabled | The active `tool_execution:*` / `resource_read:*` span |
90
91
  | `auth` | When auth enabled | Parsed JWT claims |
91
92
 
92
93
  ---
93
94
 
95
+ ## `RequestContext` — the one canonical request shape
96
+
97
+ `Context extends RequestContext`. There is a single request-shape type; the handler-facing `Context` adds handler-only surfaces (`log`, `state`, `signal`, `requestInput`, `inputs`, `enrich`, `content`, `uri`) on top of it and redeclares none of the identity fields. A handler's `ctx` is therefore assignable anywhere a `RequestContext` is — services, storage, the framework logger — with no slice helper and no cast.
98
+
99
+ ```ts
100
+ import { requestContextService, withExtra } from '@cyanheads/mcp-ts-core/utils';
101
+ import type { RequestContext } from '@cyanheads/mcp-ts-core/utils';
102
+
103
+ // A service typed against RequestContext accepts a handler ctx directly.
104
+ async function fetchUser(id: string, ctx: RequestContext) { /* … */ }
105
+ await fetchUser('123', ctx); // ctx is a Context — no conversion
106
+ ```
107
+
108
+ ### Closed by design
109
+
110
+ `RequestContext` has **no index signature**. Its fields are exactly: `auth`, `extra`, `operation`, `requestId`, `sessionId`, `spanId`, `tenantId`, `timestamp`, `traceId`. A misspelled canonical field (`tenatId`) is a compile error instead of a silently-ignored key.
111
+
112
+ Operation-specific correlation data goes in **`extra`** — the one deliberate open bag (`Readonly<Record<string, unknown>>`). The logger flattens `extra` into the emitted line, so log output looks the same as a top-level spread.
113
+
114
+ ### Adding correlation data
115
+
116
+ Three supported ways, most common first:
117
+
118
+ ```ts
119
+ // 1. Per-log-call metadata — the common case. Nothing lands on the context.
120
+ ctx.log.info('Retrying upstream call', { attempt, url });
121
+
122
+ // 2. A copy of this context carrying extra fields. `withExtra` MERGES into any
123
+ // bag the parent already had; a hand-written `{ ...ctx, extra: {…} }` replaces it.
124
+ logger.warning('Retrying upstream call', withExtra(ctx, { attempt, url }));
125
+
126
+ // 3. A derived context for a sub-operation. `additionalContext` lands on `extra`,
127
+ // merged over whatever the parent already carried.
128
+ const childCtx = requestContextService.createRequestContext({
129
+ parentContext: ctx,
130
+ operation: 'processItem',
131
+ additionalContext: { itemId: item.id }, // → childCtx.extra.itemId
132
+ });
133
+
134
+ // Reading an ad-hoc key back off a context:
135
+ const itemId = childCtx.extra?.itemId;
136
+ ```
137
+
138
+ `createRequestContext(params)` takes a closed parameter object — `additionalContext`, `operation`, `parentContext`, `tenantId` — and nothing else; a key it doesn't declare is a compile error rather than an arbitrary passthrough.
139
+
140
+ Never re-open the shape to get past a type error: no index signature, no widening a parameter back to `Record<string, unknown>`, no `as` cast. A `{ ...ctx, someKey }` object literal that fails to compile is the signal to move `someKey` into `extra`, not to loosen the type.
141
+
142
+ `ErrorContext` (the `ErrorHandler` call's `context`) is `Partial<RequestContext>` and is closed the same way — put ad-hoc keys under `extra` via `withExtra`, or pass them in the `ErrorHandler` call's own `context` field.
143
+
144
+ `RequestContextLike` is a deprecated alias for `RequestContext`, kept for one minor. Replace every use with `RequestContext`, and collapse any `RequestContextLike | RequestContext` parameter union to plain `RequestContext`.
145
+
146
+ ---
147
+
94
148
  ## `ctx.log`
95
149
 
96
150
  Request-scoped structured logger. Every log line is automatically annotated with `requestId`, `traceId`, and `tenantId` — no manual spreading needed.
97
151
 
152
+ **Dual-sink.** Each call writes to Pino *and* mirrors onto the MCP wire as a `notifications/message` (the framework advertises the `logging` capability, and the SDK filters by the level the client set via `logging/setLevel`). The wire payload is `{ message, ...data }`; `ctx.log.error` adds `error: <message>`. Delivery is fire-and-forget — a client that never upgraded to SSE, set a higher level, or already disconnected drops the notification, and a failed send never fails the handler. Treat `ctx.log` as client-visible: it is no longer a server-only sink, so don't log anything there you wouldn't put in a tool result.
153
+
98
154
  ### Methods
99
155
 
100
156
  | Method | Level |
@@ -200,6 +256,8 @@ Optional HTTP session identifier. Surfaced when the request carries a durable se
200
256
 
201
257
  In `stateful` / `auto` mode, the value mirrors the `Mcp-Session-Id` HTTP header (or a server-minted token for new sessions). Each subsequent request from the same client reuses it; reconnects after disconnect bind to the same session as long as it hasn't expired.
202
258
 
259
+ The in-flight SSE stream is resumable too: stateful sessions carry a bounded event store, so a client reconnecting with `Last-Event-ID` gets the frames it missed replayed before the live stream resumes. On by default — selecting the session mode is the opt-in — with `MCP_HTTP_RESUMABILITY=false` as the kill switch and retention capped by both event count and TTL (`api-config` has the knobs). The buffer is released when its session is evicted.
260
+
203
261
  ### Stateless-mode opt-in
204
262
 
205
263
  In stateless HTTP mode the SDK still hands the framework a freshly generated token for every request, but it has request-lifetime semantics (no `SessionStore`, no continuity). The framework hides this from handlers by default — `ctx.sessionId` is `undefined` so any handler treating it as durable fails closed.
@@ -255,71 +313,120 @@ await ctx.state.set(sessionKey, value);
255
313
  ### Behavior notes
256
314
 
257
315
  - **Not a tenant boundary.** `ctx.state` is still tenant-scoped. Building session-scoped state is the consumer's responsibility — prefix with `session/${ctx.sessionId}/` as shown above.
258
- - **Auto-task tools.** `task: true` handlers run in a detached background context with no session attachment — `ctx.sessionId` is always `undefined` regardless of mode.
316
+ - **Protocol revision.** Sessions belong to the 2025-era arm, which negotiates its revision through `initialize` and carries `Mcp-Session-Id`. The [2026-07-28 revision](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports) has no session at all — it is per-request, selected by the request's own `_meta` envelope — so `ctx.sessionId` is `undefined` for every request served on that leg.
259
317
  - **Worker bundle.** Workers use the same HTTP transport plumbing; session behavior matches Node HTTP.
260
318
 
261
319
  ---
262
320
 
263
- ## `ctx.elicit`
321
+ ## `ctx.requestInput` / `ctx.inputs`
322
+
323
+ Always present, on every transport and both protocol eras. A handler that needs something the caller didn't supply **returns** `ctx.requestInput(...)` and is re-entered with the answers on `ctx.inputs` — there is no mid-handler `await` for user input.
264
324
 
265
- Optional — `undefined` when the connected client doesn't advertise the `elicitation` capability (checked per request, after the initialize handshake). Check for presence before calling. A simple truthiness check is enough; no type guards needed.
325
+ `ctx.requestInput(spec)` never returns: it throws an `InputRequiredSignal` that the tool, resource, and prompt handler factories catch and convert into the protocol's [`input_required`](https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr) result. It bypasses the error classifier entirely — no span, no log, no `isError`.
266
326
 
267
- `ctx.elicit` is an `ElicitFn` (exported from the main entry): directly callable for form-mode elicitation, with a `.url(message, url)` method for URL-mode. On the wire, the Zod schema is converted to the restricted flat JSON Schema the MCP spec requires — handlers never deal with that detail.
327
+ One code path serves both eras. A 2026-07-28 client fulfils the embedded requests and retries the call; for a 2025-era session the SDK's legacy shim fulfils the same returns by issuing real `elicitation/create` / `sampling/createMessage` / `roots/list` round trips and re-entering the handler itself.
268
328
 
269
- ### `ctx.elicit` — ask the user for structured input
329
+ ### The shape of a multi-round-trip handler
270
330
 
271
- Presents a form to the user via the MCP elicitation protocol. The user fills in a Zod-validated schema and returns an action (`accept`, `decline`, or `cancel`).
331
+ Read `ctx.inputs` first, request only what is still missing, and write the call in return position so TypeScript narrows the line below it.
272
332
 
273
333
  ```ts
274
- if (ctx.elicit) {
275
- const result = await ctx.elicit(
276
- 'Which output format do you want?',
277
- z.object({
278
- format: z.enum(['json', 'csv', 'markdown']).describe('Output format'),
279
- includeHeaders: z.boolean().default(true).describe('Include column headers'),
280
- }),
281
- );
282
-
283
- if (result.action === 'accept') {
284
- // result.content is Record<string, string | number | boolean | string[]> | undefined
285
- await produceOutput(result.content?.format as string, result.content?.includeHeaders as boolean);
286
- } else {
287
- // 'decline' or 'cancel' — user opted out
288
- throw invalidRequest('User declined input');
289
- }
290
- }
334
+ import { inputRequired, tool, z } from '@cyanheads/mcp-ts-core';
335
+ import { validationError } from '@cyanheads/mcp-ts-core/errors';
336
+
337
+ const Confirm = z.object({
338
+ confirm: z.boolean().describe('Whether to proceed with the deletion.'),
339
+ });
340
+
341
+ export const deletePath = tool('delete_path', {
342
+ description: 'Delete a path after confirming with the user.',
343
+ input: z.object({ path: z.string().describe('Path to delete') }),
344
+ output: z.object({ deleted: z.string().describe('The path that was deleted') }),
345
+ annotations: { destructiveHint: true },
346
+
347
+ handler(input, ctx) {
348
+ // A declined or cancelled prompt is a dead end, not a round to retry —
349
+ // re-asking loops until the round budget runs out.
350
+ const view = ctx.inputs.view('confirm');
351
+ if (view.kind === 'elicit' && view.action !== 'accept') {
352
+ throw validationError(`User ${view.action} the confirmation.`);
353
+ }
354
+
355
+ const answer = ctx.inputs.accepted('confirm', Confirm);
356
+ if (!answer) {
357
+ return ctx.requestInput({
358
+ inputRequests: {
359
+ confirm: inputRequired.elicit({
360
+ message: `Delete ${input.path}?`,
361
+ requestedSchema: Confirm,
362
+ }),
363
+ },
364
+ });
365
+ }
366
+ // `answer` is narrowed here.
367
+ if (!answer.confirm) throw validationError('Deletion not confirmed.');
368
+
369
+ return { deleted: remove(input.path) };
370
+ },
371
+ });
291
372
  ```
292
373
 
293
- `ElicitResult` (from `@modelcontextprotocol/sdk/types.js`):
374
+ `ctx.requestInput` returns `never`, so `return ctx.requestInput(...)` type-checks against any output type. Calling it as a bare statement works at runtime — and is the only option from a service-layer helper — but TypeScript will not narrow across it.
375
+
376
+ ### Building the embedded requests
377
+
378
+ `inputRequired` is re-exported from the main entry. Its per-kind constructors build the entries of `inputRequests`:
379
+
380
+ | Constructor | Wire request | Notes |
381
+ |:---|:---|:---|
382
+ | `inputRequired.elicit({ message, requestedSchema })` | `elicitation/create` (form) | `requestedSchema` accepts a Zod schema; shapes the restricted elicitation JSON Schema can't express throw a `TypeError` before anything is sent |
383
+ | `inputRequired.elicitUrl({ message, url })` | `elicitation/create` (URL) | Authorization flows, hosted forms. On 2026-07-28 URL mode rides the same multi-round-trip flow; the 2025-era `elicitationId` is not part of that shape — correlate with your own identifier inside `requestState` |
384
+ | `inputRequired.createMessage(params)` | `sampling/createMessage` | Ask the client's model |
385
+ | `inputRequired.listRoots()` | `roots/list` | Ask for the client's filesystem roots |
386
+
387
+ At least one of `inputRequests` or `requestState` must be supplied — the builder throws a `TypeError` otherwise.
388
+
389
+ ### `requestState` — carrying server state across rounds
294
390
 
295
391
  ```ts
296
- // Actual SDK type — a flat object, not a discriminated union
297
- interface ElicitResult {
298
- action: 'accept' | 'decline' | 'cancel';
299
- // Present when action === 'accept'; values are primitives or string arrays
300
- content?: Record<string, string | number | boolean | string[]>;
301
- }
392
+ return ctx.requestInput({
393
+ inputRequests: { confirm: inputRequired.elicit({ message, requestedSchema: Confirm }) },
394
+ requestState: JSON.stringify({ jobId }),
395
+ });
396
+ // Next round:
397
+ const state = ctx.inputs.state<string>();
302
398
  ```
303
399
 
304
- > **Note:** `content` is not typed against the Zod schema you pass — it is a `Record` of primitives. Validate `content` against your schema manually (e.g. `MySchema.parse(result.content)`) when `action === 'accept'`.
400
+ `requestState` round-trips **through the client** and comes back as attacker-controlled input. Integrity-protect (HMAC/AEAD) anything that influences authorization, resource access, or business logic, and reject state that fails verification — the SDK does not do this for you.
401
+
402
+ ### `ctx.inputs` — reading a retried request's responses
403
+
404
+ Empty on the first round; populated on re-entry.
305
405
 
306
- ### `ctx.elicit.url` — hand the user an external link
406
+ | Member | Returns |
407
+ |:---|:---|
408
+ | `accepted(key, schema?)` | The accepted form-mode content for `key`, or `undefined` when the key is missing, the user declined or cancelled, the response was another kind, or (with a schema) validation failed |
409
+ | `view(key)` | Discriminated view of one entry: `{ kind: 'missing' }` \| `{ kind: 'elicit', action, content? }` \| `{ kind: 'sampling', result }` \| `{ kind: 'roots', roots }` |
410
+ | `state<T>()` | This round's `requestState` — verified value, raw wire string when no verifier is configured, or `undefined` on the first round |
411
+ | `dropped` | Keys the SDK dropped because the client sent a wrapped rather than a bare response object. Re-issue those requests instead of hard-failing |
412
+ | `responses` | The raw response map, for kinds the helpers don't cover |
307
413
 
308
- URL-mode elicitation (MCP 2025-11-25): instead of an inline form, the client directs the user to an external URL — authorization flows, hosted forms. The framework generates the protocol-required `elicitationId` internally.
414
+ Two rules follow from what the SDK does *not* do:
415
+
416
+ - **Responses are never re-validated against the schema the request advertised.** Pass the schema to `accepted(key, schema)` wherever the content matters, and treat every value as untrusted client input.
417
+ - **`undefined` from `accepted()` collapses five different outcomes into one.** Missing, declined, cancelled, wrong response kind, and schema-invalid are indistinguishable through it. Branch on `view(key)` when decline/cancel needs different handling from "not asked yet" — as above, re-issuing a request the user already declined just burns rounds.
418
+
419
+ ### Testing
420
+
421
+ `createMockContext({ inputResponses, requestState })` seeds `ctx.inputs`, so a handler can be driven straight into its second round:
309
422
 
310
423
  ```ts
311
- if (ctx.elicit) {
312
- const result = await ctx.elicit.url(
313
- 'Authorize access to your account',
314
- 'https://example.com/oauth/authorize?state=...',
315
- );
316
- if (result.action !== 'accept') throw forbidden('Authorization declined');
317
- }
424
+ const ctx = createMockContext({
425
+ inputResponses: { confirm: { action: 'accept', content: { confirm: true } } },
426
+ });
318
427
  ```
319
428
 
320
- `result.content` is absent in URL mode — the interaction completes out-of-band; only `action` reports the outcome.
321
-
322
- **Convention:** Only call `ctx.elicit` from tool handlers, not from services.
429
+ **Convention:** only call `ctx.requestInput` from tool, resource, and prompt handlers — not from services.
323
430
 
324
431
  ---
325
432
 
@@ -337,20 +444,56 @@ async handler(input, ctx) {
337
444
 
338
445
  ### Delivery
339
446
 
340
- A notification fired **from inside a handler** routes through that request's own channel (`relatedRequestId`), so it reaches the client on **every transport** — stdio, HTTP, and Workers — even though HTTP/Workers run a per-request `McpServer` with no long-lived notification channel.
447
+ Which channel a notification takes depends on the protocol era the request is being served under, because the two eras opt a client in differently.
448
+
449
+ | Era | Client opts in via | `ctx.notify*` routes to |
450
+ |:----|:-------------------|:------------------------|
451
+ | 2026-07-28 | a `subscriptions/listen` stream, whose filter names the types (and, for resources, the URIs) it wants | the change-event bus, where the SDK's listen router applies that filter |
452
+ | 2025-era | `resources/subscribe`, for resource updates only | the request's own channel, stamped with `relatedRequestId` |
453
+
454
+ The modern routing is not an optimization — the spec is explicit that a server MUST NOT send notification types the client has not requested, and that filter only sees what reaches the bus. A modern handler sending through its own request scope would deliver to a client that opened no stream at all.
341
455
 
342
456
  | Fired from | stdio | HTTP / Workers |
343
457
  |:-----------|:------|:---------------|
344
- | A tool / resource handler | ✅ delivered | ✅ delivered (on the request's SSE response stream) |
345
- | A `task: true` background handler, cron, or any non-request scope | ✅ delivered | ⚠️ dropped — no request scope to route through |
458
+ | A tool / resource handler | ✅ delivered | ✅ delivered — on the listen stream (2026) or the request's SSE response stream (2025) |
459
+ | A `setup()` hook, cron job, or any non-request scope | ✅ delivered | ✅ to 2026 clients, via `core.notify` — see below. ⚠️ still dropped for 2025 clients: there is no out-of-request channel on that era |
460
+
461
+ ### Emitting outside a request: `core.notify`
462
+
463
+ Under HTTP there is no long-lived server instance a background emitter can send through, so `CoreServices.notify` publishes straight to the bus:
464
+
465
+ ```ts
466
+ createApp({
467
+ tools,
468
+ setup(core) {
469
+ watchUpstream(() => core.notify.resourcesChanged());
470
+ },
471
+ });
472
+ ```
473
+
474
+ It is the same `ServerNotifier` the handler path publishes to, captured before serving starts; publishing while nothing is listening is a no-op, not an error. Delivery reaches 2026-07-28 clients with an open `subscriptions/listen` stream.
475
+
476
+ **Supply your own bus on a multi-isolate runtime.** The default is in-process, which covers a single container. On Cloudflare Workers a background emission would otherwise reach only the isolate that produced it:
477
+
478
+ ```ts
479
+ createApp({ tools, eventBus: myDurableObjectBackedBus });
480
+ ```
481
+
482
+ ### `notifyResourceUpdated` is subscription-scoped
483
+
484
+ On both eras, but through different registries.
485
+
486
+ On 2025-era connections the framework advertises `resources: { subscribe: true }` and backs it with real `resources/subscribe` / `resources/unsubscribe` handlers, so `notifyResourceUpdated(uri)` emits only for URIs the connected client actually subscribed to; an unsubscribed URI logs at debug and sends nothing. Both handlers are idempotent — re-subscribing is a no-op, and unsubscribing from a URI that was never subscribed succeeds.
346
487
 
347
- The background-under-HTTP gap is a known limitation; a session-scoped notification bus would close it. `notifyResourceUpdated` routes to the calling request, not to clients that subscribed to the URI — the framework tracks no subscription state.
488
+ That registry's scope is the `McpServer` instance, which is also the connection: one persistent instance per session on the sessionful arm, one per request under per-request serving. On the per-request leg a subscription cannot outlive the request that created it, so a handler-time `ctx.notifyResourceUpdated(uri)` delivers only when that same exchange subscribed first.
489
+
490
+ On 2026-07-28 there is no `resources/subscribe` method and no registry. The URI ships with the published event, and the listen filter's `resourceSubscriptions` field decides who receives it — upstream's job, not the framework's.
348
491
 
349
492
  ---
350
493
 
351
494
  ## `ctx.signal`
352
495
 
353
- Standard `AbortSignal`. Present on every context. Set when the client cancels the request or when a task tool is cancelled.
496
+ Standard `AbortSignal`. Present on every context. Fires when the client cancels the request — and when the transport closes, which aborts every in-flight handler.
354
497
 
355
498
  ```ts
356
499
  // Check before expensive operations
@@ -366,55 +509,6 @@ for (const item of items) {
366
509
  }
367
510
  ```
368
511
 
369
- In task tools (`task: true`), the framework signals `ctx.signal` when the client sends a cancellation request.
370
-
371
- ---
372
-
373
- ## `ctx.progress`
374
-
375
- Present only when the tool definition includes `task: true`. Undefined for standard (non-task) tools and all resource handlers.
376
-
377
- ### Methods
378
-
379
- | Method | Purpose |
380
- |:-------|:--------|
381
- | `ctx.progress.setTotal(n)` | Set the total number of steps (enables percentage calculation on client) |
382
- | `ctx.progress.increment(amount?)` | Advance progress by `amount` (default: 1) |
383
- | `ctx.progress.update(message)` | Send a descriptive status message without advancing the counter |
384
-
385
- ### Usage
386
-
387
- ```ts
388
- const asyncCountdown = tool('async_countdown', {
389
- description: 'Count down from a number with progress updates.',
390
- task: true,
391
- input: z.object({
392
- count: z.number().int().positive().describe('Number to count down from'),
393
- delayMs: z.number().default(1000).describe('Delay between counts in ms'),
394
- }),
395
- output: z.object({
396
- finalCount: z.number().describe('Final count value'),
397
- message: z.string().describe('Completion message'),
398
- }),
399
-
400
- async handler(input, ctx) {
401
- await ctx.progress!.setTotal(input.count);
402
-
403
- for (let i = input.count; i > 0; i--) {
404
- if (ctx.signal.aborted) break;
405
-
406
- await ctx.progress!.update(`Counting: ${i}`);
407
- await new Promise(resolve => setTimeout(resolve, input.delayMs));
408
- await ctx.progress!.increment();
409
- }
410
-
411
- return { finalCount: 0, message: 'Countdown complete' };
412
- },
413
- });
414
- ```
415
-
416
- **Note:** Use the non-null assertion (`ctx.progress!`) when accessing inside a `task: true` handler — the type is `ContextProgress | undefined` even though it's guaranteed present at runtime. TypeScript cannot narrow based on the `task` flag.
417
-
418
512
  ---
419
513
 
420
514
  ## `ctx.uri`
@@ -548,7 +642,7 @@ The `≥5 words` lint rule on contract `recovery` (validated at lint time) makes
548
642
 
549
643
  ## `ctx.enrich`
550
644
 
551
- Always present on `Context`. Accumulates agent-facing **success-path** context — empty-result notices, the query/filter as the server parsed it, pagination totals — onto the request. The framework merges it into `structuredContent`, advertises `output.extend(enrichment)` as the tool's `outputSchema`, and mirrors it into a `content[]` trailer. The success-path counterpart to `ctx.fail` / `ctx.recoveryFor`.
645
+ Always present on `Context`. Accumulates agent-facing **success-path** context — empty-result notices, the query/filter as the server parsed it, pagination totals — onto the request. The framework merges it into `structuredContent`, folds the `enrichment` block into the tool's advertised `outputSchema`, and mirrors it into a `content[]` trailer. The success-path counterpart to `ctx.fail` / `ctx.recoveryFor`.
552
646
 
553
647
  ```ts
554
648
  export const search = tool('search', {
@@ -698,21 +792,23 @@ Test content blocks with `getContentBlocks(ctx)` from `@cyanheads/mcp-ts-core/te
698
792
  | `ctx.requestId` | `string` | Always |
699
793
  | `ctx.timestamp` | `string` | Always |
700
794
  | `ctx.tenantId` | `string \| undefined` | Stdio (`'default'`); HTTP+`MCP_AUTH_MODE=none` (`'default'`); HTTP+`jwt`/`oauth` (JWT `tid` claim — undefined if absent) |
701
- | `ctx.sessionId` | `string \| undefined` | HTTP `stateful` / `auto` mode; stateless HTTP only when `createApp({ context: { exposeStatelessSessionId: true } })`; never in stdio or auto-task handlers |
702
- | `ctx.traceId` | `string \| undefined` | OTEL enabled |
703
- | `ctx.spanId` | `string \| undefined` | OTEL enabled |
795
+ | `ctx.sessionId` | `string \| undefined` | HTTP `stateful` / `auto` mode; stateless HTTP only when `createApp({ context: { exposeStatelessSessionId: true } })`; never in stdio or on the session-less 2026-07-28 leg |
796
+ | `ctx.traceId` | `string \| undefined` | OTEL enabled — the trace containing this handler execution |
797
+ | `ctx.spanId` | `string \| undefined` | OTEL enabled — the handler's own execution span, not the enclosing request span |
704
798
  | `ctx.auth` | `AuthContext \| undefined` | Auth enabled |
799
+ | `ctx.operation` | `string \| undefined` | Set by the context that created it (`'HandleToolRequest'` for tool calls) |
800
+ | `ctx.extra` | `Readonly<Record<string, unknown>> \| undefined` | When correlation data was attached — the one open bag on the closed shape |
705
801
  | `ctx.log` | `ContextLogger` | Always |
706
802
  | `ctx.state` | `ContextState` | Always (throws if `tenantId` missing) |
707
803
  | `ctx.signal` | `AbortSignal` | Always |
708
804
  | `ctx.enrich` | `Enrich` | Always; typed on `HandlerContext<R, E>` when an `enrichment` block is declared |
709
805
  | `ctx.content` | `ContentCollect` | Always — prepends image/audio blocks to `content[]`, never `structuredContent` |
710
- | `ctx.elicit` | `function \| undefined` | Client supports elicitation |
806
+ | `ctx.requestInput` | `(spec) => never` | Always — suspends the handler and asks the caller for more input |
807
+ | `ctx.inputs` | `ContextInputs` | Always; empty until the request is retried with responses |
711
808
  | `ctx.notifyResourceListChanged` | `function \| undefined` | Always in handler ctx; delivery request-scoped (see [§ list-changed notifications](#list-changed-notifications-ctxnotify)) |
712
- | `ctx.notifyResourceUpdated` | `function \| undefined` | Always in handler ctx; delivery request-scoped |
809
+ | `ctx.notifyResourceUpdated` | `function \| undefined` | Always in handler ctx; limited to URIs the client subscribed to, through the listen filter (2026) or the subscribe registry (2025) |
713
810
  | `ctx.notifyPromptListChanged` | `function \| undefined` | Always in handler ctx; delivery request-scoped |
714
- | `ctx.notifyToolListChanged` | `function \| undefined` | Always in handler ctx; delivery request-scoped |
715
- | `ctx.progress` | `ContextProgress \| undefined` | Tool defined with `task: true` |
811
+ | `ctx.notifyToolListChanged` | `function \| undefined` | Always in handler ctx; delivered on the client's listen stream (2026) or its own request scope (2025) |
716
812
  | `ctx.uri` | `URL \| undefined` | Resource handlers only |
717
813
  | `ctx.fail` | `(reason, msg?, data?, opts?) => McpError` | Definition declares `errors[]` contract |
718
814
  | `ctx.recoveryFor` | `(reason) => { recovery: { hint } } \| {}` | Always (no-op when no contract); strictly typed on `HandlerContext<R>` |
@@ -4,7 +4,7 @@ description: >
4
4
  MCP definition linter rules reference. Use when `bun run lint:mcp` or `bun run devcheck` reports a lint error or warning (`format-parity`, `schema-is-object`, `name-format`, `server-json-*`, etc.) and you need to understand the rule, its severity, and how to fix it. Every rule ID the linter emits has an entry in this doc.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.9"
7
+ version: "1.11"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -45,7 +45,7 @@ Grouped by family. Jump to any rule ID via its anchor.
45
45
  | Definition | `definition-invalid` | [Definition rules](#definition-rules) |
46
46
  | Format parity | `format-parity`, `format-parity-threw`, `format-parity-walk-failed`, `format-parity-depth-limit` | [Format parity](#format-parity) |
47
47
  | Schema | `schema-is-object`, `describe-on-fields`, `schema-serializable`, `schema-unsatisfiable` | [Schema rules](#schema-rules) |
48
- | Portability | `schema-format-portability`, `schema-anyof-needs-type`, `schema-no-discriminator-keyword`, `schema-no-defs`, `schema-dialect-tag` | [Portability rules](#portability-rules) |
48
+ | Portability | `schema-format-portability`, `schema-anyof-needs-type`, `schema-no-discriminator-keyword`, `schema-no-defs`, `schema-root-oneof-portability`, `schema-dialect-tag` | [Portability rules](#portability-rules) |
49
49
  | Names | `name-required`, `name-format`, `name-unique` | [Name rules](#name-rules) |
50
50
  | Tools | `description-required`, `handler-required`, `auth-type`, `auth-scope-format`, `annotation-type`, `annotation-coherence`, `meta-ui-type`, `meta-ui-resource-uri-required`, `meta-ui-resource-uri-scheme`, `app-tool-resource-pairing`, `canvas-consumer-missing` | [Tool rules](#tool-rules) |
51
51
  | Resources | `uri-template-required`, `uri-template-valid`, `resource-name-not-uri`, `template-params-align` | [Resource rules](#resource-rules) |
@@ -169,6 +169,10 @@ input: z.array(z.string())
169
169
  input: z.object({ items: z.array(z.string()).describe('List of items') })
170
170
  ```
171
171
 
172
+ **One exception, on tool `input` only:** a `z.discriminatedUnion(...)` of object variants is accepted, for a multi-mode tool with mutually exclusive argument sets. It advertises as `{"type": "object", "oneOf": [...]}` — the object requirement holds, and each branch keeps its own `required` list and `const`-tagged discriminator. A bare `z.union(...)` is still rejected: with no discriminator the model has no key to pick a branch by. Output roots stay object-only — the 2025-era projection rewrites a non-object output root and wraps `structuredContent` to match.
173
+
174
+ The other schema rules walk every variant, so a missing `.describe()`, a non-serializable type, or an unsatisfiable node inside one branch is reported at `input|<i>.<field>`.
175
+
172
176
  ### describe-on-fields
173
177
 
174
178
  **Severity:** warning
@@ -186,6 +190,7 @@ Every field in `input`, `output`, `params`, or `args` needs a `.describe('...')`
186
190
  | `z.array(primitive)` element — string, number, enum, regex-branded primitive, etc. | **No** | No — outer array describe is sufficient |
187
191
  | `z.union([a, b, ...])` non-literal option | Yes | Yes, on each option |
188
192
  | `z.union([..., z.literal(X), ...])` literal option | **No** | No — outer union describe is sufficient |
193
+ | A tool `input` root that is a `z.discriminatedUnion(...)` — its variant objects | Yes, their **fields** | No, not on the variant itself — it is a root, and roots carry no describe |
189
194
 
190
195
  The asymmetry that catches agents: inside `z.union([z.string(), z.array(z.string())])`, the outer `z.string()` option **does** need a describe (unions walk non-literal options), but the `z.string()` inside the inner array does **not** (arrays don't walk primitive elements). If the linter didn't flag a path, don't add a describe there — the redundant describe ships to the JSON Schema as clutter.
191
196
 
@@ -309,6 +314,14 @@ Fires when emitted output contains `$defs` or `$ref`. Gemini rejects these (`400
309
314
 
310
315
  **Fix:** inline the recursive type with bounded depth, or accept the Gemini limitation if you target only Anthropic clients.
311
316
 
317
+ ### schema-root-oneof-portability
318
+
319
+ **Severity:** warning (only when `portability: 'strict'`)
320
+
321
+ Fires when a tool's advertised `inputSchema` has a root-level `oneOf` — that is, when `input` is a `z.discriminatedUnion(...)`. The emitted shape is valid 2020-12, every branch is a typed object, and the bytes are identical on both MCP protocol revisions. What is unmeasured is vendor handling of a `oneOf` at the *parameter* root: a client that reads only `type` and `properties` would see a parameterless tool and drop the constraint silently rather than erroring. Opt-in, because for Anthropic clients the union is the better shape.
322
+
323
+ **Fix (only if you need the widest vendor reach):** flatten to a single `z.object()` with a discriminator field and optional per-mode fields, and validate the combination in the handler.
324
+
312
325
  ### schema-dialect-tag
313
326
 
314
327
  **Severity:** warning (only when `portability: 'strict'`)
@@ -789,7 +802,7 @@ The diagnostic message includes the declared reason(s) for the code so you can c
789
802
 
790
803
  ## Enrichment rules
791
804
 
792
- Validate the `enrichment` block — the success-path counterpart to `errors[]`. Enrichment fields are merged into `structuredContent` and advertised as `output.extend(enrichment)`, so the linter guards the block's shape and its disjointness from `output`. See `api-context`'s `ctx.enrich` and `add-tool`'s **Tool Response Design**.
805
+ Validate the `enrichment` block — the success-path counterpart to `errors[]`. Enrichment fields are merged into `structuredContent` and folded into the advertised `outputSchema`, so the linter guards the block's shape and its disjointness from `output`. See `api-context`'s `ctx.enrich` and `add-tool`'s **Tool Response Design**.
793
806
 
794
807
  ### enrichment-type
795
808
 
@@ -4,17 +4,19 @@ 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.5"
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
 
18
+ A handler's `ctx.traceId` / `ctx.spanId` name the execution span it runs in — `tool_execution:<name>` or `resource_read:<name>` — not the enclosing HTTP request span. Under HTTP the trace ID is the request's, so handler logs join to the request; the span ID is the child execution's, so they join to that span's attributes and duration. On stdio, where no transport span exists, both are still populated from the execution span the framework opens. Both are `undefined` when telemetry is disabled: the non-recording span a disabled pipeline produces carries all-zero IDs, and the framework reports no correlation rather than IDs that correlate to nothing.
19
+
18
20
  For the helper API surface (`withSpan`, `createCounter`, `createHistogram`, `buildTraceparent`, etc.) — see the `api-utils` skill, `Telemetry` section. This skill is the catalog of **what** is emitted; that one is the reference for **how** to emit your own.
19
21
 
20
22
  ---
@@ -61,15 +63,17 @@ Every handler call gets a span. Nested operations (storage, graph, LLM) become c
61
63
 
62
64
  | Span name | Source | Key attributes |
63
65
  |:----------|:-------|:---------------|
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` |
66
+ | `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` |
67
+ | `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` |
68
+ | `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
69
  | `storage:<op>` | `StorageService` (every call) | `mcp.storage.operation`, `mcp.storage.duration_ms`, `mcp.storage.success`, `mcp.storage.key_count` (batch ops) |
68
70
  | `graph:<op>` | `GraphService` (every call) | `mcp.graph.operation`, `mcp.graph.duration_ms`, `mcp.graph.success` |
69
71
  | `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
72
  | `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
73
  | `speech:stt` | Whisper provider | same as `speech:tts` |
72
74
 
75
+ 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.
76
+
73
77
  Trace context propagates across boundaries via W3C `traceparent` headers. See `api-utils` → `telemetry/trace` for `withSpan`, `buildTraceparent`, `extractTraceparent`, `createContextWithParentTrace`, `injectCurrentContextInto`, `runInContext` signatures.
74
78
 
75
79
  ---
@@ -118,7 +122,7 @@ All custom metrics are namespaced `mcp.*` (or `process.*` / `http.client.*` wher
118
122
  | `mcp.graph.duration` | histogram | `ms` | `mcp.graph.operation`, `mcp.graph.success` |
119
123
  | `mcp.graph.errors` | counter | `{errors}` | `mcp.graph.operation` |
120
124
 
121
- ### Transport, auth, sessions, tasks
125
+ ### Transport, auth, sessions
122
126
 
123
127
  | Metric | Type | Unit | Attributes |
124
128
  |:-------|:-----|:-----|:-----------|
@@ -128,12 +132,6 @@ All custom metrics are namespaced `mcp.*` (or `process.*` / `http.client.*` wher
128
132
  | `mcp.session.duration` | histogram | `s` | — |
129
133
  | `mcp.sessions.active` | observable gauge | `{sessions}` | — |
130
134
  | `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
135
 
138
136
  ### Errors, rate limits, HTTP client
139
137