@cyanheads/mcp-ts-core 0.1.0-beta.12

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 (485) hide show
  1. package/CLAUDE.md +583 -0
  2. package/LICENSE +201 -0
  3. package/README.md +287 -0
  4. package/biome.json +103 -0
  5. package/dist/app.d.ts +82 -0
  6. package/dist/app.d.ts.map +1 -0
  7. package/dist/app.js +222 -0
  8. package/dist/app.js.map +1 -0
  9. package/dist/cli/init.d.ts +8 -0
  10. package/dist/cli/init.d.ts.map +1 -0
  11. package/dist/cli/init.js +161 -0
  12. package/dist/cli/init.js.map +1 -0
  13. package/dist/config/index.d.ts +349 -0
  14. package/dist/config/index.d.ts.map +1 -0
  15. package/dist/config/index.js +464 -0
  16. package/dist/config/index.js.map +1 -0
  17. package/dist/context.d.ts +119 -0
  18. package/dist/context.d.ts.map +1 -0
  19. package/dist/context.js +144 -0
  20. package/dist/context.js.map +1 -0
  21. package/dist/index.d.ts +8 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +9 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/mcp-server/prompts/prompt-registration.d.ts +33 -0
  26. package/dist/mcp-server/prompts/prompt-registration.d.ts.map +1 -0
  27. package/dist/mcp-server/prompts/prompt-registration.js +91 -0
  28. package/dist/mcp-server/prompts/prompt-registration.js.map +1 -0
  29. package/dist/mcp-server/prompts/utils/newPromptDefinition.d.ts +49 -0
  30. package/dist/mcp-server/prompts/utils/newPromptDefinition.d.ts.map +1 -0
  31. package/dist/mcp-server/prompts/utils/newPromptDefinition.js +39 -0
  32. package/dist/mcp-server/prompts/utils/newPromptDefinition.js.map +1 -0
  33. package/dist/mcp-server/prompts/utils/promptDefinition.d.ts +37 -0
  34. package/dist/mcp-server/prompts/utils/promptDefinition.d.ts.map +1 -0
  35. package/dist/mcp-server/prompts/utils/promptDefinition.js +2 -0
  36. package/dist/mcp-server/prompts/utils/promptDefinition.js.map +1 -0
  37. package/dist/mcp-server/resources/resource-registration.d.ts +27 -0
  38. package/dist/mcp-server/resources/resource-registration.d.ts.map +1 -0
  39. package/dist/mcp-server/resources/resource-registration.js +85 -0
  40. package/dist/mcp-server/resources/resource-registration.js.map +1 -0
  41. package/dist/mcp-server/resources/utils/newResourceDefinition.d.ts +84 -0
  42. package/dist/mcp-server/resources/utils/newResourceDefinition.d.ts.map +1 -0
  43. package/dist/mcp-server/resources/utils/newResourceDefinition.js +40 -0
  44. package/dist/mcp-server/resources/utils/newResourceDefinition.js.map +1 -0
  45. package/dist/mcp-server/resources/utils/newResourceHandlerFactory.d.ts +32 -0
  46. package/dist/mcp-server/resources/utils/newResourceHandlerFactory.d.ts.map +1 -0
  47. package/dist/mcp-server/resources/utils/newResourceHandlerFactory.js +103 -0
  48. package/dist/mcp-server/resources/utils/newResourceHandlerFactory.js.map +1 -0
  49. package/dist/mcp-server/resources/utils/resourceDefinition.d.ts +94 -0
  50. package/dist/mcp-server/resources/utils/resourceDefinition.d.ts.map +1 -0
  51. package/dist/mcp-server/resources/utils/resourceDefinition.js +2 -0
  52. package/dist/mcp-server/resources/utils/resourceDefinition.js.map +1 -0
  53. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts +14 -0
  54. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -0
  55. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +111 -0
  56. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -0
  57. package/dist/mcp-server/roots/roots-registration.d.ts +22 -0
  58. package/dist/mcp-server/roots/roots-registration.d.ts.map +1 -0
  59. package/dist/mcp-server/roots/roots-registration.js +25 -0
  60. package/dist/mcp-server/roots/roots-registration.js.map +1 -0
  61. package/dist/mcp-server/server.d.ts +34 -0
  62. package/dist/mcp-server/server.d.ts.map +1 -0
  63. package/dist/mcp-server/server.js +62 -0
  64. package/dist/mcp-server/server.js.map +1 -0
  65. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.d.ts +42 -0
  66. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.d.ts.map +1 -0
  67. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.js +70 -0
  68. package/dist/mcp-server/tasks/core/sessionAwareTaskStore.js.map +1 -0
  69. package/dist/mcp-server/tasks/core/storageBackedTaskStore.d.ts +109 -0
  70. package/dist/mcp-server/tasks/core/storageBackedTaskStore.d.ts.map +1 -0
  71. package/dist/mcp-server/tasks/core/storageBackedTaskStore.js +209 -0
  72. package/dist/mcp-server/tasks/core/storageBackedTaskStore.js.map +1 -0
  73. package/dist/mcp-server/tasks/core/taskManager.d.ts +103 -0
  74. package/dist/mcp-server/tasks/core/taskManager.d.ts.map +1 -0
  75. package/dist/mcp-server/tasks/core/taskManager.js +144 -0
  76. package/dist/mcp-server/tasks/core/taskManager.js.map +1 -0
  77. package/dist/mcp-server/tasks/core/taskTypes.d.ts +11 -0
  78. package/dist/mcp-server/tasks/core/taskTypes.d.ts.map +1 -0
  79. package/dist/mcp-server/tasks/core/taskTypes.js +13 -0
  80. package/dist/mcp-server/tasks/core/taskTypes.js.map +1 -0
  81. package/dist/mcp-server/tasks/utils/taskToolDefinition.d.ts +108 -0
  82. package/dist/mcp-server/tasks/utils/taskToolDefinition.d.ts.map +1 -0
  83. package/dist/mcp-server/tasks/utils/taskToolDefinition.js +14 -0
  84. package/dist/mcp-server/tasks/utils/taskToolDefinition.js.map +1 -0
  85. package/dist/mcp-server/tools/tool-registration.d.ts +49 -0
  86. package/dist/mcp-server/tools/tool-registration.d.ts.map +1 -0
  87. package/dist/mcp-server/tools/tool-registration.js +269 -0
  88. package/dist/mcp-server/tools/tool-registration.js.map +1 -0
  89. package/dist/mcp-server/tools/utils/newToolDefinition.d.ts +73 -0
  90. package/dist/mcp-server/tools/utils/newToolDefinition.d.ts.map +1 -0
  91. package/dist/mcp-server/tools/utils/newToolDefinition.js +45 -0
  92. package/dist/mcp-server/tools/utils/newToolDefinition.js.map +1 -0
  93. package/dist/mcp-server/tools/utils/newToolHandlerFactory.d.ts +33 -0
  94. package/dist/mcp-server/tools/utils/newToolHandlerFactory.d.ts.map +1 -0
  95. package/dist/mcp-server/tools/utils/newToolHandlerFactory.js +107 -0
  96. package/dist/mcp-server/tools/utils/newToolHandlerFactory.js.map +1 -0
  97. package/dist/mcp-server/tools/utils/toolDefinition.d.ts +118 -0
  98. package/dist/mcp-server/tools/utils/toolDefinition.d.ts.map +1 -0
  99. package/dist/mcp-server/tools/utils/toolDefinition.js +2 -0
  100. package/dist/mcp-server/tools/utils/toolDefinition.js.map +1 -0
  101. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +34 -0
  102. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -0
  103. package/dist/mcp-server/tools/utils/toolHandlerFactory.js +68 -0
  104. package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -0
  105. package/dist/mcp-server/transports/ITransport.d.ts +15 -0
  106. package/dist/mcp-server/transports/ITransport.d.ts.map +1 -0
  107. package/dist/mcp-server/transports/ITransport.js +2 -0
  108. package/dist/mcp-server/transports/ITransport.js.map +1 -0
  109. package/dist/mcp-server/transports/auth/authFactory.d.ts +11 -0
  110. package/dist/mcp-server/transports/auth/authFactory.d.ts.map +1 -0
  111. package/dist/mcp-server/transports/auth/authFactory.js +43 -0
  112. package/dist/mcp-server/transports/auth/authFactory.js.map +1 -0
  113. package/dist/mcp-server/transports/auth/authMiddleware.d.ts +24 -0
  114. package/dist/mcp-server/transports/auth/authMiddleware.d.ts.map +1 -0
  115. package/dist/mcp-server/transports/auth/authMiddleware.js +69 -0
  116. package/dist/mcp-server/transports/auth/authMiddleware.js.map +1 -0
  117. package/dist/mcp-server/transports/auth/lib/authContext.d.ts +34 -0
  118. package/dist/mcp-server/transports/auth/lib/authContext.d.ts.map +1 -0
  119. package/dist/mcp-server/transports/auth/lib/authContext.js +25 -0
  120. package/dist/mcp-server/transports/auth/lib/authContext.js.map +1 -0
  121. package/dist/mcp-server/transports/auth/lib/authTypes.d.ts +19 -0
  122. package/dist/mcp-server/transports/auth/lib/authTypes.d.ts.map +1 -0
  123. package/dist/mcp-server/transports/auth/lib/authTypes.js +2 -0
  124. package/dist/mcp-server/transports/auth/lib/authTypes.js.map +1 -0
  125. package/dist/mcp-server/transports/auth/lib/authUtils.d.ts +18 -0
  126. package/dist/mcp-server/transports/auth/lib/authUtils.d.ts.map +1 -0
  127. package/dist/mcp-server/transports/auth/lib/authUtils.js +64 -0
  128. package/dist/mcp-server/transports/auth/lib/authUtils.js.map +1 -0
  129. package/dist/mcp-server/transports/auth/lib/checkScopes.d.ts +25 -0
  130. package/dist/mcp-server/transports/auth/lib/checkScopes.d.ts.map +1 -0
  131. package/dist/mcp-server/transports/auth/lib/checkScopes.js +34 -0
  132. package/dist/mcp-server/transports/auth/lib/checkScopes.js.map +1 -0
  133. package/dist/mcp-server/transports/auth/lib/claimParser.d.ts +34 -0
  134. package/dist/mcp-server/transports/auth/lib/claimParser.d.ts.map +1 -0
  135. package/dist/mcp-server/transports/auth/lib/claimParser.js +58 -0
  136. package/dist/mcp-server/transports/auth/lib/claimParser.js.map +1 -0
  137. package/dist/mcp-server/transports/auth/lib/withAuth.d.ts +25 -0
  138. package/dist/mcp-server/transports/auth/lib/withAuth.d.ts.map +1 -0
  139. package/dist/mcp-server/transports/auth/lib/withAuth.js +30 -0
  140. package/dist/mcp-server/transports/auth/lib/withAuth.js.map +1 -0
  141. package/dist/mcp-server/transports/auth/strategies/authStrategy.d.ts +18 -0
  142. package/dist/mcp-server/transports/auth/strategies/authStrategy.d.ts.map +1 -0
  143. package/dist/mcp-server/transports/auth/strategies/authStrategy.js +2 -0
  144. package/dist/mcp-server/transports/auth/strategies/authStrategy.js.map +1 -0
  145. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.d.ts +14 -0
  146. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.d.ts.map +1 -0
  147. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js +86 -0
  148. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js.map +1 -0
  149. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.d.ts +14 -0
  150. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.d.ts.map +1 -0
  151. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js +113 -0
  152. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js.map +1 -0
  153. package/dist/mcp-server/transports/http/httpErrorHandler.d.ts +25 -0
  154. package/dist/mcp-server/transports/http/httpErrorHandler.d.ts.map +1 -0
  155. package/dist/mcp-server/transports/http/httpErrorHandler.js +112 -0
  156. package/dist/mcp-server/transports/http/httpErrorHandler.js.map +1 -0
  157. package/dist/mcp-server/transports/http/httpTransport.d.ts +47 -0
  158. package/dist/mcp-server/transports/http/httpTransport.d.ts.map +1 -0
  159. package/dist/mcp-server/transports/http/httpTransport.js +396 -0
  160. package/dist/mcp-server/transports/http/httpTransport.js.map +1 -0
  161. package/dist/mcp-server/transports/http/httpTypes.d.ts +17 -0
  162. package/dist/mcp-server/transports/http/httpTypes.d.ts.map +1 -0
  163. package/dist/mcp-server/transports/http/httpTypes.js +2 -0
  164. package/dist/mcp-server/transports/http/httpTypes.js.map +1 -0
  165. package/dist/mcp-server/transports/http/protectedResourceMetadata.d.ts +21 -0
  166. package/dist/mcp-server/transports/http/protectedResourceMetadata.d.ts.map +1 -0
  167. package/dist/mcp-server/transports/http/protectedResourceMetadata.js +44 -0
  168. package/dist/mcp-server/transports/http/protectedResourceMetadata.js.map +1 -0
  169. package/dist/mcp-server/transports/http/sessionIdUtils.d.ts +33 -0
  170. package/dist/mcp-server/transports/http/sessionIdUtils.d.ts.map +1 -0
  171. package/dist/mcp-server/transports/http/sessionIdUtils.js +54 -0
  172. package/dist/mcp-server/transports/http/sessionIdUtils.js.map +1 -0
  173. package/dist/mcp-server/transports/http/sessionStore.d.ts +87 -0
  174. package/dist/mcp-server/transports/http/sessionStore.d.ts.map +1 -0
  175. package/dist/mcp-server/transports/http/sessionStore.js +209 -0
  176. package/dist/mcp-server/transports/http/sessionStore.js.map +1 -0
  177. package/dist/mcp-server/transports/manager.d.ts +22 -0
  178. package/dist/mcp-server/transports/manager.d.ts.map +1 -0
  179. package/dist/mcp-server/transports/manager.js +62 -0
  180. package/dist/mcp-server/transports/manager.js.map +1 -0
  181. package/dist/mcp-server/transports/stdio/stdioTransport.d.ts +44 -0
  182. package/dist/mcp-server/transports/stdio/stdioTransport.d.ts.map +1 -0
  183. package/dist/mcp-server/transports/stdio/stdioTransport.js +63 -0
  184. package/dist/mcp-server/transports/stdio/stdioTransport.js.map +1 -0
  185. package/dist/services/graph/core/GraphService.d.ts +205 -0
  186. package/dist/services/graph/core/GraphService.d.ts.map +1 -0
  187. package/dist/services/graph/core/GraphService.js +231 -0
  188. package/dist/services/graph/core/GraphService.js.map +1 -0
  189. package/dist/services/graph/core/IGraphProvider.d.ts +295 -0
  190. package/dist/services/graph/core/IGraphProvider.d.ts.map +1 -0
  191. package/dist/services/graph/core/IGraphProvider.js +8 -0
  192. package/dist/services/graph/core/IGraphProvider.js.map +1 -0
  193. package/dist/services/graph/types.d.ts +107 -0
  194. package/dist/services/graph/types.d.ts.map +1 -0
  195. package/dist/services/graph/types.js +8 -0
  196. package/dist/services/graph/types.js.map +1 -0
  197. package/dist/services/llm/core/ILlmProvider.d.ts +86 -0
  198. package/dist/services/llm/core/ILlmProvider.d.ts.map +1 -0
  199. package/dist/services/llm/core/ILlmProvider.js +2 -0
  200. package/dist/services/llm/core/ILlmProvider.js.map +1 -0
  201. package/dist/services/llm/providers/openrouter.provider.d.ts +187 -0
  202. package/dist/services/llm/providers/openrouter.provider.d.ts.map +1 -0
  203. package/dist/services/llm/providers/openrouter.provider.js +302 -0
  204. package/dist/services/llm/providers/openrouter.provider.js.map +1 -0
  205. package/dist/services/llm/types.d.ts +16 -0
  206. package/dist/services/llm/types.d.ts.map +1 -0
  207. package/dist/services/llm/types.js +9 -0
  208. package/dist/services/llm/types.js.map +1 -0
  209. package/dist/services/speech/core/ISpeechProvider.d.ts +92 -0
  210. package/dist/services/speech/core/ISpeechProvider.d.ts.map +1 -0
  211. package/dist/services/speech/core/ISpeechProvider.js +34 -0
  212. package/dist/services/speech/core/ISpeechProvider.js.map +1 -0
  213. package/dist/services/speech/core/SpeechService.d.ts +87 -0
  214. package/dist/services/speech/core/SpeechService.d.ts.map +1 -0
  215. package/dist/services/speech/core/SpeechService.js +135 -0
  216. package/dist/services/speech/core/SpeechService.js.map +1 -0
  217. package/dist/services/speech/providers/elevenlabs.provider.d.ts +77 -0
  218. package/dist/services/speech/providers/elevenlabs.provider.d.ts.map +1 -0
  219. package/dist/services/speech/providers/elevenlabs.provider.js +199 -0
  220. package/dist/services/speech/providers/elevenlabs.provider.js.map +1 -0
  221. package/dist/services/speech/providers/whisper.provider.d.ts +94 -0
  222. package/dist/services/speech/providers/whisper.provider.d.ts.map +1 -0
  223. package/dist/services/speech/providers/whisper.provider.js +240 -0
  224. package/dist/services/speech/providers/whisper.provider.js.map +1 -0
  225. package/dist/services/speech/types.d.ts +173 -0
  226. package/dist/services/speech/types.d.ts.map +1 -0
  227. package/dist/services/speech/types.js +8 -0
  228. package/dist/services/speech/types.js.map +1 -0
  229. package/dist/storage/core/IStorageProvider.d.ts +159 -0
  230. package/dist/storage/core/IStorageProvider.d.ts.map +1 -0
  231. package/dist/storage/core/IStorageProvider.js +2 -0
  232. package/dist/storage/core/IStorageProvider.js.map +1 -0
  233. package/dist/storage/core/StorageService.d.ts +22 -0
  234. package/dist/storage/core/StorageService.d.ts.map +1 -0
  235. package/dist/storage/core/StorageService.js +151 -0
  236. package/dist/storage/core/StorageService.js.map +1 -0
  237. package/dist/storage/core/storageFactory.d.ts +66 -0
  238. package/dist/storage/core/storageFactory.d.ts.map +1 -0
  239. package/dist/storage/core/storageFactory.js +122 -0
  240. package/dist/storage/core/storageFactory.js.map +1 -0
  241. package/dist/storage/core/storageValidation.d.ts +77 -0
  242. package/dist/storage/core/storageValidation.d.ts.map +1 -0
  243. package/dist/storage/core/storageValidation.js +303 -0
  244. package/dist/storage/core/storageValidation.js.map +1 -0
  245. package/dist/storage/providers/cloudflare/d1Provider.d.ts +94 -0
  246. package/dist/storage/providers/cloudflare/d1Provider.d.ts.map +1 -0
  247. package/dist/storage/providers/cloudflare/d1Provider.js +347 -0
  248. package/dist/storage/providers/cloudflare/d1Provider.js.map +1 -0
  249. package/dist/storage/providers/cloudflare/kvProvider.d.ts +21 -0
  250. package/dist/storage/providers/cloudflare/kvProvider.d.ts.map +1 -0
  251. package/dist/storage/providers/cloudflare/kvProvider.js +183 -0
  252. package/dist/storage/providers/cloudflare/kvProvider.js.map +1 -0
  253. package/dist/storage/providers/cloudflare/r2Provider.d.ts +28 -0
  254. package/dist/storage/providers/cloudflare/r2Provider.d.ts.map +1 -0
  255. package/dist/storage/providers/cloudflare/r2Provider.js +222 -0
  256. package/dist/storage/providers/cloudflare/r2Provider.js.map +1 -0
  257. package/dist/storage/providers/fileSystem/fileSystemProvider.d.ts +20 -0
  258. package/dist/storage/providers/fileSystem/fileSystemProvider.d.ts.map +1 -0
  259. package/dist/storage/providers/fileSystem/fileSystemProvider.js +282 -0
  260. package/dist/storage/providers/fileSystem/fileSystemProvider.js.map +1 -0
  261. package/dist/storage/providers/inMemory/inMemoryProvider.d.ts +21 -0
  262. package/dist/storage/providers/inMemory/inMemoryProvider.d.ts.map +1 -0
  263. package/dist/storage/providers/inMemory/inMemoryProvider.js +139 -0
  264. package/dist/storage/providers/inMemory/inMemoryProvider.js.map +1 -0
  265. package/dist/storage/providers/supabase/supabase.types.d.ts +49 -0
  266. package/dist/storage/providers/supabase/supabase.types.d.ts.map +1 -0
  267. package/dist/storage/providers/supabase/supabase.types.js +8 -0
  268. package/dist/storage/providers/supabase/supabase.types.js.map +1 -0
  269. package/dist/storage/providers/supabase/supabaseProvider.d.ts +24 -0
  270. package/dist/storage/providers/supabase/supabaseProvider.d.ts.map +1 -0
  271. package/dist/storage/providers/supabase/supabaseProvider.js +209 -0
  272. package/dist/storage/providers/supabase/supabaseProvider.js.map +1 -0
  273. package/dist/testing/index.d.ts +53 -0
  274. package/dist/testing/index.d.ts.map +1 -0
  275. package/dist/testing/index.js +132 -0
  276. package/dist/testing/index.js.map +1 -0
  277. package/dist/types-global/errors.d.ts +83 -0
  278. package/dist/types-global/errors.d.ts.map +1 -0
  279. package/dist/types-global/errors.js +113 -0
  280. package/dist/types-global/errors.js.map +1 -0
  281. package/dist/utils/formatting/diffFormatter.d.ts +227 -0
  282. package/dist/utils/formatting/diffFormatter.d.ts.map +1 -0
  283. package/dist/utils/formatting/diffFormatter.js +369 -0
  284. package/dist/utils/formatting/diffFormatter.js.map +1 -0
  285. package/dist/utils/formatting/index.d.ts +9 -0
  286. package/dist/utils/formatting/index.d.ts.map +1 -0
  287. package/dist/utils/formatting/index.js +9 -0
  288. package/dist/utils/formatting/index.js.map +1 -0
  289. package/dist/utils/formatting/markdownBuilder.d.ts +543 -0
  290. package/dist/utils/formatting/markdownBuilder.d.ts.map +1 -0
  291. package/dist/utils/formatting/markdownBuilder.js +674 -0
  292. package/dist/utils/formatting/markdownBuilder.js.map +1 -0
  293. package/dist/utils/formatting/tableFormatter.d.ts +261 -0
  294. package/dist/utils/formatting/tableFormatter.d.ts.map +1 -0
  295. package/dist/utils/formatting/tableFormatter.js +456 -0
  296. package/dist/utils/formatting/tableFormatter.js.map +1 -0
  297. package/dist/utils/formatting/treeFormatter.d.ts +344 -0
  298. package/dist/utils/formatting/treeFormatter.d.ts.map +1 -0
  299. package/dist/utils/formatting/treeFormatter.js +400 -0
  300. package/dist/utils/formatting/treeFormatter.js.map +1 -0
  301. package/dist/utils/internal/encoding.d.ts +42 -0
  302. package/dist/utils/internal/encoding.d.ts.map +1 -0
  303. package/dist/utils/internal/encoding.js +87 -0
  304. package/dist/utils/internal/encoding.js.map +1 -0
  305. package/dist/utils/internal/error-handler/errorHandler.d.ts +140 -0
  306. package/dist/utils/internal/error-handler/errorHandler.d.ts.map +1 -0
  307. package/dist/utils/internal/error-handler/errorHandler.js +318 -0
  308. package/dist/utils/internal/error-handler/errorHandler.js.map +1 -0
  309. package/dist/utils/internal/error-handler/helpers.d.ts +98 -0
  310. package/dist/utils/internal/error-handler/helpers.d.ts.map +1 -0
  311. package/dist/utils/internal/error-handler/helpers.js +214 -0
  312. package/dist/utils/internal/error-handler/helpers.js.map +1 -0
  313. package/dist/utils/internal/error-handler/mappings.d.ts +85 -0
  314. package/dist/utils/internal/error-handler/mappings.d.ts.map +1 -0
  315. package/dist/utils/internal/error-handler/mappings.js +234 -0
  316. package/dist/utils/internal/error-handler/mappings.js.map +1 -0
  317. package/dist/utils/internal/error-handler/types.d.ts +160 -0
  318. package/dist/utils/internal/error-handler/types.d.ts.map +1 -0
  319. package/dist/utils/internal/error-handler/types.js +6 -0
  320. package/dist/utils/internal/error-handler/types.js.map +1 -0
  321. package/dist/utils/internal/health.d.ts +60 -0
  322. package/dist/utils/internal/health.d.ts.map +1 -0
  323. package/dist/utils/internal/health.js +46 -0
  324. package/dist/utils/internal/health.js.map +1 -0
  325. package/dist/utils/internal/logger.d.ts +300 -0
  326. package/dist/utils/internal/logger.d.ts.map +1 -0
  327. package/dist/utils/internal/logger.js +573 -0
  328. package/dist/utils/internal/logger.js.map +1 -0
  329. package/dist/utils/internal/performance.d.ts +78 -0
  330. package/dist/utils/internal/performance.d.ts.map +1 -0
  331. package/dist/utils/internal/performance.js +227 -0
  332. package/dist/utils/internal/performance.js.map +1 -0
  333. package/dist/utils/internal/requestContext.d.ts +200 -0
  334. package/dist/utils/internal/requestContext.d.ts.map +1 -0
  335. package/dist/utils/internal/requestContext.js +163 -0
  336. package/dist/utils/internal/requestContext.js.map +1 -0
  337. package/dist/utils/internal/runtime.d.ts +49 -0
  338. package/dist/utils/internal/runtime.d.ts.map +1 -0
  339. package/dist/utils/internal/runtime.js +90 -0
  340. package/dist/utils/internal/runtime.js.map +1 -0
  341. package/dist/utils/internal/startupBanner.d.ts +23 -0
  342. package/dist/utils/internal/startupBanner.d.ts.map +1 -0
  343. package/dist/utils/internal/startupBanner.js +34 -0
  344. package/dist/utils/internal/startupBanner.js.map +1 -0
  345. package/dist/utils/metrics/tokenCounter.d.ts +97 -0
  346. package/dist/utils/metrics/tokenCounter.d.ts.map +1 -0
  347. package/dist/utils/metrics/tokenCounter.js +162 -0
  348. package/dist/utils/metrics/tokenCounter.js.map +1 -0
  349. package/dist/utils/network/fetchWithTimeout.d.ts +91 -0
  350. package/dist/utils/network/fetchWithTimeout.d.ts.map +1 -0
  351. package/dist/utils/network/fetchWithTimeout.js +305 -0
  352. package/dist/utils/network/fetchWithTimeout.js.map +1 -0
  353. package/dist/utils/pagination/pagination.d.ts +157 -0
  354. package/dist/utils/pagination/pagination.d.ts.map +1 -0
  355. package/dist/utils/pagination/pagination.js +191 -0
  356. package/dist/utils/pagination/pagination.js.map +1 -0
  357. package/dist/utils/parsing/csvParser.d.ts +84 -0
  358. package/dist/utils/parsing/csvParser.d.ts.map +1 -0
  359. package/dist/utils/parsing/csvParser.js +132 -0
  360. package/dist/utils/parsing/csvParser.js.map +1 -0
  361. package/dist/utils/parsing/dateParser.d.ts +103 -0
  362. package/dist/utils/parsing/dateParser.d.ts.map +1 -0
  363. package/dist/utils/parsing/dateParser.js +142 -0
  364. package/dist/utils/parsing/dateParser.js.map +1 -0
  365. package/dist/utils/parsing/frontmatterParser.d.ts +91 -0
  366. package/dist/utils/parsing/frontmatterParser.d.ts.map +1 -0
  367. package/dist/utils/parsing/frontmatterParser.js +163 -0
  368. package/dist/utils/parsing/frontmatterParser.js.map +1 -0
  369. package/dist/utils/parsing/index.d.ts +15 -0
  370. package/dist/utils/parsing/index.d.ts.map +1 -0
  371. package/dist/utils/parsing/index.js +15 -0
  372. package/dist/utils/parsing/index.js.map +1 -0
  373. package/dist/utils/parsing/jsonParser.d.ts +115 -0
  374. package/dist/utils/parsing/jsonParser.d.ts.map +1 -0
  375. package/dist/utils/parsing/jsonParser.js +177 -0
  376. package/dist/utils/parsing/jsonParser.js.map +1 -0
  377. package/dist/utils/parsing/pdfParser.d.ts +563 -0
  378. package/dist/utils/parsing/pdfParser.d.ts.map +1 -0
  379. package/dist/utils/parsing/pdfParser.js +775 -0
  380. package/dist/utils/parsing/pdfParser.js.map +1 -0
  381. package/dist/utils/parsing/thinkBlock.d.ts +31 -0
  382. package/dist/utils/parsing/thinkBlock.d.ts.map +1 -0
  383. package/dist/utils/parsing/thinkBlock.js +31 -0
  384. package/dist/utils/parsing/thinkBlock.js.map +1 -0
  385. package/dist/utils/parsing/xmlParser.d.ts +69 -0
  386. package/dist/utils/parsing/xmlParser.d.ts.map +1 -0
  387. package/dist/utils/parsing/xmlParser.js +140 -0
  388. package/dist/utils/parsing/xmlParser.js.map +1 -0
  389. package/dist/utils/parsing/yamlParser.d.ts +64 -0
  390. package/dist/utils/parsing/yamlParser.d.ts.map +1 -0
  391. package/dist/utils/parsing/yamlParser.js +129 -0
  392. package/dist/utils/parsing/yamlParser.js.map +1 -0
  393. package/dist/utils/scheduling/scheduler.d.ts +174 -0
  394. package/dist/utils/scheduling/scheduler.d.ts.map +1 -0
  395. package/dist/utils/scheduling/scheduler.js +248 -0
  396. package/dist/utils/scheduling/scheduler.js.map +1 -0
  397. package/dist/utils/security/idGenerator.d.ts +189 -0
  398. package/dist/utils/security/idGenerator.d.ts.map +1 -0
  399. package/dist/utils/security/idGenerator.js +301 -0
  400. package/dist/utils/security/idGenerator.js.map +1 -0
  401. package/dist/utils/security/index.d.ts +8 -0
  402. package/dist/utils/security/index.d.ts.map +1 -0
  403. package/dist/utils/security/index.js +8 -0
  404. package/dist/utils/security/index.js.map +1 -0
  405. package/dist/utils/security/rateLimiter.d.ts +171 -0
  406. package/dist/utils/security/rateLimiter.d.ts.map +1 -0
  407. package/dist/utils/security/rateLimiter.js +294 -0
  408. package/dist/utils/security/rateLimiter.js.map +1 -0
  409. package/dist/utils/security/sanitization.d.ts +430 -0
  410. package/dist/utils/security/sanitization.d.ts.map +1 -0
  411. package/dist/utils/security/sanitization.js +759 -0
  412. package/dist/utils/security/sanitization.js.map +1 -0
  413. package/dist/utils/telemetry/index.d.ts +12 -0
  414. package/dist/utils/telemetry/index.d.ts.map +1 -0
  415. package/dist/utils/telemetry/index.js +12 -0
  416. package/dist/utils/telemetry/index.js.map +1 -0
  417. package/dist/utils/telemetry/instrumentation.d.ts +62 -0
  418. package/dist/utils/telemetry/instrumentation.d.ts.map +1 -0
  419. package/dist/utils/telemetry/instrumentation.js +223 -0
  420. package/dist/utils/telemetry/instrumentation.js.map +1 -0
  421. package/dist/utils/telemetry/metrics.d.ts +170 -0
  422. package/dist/utils/telemetry/metrics.d.ts.map +1 -0
  423. package/dist/utils/telemetry/metrics.js +205 -0
  424. package/dist/utils/telemetry/metrics.js.map +1 -0
  425. package/dist/utils/telemetry/semconv.d.ts +147 -0
  426. package/dist/utils/telemetry/semconv.d.ts.map +1 -0
  427. package/dist/utils/telemetry/semconv.js +159 -0
  428. package/dist/utils/telemetry/semconv.js.map +1 -0
  429. package/dist/utils/telemetry/trace.d.ts +141 -0
  430. package/dist/utils/telemetry/trace.d.ts.map +1 -0
  431. package/dist/utils/telemetry/trace.js +193 -0
  432. package/dist/utils/telemetry/trace.js.map +1 -0
  433. package/dist/utils/types/guards.d.ts +209 -0
  434. package/dist/utils/types/guards.d.ts.map +1 -0
  435. package/dist/utils/types/guards.js +229 -0
  436. package/dist/utils/types/guards.js.map +1 -0
  437. package/dist/utils/types/index.d.ts +6 -0
  438. package/dist/utils/types/index.d.ts.map +1 -0
  439. package/dist/utils/types/index.js +6 -0
  440. package/dist/utils/types/index.js.map +1 -0
  441. package/dist/worker.d.ts +59 -0
  442. package/dist/worker.d.ts.map +1 -0
  443. package/dist/worker.js +216 -0
  444. package/dist/worker.js.map +1 -0
  445. package/package.json +377 -0
  446. package/skills/README.md +38 -0
  447. package/skills/add-export/SKILL.md +49 -0
  448. package/skills/add-prompt/SKILL.md +97 -0
  449. package/skills/add-provider/SKILL.md +53 -0
  450. package/skills/add-resource/SKILL.md +107 -0
  451. package/skills/add-service/SKILL.md +113 -0
  452. package/skills/add-tool/SKILL.md +110 -0
  453. package/skills/api-auth/SKILL.md +173 -0
  454. package/skills/api-config/SKILL.md +68 -0
  455. package/skills/api-context/SKILL.md +321 -0
  456. package/skills/api-errors/SKILL.md +146 -0
  457. package/skills/api-services/SKILL.md +24 -0
  458. package/skills/api-services/references/graph.md +124 -0
  459. package/skills/api-services/references/llm.md +46 -0
  460. package/skills/api-services/references/speech.md +72 -0
  461. package/skills/api-testing/SKILL.md +263 -0
  462. package/skills/api-utils/SKILL.md +106 -0
  463. package/skills/api-utils/references/formatting.md +237 -0
  464. package/skills/api-utils/references/parsing.md +263 -0
  465. package/skills/api-utils/references/security.md +226 -0
  466. package/skills/api-workers/SKILL.md +165 -0
  467. package/skills/devcheck/SKILL.md +31 -0
  468. package/skills/maintenance/SKILL.md +52 -0
  469. package/skills/migrate-mcp-ts-template/SKILL.md +131 -0
  470. package/skills/release/SKILL.md +67 -0
  471. package/skills/setup/SKILL.md +89 -0
  472. package/skills/walkthrough-init/SKILL.md +50 -0
  473. package/templates/.env.example +17 -0
  474. package/templates/AGENTS.md +113 -0
  475. package/templates/CLAUDE.md +113 -0
  476. package/templates/_tsconfig.json +33 -0
  477. package/templates/biome.template.json +43 -0
  478. package/templates/package.json +26 -0
  479. package/templates/src/index.ts +16 -0
  480. package/templates/src/mcp-server/prompts/definitions/echo.prompt.ts +19 -0
  481. package/templates/src/mcp-server/resources/definitions/echo.resource.ts +30 -0
  482. package/templates/src/mcp-server/tools/definitions/echo.tool.ts +24 -0
  483. package/templates/vitest.config.ts +12 -0
  484. package/tsconfig.base.json +44 -0
  485. package/vitest.config.base.ts +38 -0
@@ -0,0 +1,305 @@
1
+ /**
2
+ * @fileoverview Provides a utility function to make fetch requests with a specified timeout
3
+ * and optional SSRF protection including DNS resolution validation and redirect following.
4
+ * @module src/utils/network/fetchWithTimeout
5
+ */
6
+ import { JsonRpcErrorCode, McpError } from '../../types-global/errors.js';
7
+ import { logger } from '../../utils/internal/logger.js';
8
+ import { runtimeCaps } from '../../utils/internal/runtime.js';
9
+ /**
10
+ * IPv4 patterns for private/reserved ranges that should be blocked when
11
+ * `rejectPrivateIPs` is enabled. Covers RFC 1918, loopback, link-local,
12
+ * and cloud metadata endpoints.
13
+ */
14
+ const PRIVATE_IP_PATTERNS = [
15
+ /^127\./, // Loopback
16
+ /^10\./, // RFC 1918 Class A
17
+ /^172\.(1[6-9]|2\d|3[01])\./, // RFC 1918 Class B
18
+ /^192\.168\./, // RFC 1918 Class C
19
+ /^169\.254\./, // Link-local / cloud metadata
20
+ /^0\./, // Current network
21
+ /^100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\./, // RFC 6598 (CGNAT)
22
+ ];
23
+ /** IPv6 prefixes for private/reserved ranges (checked against DNS-resolved addresses). */
24
+ const PRIVATE_IPV6_PREFIXES = [
25
+ 'fe80:', // Link-local
26
+ 'fc', // Unique local (fc00::/7)
27
+ 'fd', // Unique local (fc00::/7)
28
+ '::1', // Loopback
29
+ '::ffff:127.', // IPv4-mapped loopback
30
+ '::ffff:10.', // IPv4-mapped RFC 1918
31
+ '::ffff:192.168.', // IPv4-mapped RFC 1918
32
+ '::ffff:172.16.', // IPv4-mapped RFC 1918 (partial)
33
+ '::ffff:169.254.', // IPv4-mapped link-local
34
+ ];
35
+ const PRIVATE_HOSTNAMES = new Set(['localhost', 'metadata.google.internal', 'metadata.internal']);
36
+ /** Maximum number of redirects to follow when rejectPrivateIPs is enabled. */
37
+ const MAX_SSRF_REDIRECTS = 5;
38
+ /**
39
+ * Checks whether a resolved IP address (v4 or v6) falls within a private or
40
+ * reserved range as defined by {@link PRIVATE_IP_PATTERNS} and
41
+ * {@link PRIVATE_IPV6_PREFIXES}.
42
+ *
43
+ * @param ip - The IP address string to check (bare, no brackets).
44
+ * @returns `true` if the address is private or reserved, `false` otherwise.
45
+ */
46
+ function isPrivateIP(ip) {
47
+ if (PRIVATE_IP_PATTERNS.some((pattern) => pattern.test(ip)))
48
+ return true;
49
+ const lower = ip.toLowerCase();
50
+ if (PRIVATE_IPV6_PREFIXES.some((prefix) => lower.startsWith(prefix)))
51
+ return true;
52
+ return false;
53
+ }
54
+ /**
55
+ * Validates that a URL string does not target private or reserved IP space.
56
+ *
57
+ * Performs three checks in order:
58
+ * 1. Known private hostnames (`localhost`, `metadata.google.internal`, etc.)
59
+ * 2. Literal IPv4/IPv6 addresses in the URL hostname
60
+ * 3. DNS resolution (Node.js only) — resolves A and AAAA records and validates each
61
+ *
62
+ * DNS resolution failures (ENOTFOUND, etc.) are swallowed and left for the native
63
+ * `fetch` to handle; only confirmed private IPs cause rejection.
64
+ *
65
+ * @param urlString - The fully-qualified URL string to validate.
66
+ * @throws {McpError} `ValidationError` if the URL is malformed, the hostname is a known
67
+ * internal name, the literal IP is private, or DNS resolves to a private address.
68
+ */
69
+ async function assertNotPrivateUrl(urlString) {
70
+ let parsed;
71
+ try {
72
+ parsed = new URL(urlString);
73
+ }
74
+ catch {
75
+ throw new McpError(JsonRpcErrorCode.ValidationError, `Invalid URL: ${urlString}`);
76
+ }
77
+ const hostname = parsed.hostname.replace(/^\[|\]$/g, ''); // Strip IPv6 brackets
78
+ // Check known private hostnames
79
+ if (PRIVATE_HOSTNAMES.has(hostname.toLowerCase())) {
80
+ throw new McpError(JsonRpcErrorCode.ValidationError, `Request to private/internal hostname blocked: ${hostname}`);
81
+ }
82
+ // Check IPv6 loopback
83
+ if (hostname === '::1' || hostname === '0:0:0:0:0:0:0:1') {
84
+ throw new McpError(JsonRpcErrorCode.ValidationError, `Request to loopback address blocked: ${hostname}`);
85
+ }
86
+ // Check IPv4 private ranges (hostname as literal IP)
87
+ if (PRIVATE_IP_PATTERNS.some((pattern) => pattern.test(hostname))) {
88
+ throw new McpError(JsonRpcErrorCode.ValidationError, `Request to private/reserved IP blocked: ${hostname}`);
89
+ }
90
+ // DNS resolution check (Node.js only — Workers have no DNS API)
91
+ if (runtimeCaps.isNode) {
92
+ await assertDnsNotPrivate(hostname);
93
+ }
94
+ }
95
+ /**
96
+ * Resolves DNS for a hostname (A and AAAA records in parallel) and confirms
97
+ * that none of the resolved IP addresses fall within private or reserved ranges.
98
+ *
99
+ * DNS resolution errors (e.g., `ENOTFOUND`) are silently swallowed — they are
100
+ * not an SSRF signal and are better handled by the native `fetch` call.
101
+ *
102
+ * @param hostname - The bare hostname to resolve (no brackets, no port).
103
+ * @throws {McpError} `ValidationError` if any resolved address is private.
104
+ */
105
+ async function assertDnsNotPrivate(hostname) {
106
+ try {
107
+ const dns = await import('node:dns/promises');
108
+ const [ipv4Results, ipv6Results] = await Promise.allSettled([
109
+ dns.resolve4(hostname),
110
+ dns.resolve6(hostname),
111
+ ]);
112
+ const resolvedIPs = [
113
+ ...(ipv4Results.status === 'fulfilled' ? ipv4Results.value : []),
114
+ ...(ipv6Results.status === 'fulfilled' ? ipv6Results.value : []),
115
+ ];
116
+ for (const ip of resolvedIPs) {
117
+ if (isPrivateIP(ip)) {
118
+ throw new McpError(JsonRpcErrorCode.ValidationError, `DNS resolved ${hostname} to private IP ${ip} — SSRF blocked`);
119
+ }
120
+ }
121
+ }
122
+ catch (error) {
123
+ if (error instanceof McpError)
124
+ throw error;
125
+ // DNS resolution failures (ENOTFOUND, etc.) are not SSRF — let fetch handle them
126
+ }
127
+ }
128
+ /**
129
+ * Fetches a resource with a configurable timeout and optional SSRF protection.
130
+ *
131
+ * Internally manages an `AbortController` that fires after `timeoutMs`. An optional
132
+ * external `signal` (e.g., `ctx.signal`) can be passed via `options` to support
133
+ * early cancellation by the caller. The two signals are composed — whichever fires
134
+ * first wins.
135
+ *
136
+ * When `options.rejectPrivateIPs` is `true`, the target URL is validated before the
137
+ * request is sent, and all redirects are followed manually with per-hop SSRF checks
138
+ * (up to 5 hops). This mode forces `redirect: 'manual'` on the underlying fetch.
139
+ *
140
+ * Non-2xx responses are treated as errors: the response body is read, logged, and
141
+ * wrapped in a `McpError` with code `ServiceUnavailable`.
142
+ *
143
+ * @param url - The URL to fetch (string or `URL` instance).
144
+ * @param timeoutMs - Maximum duration in milliseconds before the request is aborted.
145
+ * @param context - Request context used for structured logging (requestId, operation, etc.).
146
+ * @param options - Optional fetch configuration extending `RequestInit`.
147
+ * - `rejectPrivateIPs`: Block requests to private/internal IP space (SSRF protection).
148
+ * - `signal`: External `AbortSignal` to cancel the request independently of the timeout.
149
+ * - All other standard `RequestInit` fields (method, headers, body, etc.) are forwarded.
150
+ * @returns A promise resolving to the `Response` object on HTTP 2xx.
151
+ * @throws {McpError} `ValidationError` if the URL targets a private/reserved address
152
+ * and `rejectPrivateIPs` is enabled.
153
+ * @throws {McpError} `Timeout` if the request exceeds `timeoutMs`.
154
+ * @throws {McpError} `InternalError` if the request is cancelled via the external signal.
155
+ * @throws {McpError} `ServiceUnavailable` if the server returns a non-2xx status or a
156
+ * network-level error occurs.
157
+ * @example
158
+ * ```ts
159
+ * // Basic GET with a 5-second timeout
160
+ * const response = await fetchWithTimeout(
161
+ * 'https://api.example.com/data',
162
+ * 5000,
163
+ * ctx,
164
+ * );
165
+ * const data = await response.json();
166
+ *
167
+ * // POST with SSRF protection and caller-cancellable signal
168
+ * const response = await fetchWithTimeout(
169
+ * userProvidedUrl,
170
+ * 10_000,
171
+ * ctx,
172
+ * {
173
+ * method: 'POST',
174
+ * headers: { 'Content-Type': 'application/json' },
175
+ * body: JSON.stringify(payload),
176
+ * rejectPrivateIPs: true,
177
+ * signal: ctx.signal,
178
+ * },
179
+ * );
180
+ * ```
181
+ */
182
+ export async function fetchWithTimeout(url, timeoutMs, context, options) {
183
+ const urlString = url.toString();
184
+ // SSRF protection: reject private/internal targets when enabled
185
+ if (options?.rejectPrivateIPs) {
186
+ await assertNotPrivateUrl(urlString);
187
+ }
188
+ const operationDescription = `fetch ${options?.method || 'GET'} ${urlString}`;
189
+ logger.debug(`Attempting ${operationDescription} with ${timeoutMs}ms timeout.`, context);
190
+ // Strip custom options before passing to native fetch
191
+ const { rejectPrivateIPs: rejectPrivate, signal: externalSignal, ...fetchInit } = options ?? {};
192
+ // When SSRF protection is active, handle redirects manually to validate each hop
193
+ if (rejectPrivate) {
194
+ fetchInit.redirect = 'manual';
195
+ }
196
+ // Use AbortController instead of AbortSignal.timeout() for cross-runtime compatibility
197
+ // (AbortSignal.timeout() can fail in Bun's stdio transport due to realm mismatch)
198
+ const controller = new AbortController();
199
+ const timeoutSentinel = 'FETCH_TIMEOUT';
200
+ const timeoutId = setTimeout(() => controller.abort(timeoutSentinel), timeoutMs);
201
+ // If an external signal is provided (e.g., client disconnect), forward its abort
202
+ if (externalSignal) {
203
+ if (externalSignal.aborted) {
204
+ controller.abort(externalSignal.reason);
205
+ }
206
+ else {
207
+ externalSignal.addEventListener('abort', () => controller.abort(externalSignal.reason), {
208
+ once: true,
209
+ signal: controller.signal,
210
+ });
211
+ }
212
+ }
213
+ try {
214
+ let currentUrl = url;
215
+ let redirectCount = 0;
216
+ for (;;) {
217
+ const response = await fetch(currentUrl, {
218
+ ...fetchInit,
219
+ signal: controller.signal,
220
+ });
221
+ // Handle redirects manually when SSRF protection is active
222
+ if (rejectPrivate && response.status >= 300 && response.status < 400) {
223
+ const location = response.headers.get('location');
224
+ if (!location) {
225
+ throw new McpError(JsonRpcErrorCode.ServiceUnavailable, `Redirect response missing Location header from ${String(currentUrl)}`);
226
+ }
227
+ redirectCount++;
228
+ if (redirectCount > MAX_SSRF_REDIRECTS) {
229
+ throw new McpError(JsonRpcErrorCode.ValidationError, `Too many redirects (${MAX_SSRF_REDIRECTS}) — possible SSRF redirect loop`);
230
+ }
231
+ // Resolve relative redirect URLs against the current URL
232
+ const redirectUrl = new URL(location, currentUrl.toString()).toString();
233
+ // Validate the redirect target against SSRF rules
234
+ await assertNotPrivateUrl(redirectUrl);
235
+ logger.debug(`Following validated redirect ${redirectCount}: ${redirectUrl}`, context);
236
+ currentUrl = redirectUrl;
237
+ continue;
238
+ }
239
+ if (!response.ok) {
240
+ const errorBody = await response.text().catch(() => 'Could not read response body');
241
+ logger.error(`Fetch failed for ${String(currentUrl)} with status ${response.status}.`, {
242
+ ...context,
243
+ statusCode: response.status,
244
+ statusText: response.statusText,
245
+ responseBody: errorBody,
246
+ errorSource: 'FetchHttpError',
247
+ });
248
+ throw new McpError(JsonRpcErrorCode.ServiceUnavailable, `Fetch failed for ${String(currentUrl)}. Status: ${response.status}`, {
249
+ requestId: context.requestId,
250
+ operation: context.operation,
251
+ statusCode: response.status,
252
+ statusText: response.statusText,
253
+ responseBody: errorBody,
254
+ });
255
+ }
256
+ logger.debug(`Successfully fetched ${String(currentUrl)}. Status: ${response.status}`, context);
257
+ return response;
258
+ }
259
+ }
260
+ catch (error) {
261
+ if (error instanceof Error && (error.name === 'TimeoutError' || error.name === 'AbortError')) {
262
+ const isTimeout = error.name === 'TimeoutError' || controller.signal.reason === timeoutSentinel;
263
+ if (isTimeout) {
264
+ logger.error(`${operationDescription} timed out after ${timeoutMs}ms.`, {
265
+ ...context,
266
+ errorSource: 'FetchTimeout',
267
+ });
268
+ throw new McpError(JsonRpcErrorCode.Timeout, `${operationDescription} timed out.`, {
269
+ requestId: context.requestId,
270
+ operation: context.operation,
271
+ errorSource: 'FetchTimeout',
272
+ });
273
+ }
274
+ // External signal abort (e.g., client disconnect) — not a timeout
275
+ logger.info(`${operationDescription} aborted by caller.`, {
276
+ ...context,
277
+ errorSource: 'FetchAborted',
278
+ });
279
+ throw new McpError(JsonRpcErrorCode.InternalError, `${operationDescription} was aborted.`, {
280
+ requestId: context.requestId,
281
+ operation: context.operation,
282
+ errorSource: 'FetchAborted',
283
+ });
284
+ }
285
+ const errorMessage = error instanceof Error ? error.message : String(error);
286
+ logger.error(`Network error during ${operationDescription}: ${errorMessage}`, {
287
+ ...context,
288
+ originalErrorName: error instanceof Error ? error.name : 'UnknownError',
289
+ errorSource: 'FetchNetworkError',
290
+ });
291
+ if (error instanceof McpError) {
292
+ throw error;
293
+ }
294
+ throw new McpError(JsonRpcErrorCode.ServiceUnavailable, `Network error during ${operationDescription}: ${errorMessage}`, {
295
+ requestId: context.requestId,
296
+ operation: context.operation,
297
+ originalErrorName: error instanceof Error ? error.name : 'UnknownError',
298
+ errorSource: 'FetchNetworkErrorWrapper',
299
+ });
300
+ }
301
+ finally {
302
+ clearTimeout(timeoutId);
303
+ }
304
+ }
305
+ //# sourceMappingURL=fetchWithTimeout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchWithTimeout.js","sourceRoot":"","sources":["../../../src/utils/network/fetchWithTimeout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * @fileoverview Pagination utilities for MCP list operations.
3
+ * Implements cursor-based pagination per MCP specification 2025-06-18.
4
+ *
5
+ * MCP Pagination Model:
6
+ * - Opaque cursor-based approach (not numbered pages)
7
+ * - Cursor is an opaque string token representing a position in the result set
8
+ * - Page size is determined by server (clients MUST NOT assume fixed page size)
9
+ * - Invalid cursors should result in error code -32602 (Invalid params)
10
+ *
11
+ * @see {@link https://modelcontextprotocol.io/specification/2025-06-18/utils/pagination | MCP Pagination Spec}
12
+ * @module src/utils/pagination/pagination
13
+ */
14
+ import type { RequestContext } from '../../utils/internal/requestContext.js';
15
+ /**
16
+ * Generic pagination state that can be encoded into a cursor.
17
+ * Implementations can extend this with additional fields as needed.
18
+ * This is the data structure serialized into the opaque cursor token.
19
+ */
20
+ export interface PaginationState {
21
+ /** Maximum number of items per page; must be a positive integer */
22
+ limit: number;
23
+ /** Zero-based index of the first item in the current page */
24
+ offset: number;
25
+ /** Optional additional state preserved across pages (implementation-specific) */
26
+ [key: string]: unknown;
27
+ }
28
+ /**
29
+ * Result of a paginated operation.
30
+ * Returned by {@link paginateArray} and should be the shape returned by MCP list handlers.
31
+ * Per MCP spec, `nextCursor` must be omitted (not set to `null` or `""`) when no further pages exist.
32
+ */
33
+ export interface PaginatedResult<T> {
34
+ /** Items for the current page */
35
+ items: T[];
36
+ /**
37
+ * Opaque cursor token for the next page.
38
+ * Omitted entirely when this is the last page — do not set to `null` or `""`.
39
+ */
40
+ nextCursor?: string;
41
+ /**
42
+ * Total item count across all pages, if cheaply available.
43
+ * Optional — some backends cannot compute this efficiently.
44
+ */
45
+ totalCount?: number;
46
+ }
47
+ /**
48
+ * Encodes pagination state into an opaque base64url cursor string.
49
+ * Serializes `state` to JSON, then encodes as base64url (URL-safe, no padding).
50
+ * The format is an implementation detail — callers must treat the returned string as opaque.
51
+ *
52
+ * @param state - Pagination state to encode; must have `offset >= 0` and `limit > 0`
53
+ * @returns Base64url-encoded cursor string (no `+`, `/`, or `=` characters)
54
+ * @throws {McpError} With code `InternalError` (-32603) if JSON serialization fails
55
+ * @example
56
+ * const cursor = encodeCursor({ offset: 50, limit: 25 });
57
+ * // cursor is an opaque string like "eyJvZmZzZXQiOjUwLCJsaW1pdCI6MjV9"
58
+ */
59
+ export declare function encodeCursor(state: PaginationState): string;
60
+ /**
61
+ * Decodes an opaque cursor string back into pagination state.
62
+ * Reverses base64url encoding, parses the JSON, and validates the required fields:
63
+ * `offset` must be a non-negative number and `limit` must be a positive number.
64
+ * Logs a warning and throws `InvalidParams` on any failure, per MCP spec.
65
+ *
66
+ * @param cursor - Opaque cursor string previously returned by {@link encodeCursor}
67
+ * @param context - Request context used to correlate warning log entries
68
+ * @returns Validated `PaginationState` decoded from the cursor
69
+ * @throws {McpError} With code `InvalidParams` (-32602) if the cursor is malformed,
70
+ * base64-invalid, not valid JSON, or has an invalid `offset`/`limit` structure
71
+ * @example
72
+ * const state = decodeCursor(req.params.cursor, ctx);
73
+ * // state.offset and state.limit are safe to use directly
74
+ */
75
+ export declare function decodeCursor(cursor: string, context: RequestContext): PaginationState;
76
+ /**
77
+ * Extracts the cursor parameter from an MCP request's params object.
78
+ * Checks `params.cursor` first, then falls back to `params._meta.cursor`,
79
+ * matching both locations where MCP clients may supply the cursor token.
80
+ *
81
+ * @param params - Optional request params object; may contain `cursor` at the top
82
+ * level or nested under `_meta`
83
+ * @returns The cursor string if found in either location, `undefined` otherwise
84
+ * @example
85
+ * // Top-level cursor
86
+ * extractCursor({ cursor: 'abc123' }); // => 'abc123'
87
+ *
88
+ * // Nested under _meta
89
+ * extractCursor({ _meta: { cursor: 'abc123' } }); // => 'abc123'
90
+ *
91
+ * // No cursor present
92
+ * extractCursor({}); // => undefined
93
+ * extractCursor(undefined); // => undefined
94
+ */
95
+ export declare function extractCursor(params?: {
96
+ cursor?: string;
97
+ _meta?: {
98
+ cursor?: string;
99
+ };
100
+ }): string | undefined;
101
+ /**
102
+ * Paginates an in-memory array using opaque cursor-based pagination.
103
+ * Decodes the cursor (if provided) to determine offset and limit, slices the array,
104
+ * and returns a {@link PaginatedResult} with `nextCursor` set only when more items follow.
105
+ * When `cursorStr` is `undefined`, pagination starts from the beginning using `defaultPageSize`.
106
+ * When the decoded cursor's offset is beyond the array length, returns an empty items array.
107
+ * The cursor's `limit` is clamped to `maxPageSize` even if the encoded value is larger.
108
+ *
109
+ * @param items - Complete array of items to paginate; not mutated
110
+ * @param cursorStr - Opaque cursor token from the client, or `undefined` for the first page
111
+ * @param defaultPageSize - Page size to use when no cursor is present; must be a positive integer
112
+ * @param maxPageSize - Upper bound on items per page; cursor limit values are clamped to this
113
+ * @param context - Request context used for logging when cursor decoding fails
114
+ * @returns `PaginatedResult<T>` with the current page's items, `totalCount` set to `items.length`,
115
+ * and `nextCursor` included only if additional pages remain
116
+ * @throws {McpError} With code `InvalidParams` (-32602) if `cursorStr` is present but invalid,
117
+ * propagated from {@link decodeCursor}
118
+ * @example
119
+ * const allItems = ['a', 'b', 'c', 'd', 'e'];
120
+ *
121
+ * // First page (no cursor)
122
+ * const page1 = paginateArray(allItems, undefined, 2, 100, ctx);
123
+ * // => { items: ['a', 'b'], nextCursor: '<opaque>', totalCount: 5 }
124
+ *
125
+ * // Second page (using cursor from page1)
126
+ * const page2 = paginateArray(allItems, page1.nextCursor, 2, 100, ctx);
127
+ * // => { items: ['c', 'd'], nextCursor: '<opaque>', totalCount: 5 }
128
+ *
129
+ * // Last page
130
+ * const page3 = paginateArray(allItems, page2.nextCursor, 2, 100, ctx);
131
+ * // => { items: ['e'], totalCount: 5 } (no nextCursor)
132
+ */
133
+ export declare function paginateArray<T>(items: T[], cursorStr: string | undefined, defaultPageSize: number, maxPageSize: number, context: RequestContext): PaginatedResult<T>;
134
+ /**
135
+ * Default pagination configuration values used by {@link paginateArray} callers.
136
+ * These serve as sensible defaults; individual call sites may pass different values
137
+ * or defer to environment-variable-driven config parsed in the config module.
138
+ *
139
+ * @example
140
+ * import { DEFAULT_PAGINATION_CONFIG, paginateArray } from '../../utils/pagination/pagination.js';
141
+ * const result = paginateArray(
142
+ * items,
143
+ * cursorStr,
144
+ * DEFAULT_PAGINATION_CONFIG.DEFAULT_PAGE_SIZE,
145
+ * DEFAULT_PAGINATION_CONFIG.MAX_PAGE_SIZE,
146
+ * ctx,
147
+ * );
148
+ */
149
+ export declare const DEFAULT_PAGINATION_CONFIG: {
150
+ /** Default number of items per page */
151
+ readonly DEFAULT_PAGE_SIZE: 50;
152
+ /** Maximum allowed items per page */
153
+ readonly MAX_PAGE_SIZE: 1000;
154
+ /** Minimum allowed items per page */
155
+ readonly MIN_PAGE_SIZE: 1;
156
+ };
157
+ //# sourceMappingURL=pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../src/utils/pagination/pagination.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,iCAAiC;IACjC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAc3D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,eAAe,CA8BrF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7B,GAAG,MAAM,GAAG,SAAS,CAErB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,KAAK,EAAE,CAAC,EAAE,EACV,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,GACtB,eAAe,CAAC,CAAC,CAAC,CAmCpB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,yBAAyB;IACpC,uCAAuC;;IAEvC,qCAAqC;;IAErC,qCAAqC;;CAE7B,CAAC"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * @fileoverview Pagination utilities for MCP list operations.
3
+ * Implements cursor-based pagination per MCP specification 2025-06-18.
4
+ *
5
+ * MCP Pagination Model:
6
+ * - Opaque cursor-based approach (not numbered pages)
7
+ * - Cursor is an opaque string token representing a position in the result set
8
+ * - Page size is determined by server (clients MUST NOT assume fixed page size)
9
+ * - Invalid cursors should result in error code -32602 (Invalid params)
10
+ *
11
+ * @see {@link https://modelcontextprotocol.io/specification/2025-06-18/utils/pagination | MCP Pagination Spec}
12
+ * @module src/utils/pagination/pagination
13
+ */
14
+ import { JsonRpcErrorCode, McpError } from '../../types-global/errors.js';
15
+ import { base64ToString, stringToBase64 } from '../../utils/internal/encoding.js';
16
+ import { logger } from '../../utils/internal/logger.js';
17
+ /**
18
+ * Encodes pagination state into an opaque base64url cursor string.
19
+ * Serializes `state` to JSON, then encodes as base64url (URL-safe, no padding).
20
+ * The format is an implementation detail — callers must treat the returned string as opaque.
21
+ *
22
+ * @param state - Pagination state to encode; must have `offset >= 0` and `limit > 0`
23
+ * @returns Base64url-encoded cursor string (no `+`, `/`, or `=` characters)
24
+ * @throws {McpError} With code `InternalError` (-32603) if JSON serialization fails
25
+ * @example
26
+ * const cursor = encodeCursor({ offset: 50, limit: 25 });
27
+ * // cursor is an opaque string like "eyJvZmZzZXQiOjUwLCJsaW1pdCI6MjV9"
28
+ */
29
+ export function encodeCursor(state) {
30
+ try {
31
+ const jsonString = JSON.stringify(state);
32
+ // Use cross-platform encoding, then convert standard base64 to base64url
33
+ const base64 = stringToBase64(jsonString)
34
+ .replace(/\+/g, '-')
35
+ .replace(/\//g, '_')
36
+ .replace(/=+$/, '');
37
+ return base64;
38
+ }
39
+ catch (error) {
40
+ throw new McpError(JsonRpcErrorCode.InternalError, 'Failed to encode pagination cursor', {
41
+ error: error instanceof Error ? error.message : String(error),
42
+ });
43
+ }
44
+ }
45
+ /**
46
+ * Decodes an opaque cursor string back into pagination state.
47
+ * Reverses base64url encoding, parses the JSON, and validates the required fields:
48
+ * `offset` must be a non-negative number and `limit` must be a positive number.
49
+ * Logs a warning and throws `InvalidParams` on any failure, per MCP spec.
50
+ *
51
+ * @param cursor - Opaque cursor string previously returned by {@link encodeCursor}
52
+ * @param context - Request context used to correlate warning log entries
53
+ * @returns Validated `PaginationState` decoded from the cursor
54
+ * @throws {McpError} With code `InvalidParams` (-32602) if the cursor is malformed,
55
+ * base64-invalid, not valid JSON, or has an invalid `offset`/`limit` structure
56
+ * @example
57
+ * const state = decodeCursor(req.params.cursor, ctx);
58
+ * // state.offset and state.limit are safe to use directly
59
+ */
60
+ export function decodeCursor(cursor, context) {
61
+ try {
62
+ // Convert base64url back to standard base64, then decode cross-platform
63
+ const standardBase64 = cursor.replace(/-/g, '+').replace(/_/g, '/');
64
+ const jsonString = base64ToString(standardBase64);
65
+ const state = JSON.parse(jsonString);
66
+ // Validate required fields
67
+ if (typeof state.offset !== 'number' ||
68
+ typeof state.limit !== 'number' ||
69
+ state.offset < 0 ||
70
+ state.limit <= 0) {
71
+ throw new Error('Invalid pagination state structure');
72
+ }
73
+ return state;
74
+ }
75
+ catch (error) {
76
+ logger.warning('Failed to decode pagination cursor', {
77
+ ...context,
78
+ cursor,
79
+ error: error instanceof Error ? error.message : String(error),
80
+ });
81
+ throw new McpError(JsonRpcErrorCode.InvalidParams, 'Invalid pagination cursor. The cursor may be expired, corrupted, or from a different request.', { cursor });
82
+ }
83
+ }
84
+ /**
85
+ * Extracts the cursor parameter from an MCP request's params object.
86
+ * Checks `params.cursor` first, then falls back to `params._meta.cursor`,
87
+ * matching both locations where MCP clients may supply the cursor token.
88
+ *
89
+ * @param params - Optional request params object; may contain `cursor` at the top
90
+ * level or nested under `_meta`
91
+ * @returns The cursor string if found in either location, `undefined` otherwise
92
+ * @example
93
+ * // Top-level cursor
94
+ * extractCursor({ cursor: 'abc123' }); // => 'abc123'
95
+ *
96
+ * // Nested under _meta
97
+ * extractCursor({ _meta: { cursor: 'abc123' } }); // => 'abc123'
98
+ *
99
+ * // No cursor present
100
+ * extractCursor({}); // => undefined
101
+ * extractCursor(undefined); // => undefined
102
+ */
103
+ export function extractCursor(params) {
104
+ return params?.cursor ?? params?._meta?.cursor;
105
+ }
106
+ /**
107
+ * Paginates an in-memory array using opaque cursor-based pagination.
108
+ * Decodes the cursor (if provided) to determine offset and limit, slices the array,
109
+ * and returns a {@link PaginatedResult} with `nextCursor` set only when more items follow.
110
+ * When `cursorStr` is `undefined`, pagination starts from the beginning using `defaultPageSize`.
111
+ * When the decoded cursor's offset is beyond the array length, returns an empty items array.
112
+ * The cursor's `limit` is clamped to `maxPageSize` even if the encoded value is larger.
113
+ *
114
+ * @param items - Complete array of items to paginate; not mutated
115
+ * @param cursorStr - Opaque cursor token from the client, or `undefined` for the first page
116
+ * @param defaultPageSize - Page size to use when no cursor is present; must be a positive integer
117
+ * @param maxPageSize - Upper bound on items per page; cursor limit values are clamped to this
118
+ * @param context - Request context used for logging when cursor decoding fails
119
+ * @returns `PaginatedResult<T>` with the current page's items, `totalCount` set to `items.length`,
120
+ * and `nextCursor` included only if additional pages remain
121
+ * @throws {McpError} With code `InvalidParams` (-32602) if `cursorStr` is present but invalid,
122
+ * propagated from {@link decodeCursor}
123
+ * @example
124
+ * const allItems = ['a', 'b', 'c', 'd', 'e'];
125
+ *
126
+ * // First page (no cursor)
127
+ * const page1 = paginateArray(allItems, undefined, 2, 100, ctx);
128
+ * // => { items: ['a', 'b'], nextCursor: '<opaque>', totalCount: 5 }
129
+ *
130
+ * // Second page (using cursor from page1)
131
+ * const page2 = paginateArray(allItems, page1.nextCursor, 2, 100, ctx);
132
+ * // => { items: ['c', 'd'], nextCursor: '<opaque>', totalCount: 5 }
133
+ *
134
+ * // Last page
135
+ * const page3 = paginateArray(allItems, page2.nextCursor, 2, 100, ctx);
136
+ * // => { items: ['e'], totalCount: 5 } (no nextCursor)
137
+ */
138
+ export function paginateArray(items, cursorStr, defaultPageSize, maxPageSize, context) {
139
+ let offset = 0;
140
+ let limit = defaultPageSize;
141
+ // Decode cursor if provided
142
+ if (cursorStr) {
143
+ const state = decodeCursor(cursorStr, context);
144
+ offset = state.offset;
145
+ limit = Math.min(state.limit, maxPageSize); // Enforce max page size
146
+ }
147
+ // Validate bounds
148
+ if (offset >= items.length) {
149
+ return {
150
+ items: [],
151
+ totalCount: items.length,
152
+ };
153
+ }
154
+ // Extract page
155
+ const pageItems = items.slice(offset, offset + limit);
156
+ const hasMore = offset + limit < items.length;
157
+ // Build result, conditionally adding nextCursor only if it exists
158
+ const result = {
159
+ items: pageItems,
160
+ totalCount: items.length,
161
+ };
162
+ // Only add nextCursor if more results exist
163
+ if (hasMore) {
164
+ result.nextCursor = encodeCursor({ offset: offset + limit, limit });
165
+ }
166
+ return result;
167
+ }
168
+ /**
169
+ * Default pagination configuration values used by {@link paginateArray} callers.
170
+ * These serve as sensible defaults; individual call sites may pass different values
171
+ * or defer to environment-variable-driven config parsed in the config module.
172
+ *
173
+ * @example
174
+ * import { DEFAULT_PAGINATION_CONFIG, paginateArray } from '../../utils/pagination/pagination.js';
175
+ * const result = paginateArray(
176
+ * items,
177
+ * cursorStr,
178
+ * DEFAULT_PAGINATION_CONFIG.DEFAULT_PAGE_SIZE,
179
+ * DEFAULT_PAGINATION_CONFIG.MAX_PAGE_SIZE,
180
+ * ctx,
181
+ * );
182
+ */
183
+ export const DEFAULT_PAGINATION_CONFIG = {
184
+ /** Default number of items per page */
185
+ DEFAULT_PAGE_SIZE: 50,
186
+ /** Maximum allowed items per page */
187
+ MAX_PAGE_SIZE: 1000,
188
+ /** Minimum allowed items per page */
189
+ MIN_PAGE_SIZE: 1,
190
+ };
191
+ //# sourceMappingURL=pagination.js.map