@copilotkit/aimock 1.45.1 → 1.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (492) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +25 -1
  4. package/README.md +11 -2
  5. package/dist/agui-mock.cjs +25 -25
  6. package/dist/agui-mock.cjs.map +1 -1
  7. package/dist/agui-mock.d.cts.map +1 -1
  8. package/dist/agui-mock.d.ts.map +1 -1
  9. package/dist/agui-mock.js +25 -25
  10. package/dist/agui-mock.js.map +1 -1
  11. package/dist/agui-types.d.cts.map +1 -1
  12. package/dist/aimock-cli.cjs +15 -1
  13. package/dist/aimock-cli.cjs.map +1 -1
  14. package/dist/aimock-cli.d.cts.map +1 -1
  15. package/dist/aimock-cli.d.ts.map +1 -1
  16. package/dist/aimock-cli.js +16 -2
  17. package/dist/aimock-cli.js.map +1 -1
  18. package/dist/api-key-auth.cjs +10 -9
  19. package/dist/api-key-auth.cjs.map +1 -1
  20. package/dist/api-key-auth.d.cts.map +1 -1
  21. package/dist/api-key-auth.d.ts.map +1 -1
  22. package/dist/api-key-auth.js +11 -10
  23. package/dist/api-key-auth.js.map +1 -1
  24. package/dist/aws-event-stream.cjs +20 -23
  25. package/dist/aws-event-stream.cjs.map +1 -1
  26. package/dist/aws-event-stream.d.cts +0 -1
  27. package/dist/aws-event-stream.d.ts +0 -1
  28. package/dist/aws-event-stream.js +20 -24
  29. package/dist/aws-event-stream.js.map +1 -1
  30. package/dist/batches.cjs +2 -4
  31. package/dist/batches.cjs.map +1 -1
  32. package/dist/batches.d.cts.map +1 -1
  33. package/dist/batches.d.ts.map +1 -1
  34. package/dist/batches.js +2 -4
  35. package/dist/batches.js.map +1 -1
  36. package/dist/bedrock-converse.cjs +139 -161
  37. package/dist/bedrock-converse.cjs.map +1 -1
  38. package/dist/bedrock-converse.d.cts.map +1 -1
  39. package/dist/bedrock-converse.d.ts.map +1 -1
  40. package/dist/bedrock-converse.js +137 -159
  41. package/dist/bedrock-converse.js.map +1 -1
  42. package/dist/bedrock.cjs +175 -216
  43. package/dist/bedrock.cjs.map +1 -1
  44. package/dist/bedrock.d.cts.map +1 -1
  45. package/dist/bedrock.d.ts.map +1 -1
  46. package/dist/bedrock.js +172 -213
  47. package/dist/bedrock.js.map +1 -1
  48. package/dist/byteplus-video.cjs +11 -10
  49. package/dist/byteplus-video.cjs.map +1 -1
  50. package/dist/byteplus-video.d.cts.map +1 -1
  51. package/dist/byteplus-video.d.ts.map +1 -1
  52. package/dist/byteplus-video.js +5 -4
  53. package/dist/byteplus-video.js.map +1 -1
  54. package/dist/chaos.cjs +5 -4
  55. package/dist/chaos.cjs.map +1 -1
  56. package/dist/chaos.d.cts.map +1 -1
  57. package/dist/chaos.d.ts.map +1 -1
  58. package/dist/chaos.js +5 -4
  59. package/dist/chaos.js.map +1 -1
  60. package/dist/cli.cjs +15 -0
  61. package/dist/cli.cjs.map +1 -1
  62. package/dist/cli.js +15 -0
  63. package/dist/cli.js.map +1 -1
  64. package/dist/cohere.cjs +107 -180
  65. package/dist/cohere.cjs.map +1 -1
  66. package/dist/cohere.d.cts.map +1 -1
  67. package/dist/cohere.d.ts.map +1 -1
  68. package/dist/cohere.js +106 -179
  69. package/dist/cohere.js.map +1 -1
  70. package/dist/config-loader.cjs +29 -1
  71. package/dist/config-loader.cjs.map +1 -1
  72. package/dist/config-loader.d.cts +7 -1
  73. package/dist/config-loader.d.cts.map +1 -1
  74. package/dist/config-loader.d.ts +7 -1
  75. package/dist/config-loader.d.ts.map +1 -1
  76. package/dist/config-loader.js +29 -2
  77. package/dist/config-loader.js.map +1 -1
  78. package/dist/count-tokens.cjs +146 -0
  79. package/dist/count-tokens.cjs.map +1 -0
  80. package/dist/count-tokens.js +146 -0
  81. package/dist/count-tokens.js.map +1 -0
  82. package/dist/elevenlabs-audio.cjs +36 -34
  83. package/dist/elevenlabs-audio.cjs.map +1 -1
  84. package/dist/elevenlabs-audio.d.cts.map +1 -1
  85. package/dist/elevenlabs-audio.d.ts.map +1 -1
  86. package/dist/elevenlabs-audio.js +33 -31
  87. package/dist/elevenlabs-audio.js.map +1 -1
  88. package/dist/elevenlabs-voice.cjs +38 -37
  89. package/dist/elevenlabs-voice.cjs.map +1 -1
  90. package/dist/elevenlabs-voice.d.cts.map +1 -1
  91. package/dist/elevenlabs-voice.d.ts.map +1 -1
  92. package/dist/elevenlabs-voice.js +33 -32
  93. package/dist/elevenlabs-voice.js.map +1 -1
  94. package/dist/embedding-request.cjs +16 -0
  95. package/dist/embedding-request.cjs.map +1 -0
  96. package/dist/embedding-request.js +14 -0
  97. package/dist/embedding-request.js.map +1 -0
  98. package/dist/embeddings.cjs +30 -20
  99. package/dist/embeddings.cjs.map +1 -1
  100. package/dist/embeddings.d.cts.map +1 -1
  101. package/dist/embeddings.d.ts.map +1 -1
  102. package/dist/embeddings.js +30 -20
  103. package/dist/embeddings.js.map +1 -1
  104. package/dist/fal-audio.cjs +45 -43
  105. package/dist/fal-audio.cjs.map +1 -1
  106. package/dist/fal-audio.d.cts.map +1 -1
  107. package/dist/fal-audio.d.ts.map +1 -1
  108. package/dist/fal-audio.js +42 -40
  109. package/dist/fal-audio.js.map +1 -1
  110. package/dist/fal.cjs +17 -15
  111. package/dist/fal.cjs.map +1 -1
  112. package/dist/fal.js +16 -14
  113. package/dist/fal.js.map +1 -1
  114. package/dist/files.cjs +2 -4
  115. package/dist/files.cjs.map +1 -1
  116. package/dist/files.d.cts.map +1 -1
  117. package/dist/files.d.ts.map +1 -1
  118. package/dist/files.js +2 -4
  119. package/dist/files.js.map +1 -1
  120. package/dist/fine-tuning.cjs +2 -4
  121. package/dist/fine-tuning.cjs.map +1 -1
  122. package/dist/fine-tuning.d.cts.map +1 -1
  123. package/dist/fine-tuning.d.ts.map +1 -1
  124. package/dist/fine-tuning.js +2 -4
  125. package/dist/fine-tuning.js.map +1 -1
  126. package/dist/fixture-loader.cjs +140 -44
  127. package/dist/fixture-loader.cjs.map +1 -1
  128. package/dist/fixture-loader.d.cts.map +1 -1
  129. package/dist/fixture-loader.d.ts.map +1 -1
  130. package/dist/fixture-loader.js +141 -46
  131. package/dist/fixture-loader.js.map +1 -1
  132. package/dist/gemini-embeddings.cjs +26 -24
  133. package/dist/gemini-embeddings.cjs.map +1 -1
  134. package/dist/gemini-embeddings.js +26 -24
  135. package/dist/gemini-embeddings.js.map +1 -1
  136. package/dist/gemini-interactions.cjs +113 -156
  137. package/dist/gemini-interactions.cjs.map +1 -1
  138. package/dist/gemini-interactions.d.cts +0 -1
  139. package/dist/gemini-interactions.d.cts.map +1 -1
  140. package/dist/gemini-interactions.d.ts +0 -1
  141. package/dist/gemini-interactions.d.ts.map +1 -1
  142. package/dist/gemini-interactions.js +113 -156
  143. package/dist/gemini-interactions.js.map +1 -1
  144. package/dist/gemini.cjs +295 -218
  145. package/dist/gemini.cjs.map +1 -1
  146. package/dist/gemini.d.cts.map +1 -1
  147. package/dist/gemini.d.ts.map +1 -1
  148. package/dist/gemini.js +295 -219
  149. package/dist/gemini.js.map +1 -1
  150. package/dist/grok-video.cjs +14 -12
  151. package/dist/grok-video.cjs.map +1 -1
  152. package/dist/grok-video.d.cts.map +1 -1
  153. package/dist/grok-video.d.ts.map +1 -1
  154. package/dist/grok-video.js +8 -6
  155. package/dist/grok-video.js.map +1 -1
  156. package/dist/helpers.cjs +411 -75
  157. package/dist/helpers.cjs.map +1 -1
  158. package/dist/helpers.d.cts +51 -1
  159. package/dist/helpers.d.cts.map +1 -1
  160. package/dist/helpers.d.ts +51 -1
  161. package/dist/helpers.d.ts.map +1 -1
  162. package/dist/helpers.js +402 -74
  163. package/dist/helpers.js.map +1 -1
  164. package/dist/images.cjs +30 -28
  165. package/dist/images.cjs.map +1 -1
  166. package/dist/images.d.cts.map +1 -1
  167. package/dist/images.d.ts.map +1 -1
  168. package/dist/images.js +27 -25
  169. package/dist/images.js.map +1 -1
  170. package/dist/index.cjs +4 -2
  171. package/dist/index.d.cts +4 -4
  172. package/dist/index.d.ts +4 -4
  173. package/dist/index.js +4 -4
  174. package/dist/journal.cjs +3 -1
  175. package/dist/journal.cjs.map +1 -1
  176. package/dist/journal.d.cts.map +1 -1
  177. package/dist/journal.d.ts.map +1 -1
  178. package/dist/journal.js +2 -1
  179. package/dist/journal.js.map +1 -1
  180. package/dist/llmock.cjs +3 -0
  181. package/dist/llmock.cjs.map +1 -1
  182. package/dist/llmock.d.cts.map +1 -1
  183. package/dist/llmock.d.ts.map +1 -1
  184. package/dist/llmock.js +3 -0
  185. package/dist/llmock.js.map +1 -1
  186. package/dist/mcp-fakes.d.cts.map +1 -1
  187. package/dist/mcp-handler.cjs +7 -3
  188. package/dist/mcp-handler.cjs.map +1 -1
  189. package/dist/mcp-handler.js +7 -3
  190. package/dist/mcp-handler.js.map +1 -1
  191. package/dist/mcp-recorder.cjs +2 -1
  192. package/dist/mcp-recorder.cjs.map +1 -1
  193. package/dist/mcp-recorder.js +3 -2
  194. package/dist/mcp-recorder.js.map +1 -1
  195. package/dist/messages.cjs +235 -202
  196. package/dist/messages.cjs.map +1 -1
  197. package/dist/messages.d.cts.map +1 -1
  198. package/dist/messages.d.ts.map +1 -1
  199. package/dist/messages.js +234 -202
  200. package/dist/messages.js.map +1 -1
  201. package/dist/metrics.cjs +5 -2
  202. package/dist/metrics.cjs.map +1 -1
  203. package/dist/metrics.d.cts.map +1 -1
  204. package/dist/metrics.d.ts.map +1 -1
  205. package/dist/metrics.js +6 -3
  206. package/dist/metrics.js.map +1 -1
  207. package/dist/misbehavior.cjs +62 -3
  208. package/dist/misbehavior.cjs.map +1 -1
  209. package/dist/misbehavior.d.cts.map +1 -1
  210. package/dist/misbehavior.d.ts.map +1 -1
  211. package/dist/misbehavior.js +63 -4
  212. package/dist/misbehavior.js.map +1 -1
  213. package/dist/model-utils.cjs +1 -0
  214. package/dist/model-utils.cjs.map +1 -1
  215. package/dist/model-utils.js +1 -1
  216. package/dist/model-utils.js.map +1 -1
  217. package/dist/moderation.cjs +15 -12
  218. package/dist/moderation.cjs.map +1 -1
  219. package/dist/moderation.js +14 -11
  220. package/dist/moderation.js.map +1 -1
  221. package/dist/ndjson-writer.cjs +21 -23
  222. package/dist/ndjson-writer.cjs.map +1 -1
  223. package/dist/ndjson-writer.d.cts +0 -1
  224. package/dist/ndjson-writer.d.cts.map +1 -1
  225. package/dist/ndjson-writer.d.ts +0 -1
  226. package/dist/ndjson-writer.d.ts.map +1 -1
  227. package/dist/ndjson-writer.js +21 -24
  228. package/dist/ndjson-writer.js.map +1 -1
  229. package/dist/ollama.cjs +276 -210
  230. package/dist/ollama.cjs.map +1 -1
  231. package/dist/ollama.d.cts.map +1 -1
  232. package/dist/ollama.d.ts.map +1 -1
  233. package/dist/ollama.js +273 -207
  234. package/dist/ollama.js.map +1 -1
  235. package/dist/openapi.cjs +323 -3
  236. package/dist/openapi.cjs.map +1 -1
  237. package/dist/openapi.d.cts.map +1 -1
  238. package/dist/openapi.d.ts.map +1 -1
  239. package/dist/openapi.js +324 -4
  240. package/dist/openapi.js.map +1 -1
  241. package/dist/openrouter-chat.cjs +11 -155
  242. package/dist/openrouter-chat.cjs.map +1 -1
  243. package/dist/openrouter-chat.js +11 -155
  244. package/dist/openrouter-chat.js.map +1 -1
  245. package/dist/openrouter-video.cjs +51 -74
  246. package/dist/openrouter-video.cjs.map +1 -1
  247. package/dist/openrouter-video.d.cts.map +1 -1
  248. package/dist/openrouter-video.d.ts.map +1 -1
  249. package/dist/openrouter-video.js +37 -60
  250. package/dist/openrouter-video.js.map +1 -1
  251. package/dist/raw-validation.cjs +177 -0
  252. package/dist/raw-validation.cjs.map +1 -0
  253. package/dist/raw-validation.js +176 -0
  254. package/dist/raw-validation.js.map +1 -0
  255. package/dist/recorder.cjs +176 -95
  256. package/dist/recorder.cjs.map +1 -1
  257. package/dist/recorder.d.cts.map +1 -1
  258. package/dist/recorder.d.ts.map +1 -1
  259. package/dist/recorder.js +176 -95
  260. package/dist/recorder.js.map +1 -1
  261. package/dist/rerank.cjs +13 -11
  262. package/dist/rerank.cjs.map +1 -1
  263. package/dist/rerank.js +12 -10
  264. package/dist/rerank.js.map +1 -1
  265. package/dist/response/catalog/anthropic-page.cjs +39 -0
  266. package/dist/response/catalog/anthropic-page.cjs.map +1 -0
  267. package/dist/response/catalog/anthropic-page.js +38 -0
  268. package/dist/response/catalog/anthropic-page.js.map +1 -0
  269. package/dist/response/catalog/collect.cjs +61 -0
  270. package/dist/response/catalog/collect.cjs.map +1 -0
  271. package/dist/response/catalog/collect.js +59 -0
  272. package/dist/response/catalog/collect.js.map +1 -0
  273. package/dist/response/catalog/gemini-page.cjs +25 -0
  274. package/dist/response/catalog/gemini-page.cjs.map +1 -0
  275. package/dist/response/catalog/gemini-page.js +24 -0
  276. package/dist/response/catalog/gemini-page.js.map +1 -0
  277. package/dist/response/catalog/render.cjs +182 -0
  278. package/dist/response/catalog/render.cjs.map +1 -0
  279. package/dist/response/catalog/render.js +171 -0
  280. package/dist/response/catalog/render.js.map +1 -0
  281. package/dist/response/deliver.cjs +144 -0
  282. package/dist/response/deliver.cjs.map +1 -0
  283. package/dist/response/deliver.js +143 -0
  284. package/dist/response/deliver.js.map +1 -0
  285. package/dist/response/errors.cjs +225 -0
  286. package/dist/response/errors.cjs.map +1 -0
  287. package/dist/response/errors.js +224 -0
  288. package/dist/response/errors.js.map +1 -0
  289. package/dist/response/finish.cjs +39 -0
  290. package/dist/response/finish.cjs.map +1 -0
  291. package/dist/response/finish.js +35 -0
  292. package/dist/response/finish.js.map +1 -0
  293. package/dist/response/frame-loop.cjs +102 -0
  294. package/dist/response/frame-loop.cjs.map +1 -0
  295. package/dist/response/frame-loop.js +100 -0
  296. package/dist/response/frame-loop.js.map +1 -0
  297. package/dist/response/lifecycle.cjs +223 -0
  298. package/dist/response/lifecycle.cjs.map +1 -0
  299. package/dist/response/lifecycle.js +211 -0
  300. package/dist/response/lifecycle.js.map +1 -0
  301. package/dist/response/policy.cjs +126 -0
  302. package/dist/response/policy.cjs.map +1 -0
  303. package/dist/response/policy.js +120 -0
  304. package/dist/response/policy.js.map +1 -0
  305. package/dist/response/profiles.cjs +69 -0
  306. package/dist/response/profiles.cjs.map +1 -0
  307. package/dist/response/profiles.js +65 -0
  308. package/dist/response/profiles.js.map +1 -0
  309. package/dist/response/proxy.cjs +71 -0
  310. package/dist/response/proxy.cjs.map +1 -0
  311. package/dist/response/proxy.js +70 -0
  312. package/dist/response/proxy.js.map +1 -0
  313. package/dist/response/raw.cjs +228 -0
  314. package/dist/response/raw.cjs.map +1 -0
  315. package/dist/response/raw.js +227 -0
  316. package/dist/response/raw.js.map +1 -0
  317. package/dist/response/timing.cjs +157 -0
  318. package/dist/response/timing.cjs.map +1 -0
  319. package/dist/response/timing.js +150 -0
  320. package/dist/response/timing.js.map +1 -0
  321. package/dist/response/usage/codecs/anthropic.cjs +52 -0
  322. package/dist/response/usage/codecs/anthropic.cjs.map +1 -0
  323. package/dist/response/usage/codecs/anthropic.js +50 -0
  324. package/dist/response/usage/codecs/anthropic.js.map +1 -0
  325. package/dist/response/usage/codecs/cohere.cjs +36 -0
  326. package/dist/response/usage/codecs/cohere.cjs.map +1 -0
  327. package/dist/response/usage/codecs/cohere.js +35 -0
  328. package/dist/response/usage/codecs/cohere.js.map +1 -0
  329. package/dist/response/usage/codecs/converse.cjs +16 -0
  330. package/dist/response/usage/codecs/converse.cjs.map +1 -0
  331. package/dist/response/usage/codecs/converse.js +15 -0
  332. package/dist/response/usage/codecs/converse.js.map +1 -0
  333. package/dist/response/usage/codecs/gemini.cjs +31 -0
  334. package/dist/response/usage/codecs/gemini.cjs.map +1 -0
  335. package/dist/response/usage/codecs/gemini.js +31 -0
  336. package/dist/response/usage/codecs/gemini.js.map +1 -0
  337. package/dist/response/usage/codecs/interactions.cjs +22 -0
  338. package/dist/response/usage/codecs/interactions.cjs.map +1 -0
  339. package/dist/response/usage/codecs/interactions.js +21 -0
  340. package/dist/response/usage/codecs/interactions.js.map +1 -0
  341. package/dist/response/usage/codecs/ollama.cjs +17 -0
  342. package/dist/response/usage/codecs/ollama.cjs.map +1 -0
  343. package/dist/response/usage/codecs/ollama.js +17 -0
  344. package/dist/response/usage/codecs/ollama.js.map +1 -0
  345. package/dist/response/usage/codecs/openai.cjs +52 -0
  346. package/dist/response/usage/codecs/openai.cjs.map +1 -0
  347. package/dist/response/usage/codecs/openai.js +50 -0
  348. package/dist/response/usage/codecs/openai.js.map +1 -0
  349. package/dist/response/usage/codecs/openrouter.cjs +68 -0
  350. package/dist/response/usage/codecs/openrouter.cjs.map +1 -0
  351. package/dist/response/usage/codecs/openrouter.js +67 -0
  352. package/dist/response/usage/codecs/openrouter.js.map +1 -0
  353. package/dist/response/usage/codecs/realtime.cjs +19 -0
  354. package/dist/response/usage/codecs/realtime.cjs.map +1 -0
  355. package/dist/response/usage/codecs/realtime.js +18 -0
  356. package/dist/response/usage/codecs/realtime.js.map +1 -0
  357. package/dist/response/usage/codecs/responses.cjs +21 -0
  358. package/dist/response/usage/codecs/responses.cjs.map +1 -0
  359. package/dist/response/usage/codecs/responses.js +20 -0
  360. package/dist/response/usage/codecs/responses.js.map +1 -0
  361. package/dist/response/usage/estimate.cjs +23 -0
  362. package/dist/response/usage/estimate.cjs.map +1 -0
  363. package/dist/response/usage/estimate.js +21 -0
  364. package/dist/response/usage/estimate.js.map +1 -0
  365. package/dist/response/usage/fields.cjs +72 -0
  366. package/dist/response/usage/fields.cjs.map +1 -0
  367. package/dist/response/usage/fields.js +70 -0
  368. package/dist/response/usage/fields.js.map +1 -0
  369. package/dist/response/usage/recording.cjs +52 -0
  370. package/dist/response/usage/recording.cjs.map +1 -0
  371. package/dist/response/usage/recording.js +52 -0
  372. package/dist/response/usage/recording.js.map +1 -0
  373. package/dist/responses.cjs +304 -196
  374. package/dist/responses.cjs.map +1 -1
  375. package/dist/responses.d.cts.map +1 -1
  376. package/dist/responses.d.ts.map +1 -1
  377. package/dist/responses.js +303 -195
  378. package/dist/responses.js.map +1 -1
  379. package/dist/route-registry.cjs +45 -0
  380. package/dist/route-registry.cjs.map +1 -1
  381. package/dist/route-registry.d.cts.map +1 -1
  382. package/dist/route-registry.d.ts.map +1 -1
  383. package/dist/route-registry.js +41 -1
  384. package/dist/route-registry.js.map +1 -1
  385. package/dist/router.cjs +24 -4
  386. package/dist/router.cjs.map +1 -1
  387. package/dist/router.d.cts +7 -1
  388. package/dist/router.d.cts.map +1 -1
  389. package/dist/router.d.ts +7 -1
  390. package/dist/router.d.ts.map +1 -1
  391. package/dist/router.js +24 -5
  392. package/dist/router.js.map +1 -1
  393. package/dist/search.cjs +13 -11
  394. package/dist/search.cjs.map +1 -1
  395. package/dist/search.js +12 -10
  396. package/dist/search.js.map +1 -1
  397. package/dist/server.cjs +293 -248
  398. package/dist/server.cjs.map +1 -1
  399. package/dist/server.d.cts.map +1 -1
  400. package/dist/server.d.ts.map +1 -1
  401. package/dist/server.js +294 -249
  402. package/dist/server.js.map +1 -1
  403. package/dist/speech.cjs +20 -18
  404. package/dist/speech.cjs.map +1 -1
  405. package/dist/speech.d.cts.map +1 -1
  406. package/dist/speech.d.ts.map +1 -1
  407. package/dist/speech.js +19 -17
  408. package/dist/speech.js.map +1 -1
  409. package/dist/sse-writer.cjs +56 -30
  410. package/dist/sse-writer.cjs.map +1 -1
  411. package/dist/sse-writer.d.cts +0 -1
  412. package/dist/sse-writer.d.cts.map +1 -1
  413. package/dist/sse-writer.d.ts +0 -1
  414. package/dist/sse-writer.d.ts.map +1 -1
  415. package/dist/sse-writer.js +55 -31
  416. package/dist/sse-writer.js.map +1 -1
  417. package/dist/stream-collapse.cjs +118 -11
  418. package/dist/stream-collapse.cjs.map +1 -1
  419. package/dist/stream-collapse.d.cts +10 -9
  420. package/dist/stream-collapse.d.cts.map +1 -1
  421. package/dist/stream-collapse.d.ts +10 -9
  422. package/dist/stream-collapse.d.ts.map +1 -1
  423. package/dist/stream-collapse.js +118 -11
  424. package/dist/stream-collapse.js.map +1 -1
  425. package/dist/transcription.cjs +31 -43
  426. package/dist/transcription.cjs.map +1 -1
  427. package/dist/transcription.d.cts.map +1 -1
  428. package/dist/transcription.d.ts.map +1 -1
  429. package/dist/transcription.js +31 -43
  430. package/dist/transcription.js.map +1 -1
  431. package/dist/types.cjs.map +1 -1
  432. package/dist/types.d.cts +231 -9
  433. package/dist/types.d.cts.map +1 -1
  434. package/dist/types.d.ts +231 -9
  435. package/dist/types.d.ts.map +1 -1
  436. package/dist/types.js.map +1 -1
  437. package/dist/vector-mock.cjs +3 -8
  438. package/dist/vector-mock.cjs.map +1 -1
  439. package/dist/vector-mock.d.cts.map +1 -1
  440. package/dist/vector-mock.d.ts.map +1 -1
  441. package/dist/vector-mock.js +3 -8
  442. package/dist/vector-mock.js.map +1 -1
  443. package/dist/vector-stores.cjs +2 -4
  444. package/dist/vector-stores.cjs.map +1 -1
  445. package/dist/vector-stores.d.cts.map +1 -1
  446. package/dist/vector-stores.d.ts.map +1 -1
  447. package/dist/vector-stores.js +2 -4
  448. package/dist/vector-stores.js.map +1 -1
  449. package/dist/veo-video.cjs +16 -14
  450. package/dist/veo-video.cjs.map +1 -1
  451. package/dist/veo-video.js +10 -8
  452. package/dist/veo-video.js.map +1 -1
  453. package/dist/video.cjs +21 -19
  454. package/dist/video.cjs.map +1 -1
  455. package/dist/video.d.cts.map +1 -1
  456. package/dist/video.d.ts.map +1 -1
  457. package/dist/video.js +20 -18
  458. package/dist/video.js.map +1 -1
  459. package/dist/ws-framing.cjs +12 -6
  460. package/dist/ws-framing.cjs.map +1 -1
  461. package/dist/ws-framing.d.cts +2 -0
  462. package/dist/ws-framing.d.cts.map +1 -1
  463. package/dist/ws-framing.d.ts +2 -0
  464. package/dist/ws-framing.d.ts.map +1 -1
  465. package/dist/ws-framing.js +12 -6
  466. package/dist/ws-framing.js.map +1 -1
  467. package/dist/ws-gemini-live.cjs +131 -96
  468. package/dist/ws-gemini-live.cjs.map +1 -1
  469. package/dist/ws-gemini-live.d.cts +0 -1
  470. package/dist/ws-gemini-live.d.cts.map +1 -1
  471. package/dist/ws-gemini-live.d.ts +0 -1
  472. package/dist/ws-gemini-live.d.ts.map +1 -1
  473. package/dist/ws-gemini-live.js +130 -95
  474. package/dist/ws-gemini-live.js.map +1 -1
  475. package/dist/ws-live.cjs +6 -5
  476. package/dist/ws-live.cjs.map +1 -1
  477. package/dist/ws-live.js +5 -4
  478. package/dist/ws-live.js.map +1 -1
  479. package/dist/ws-realtime.cjs +108 -124
  480. package/dist/ws-realtime.cjs.map +1 -1
  481. package/dist/ws-realtime.d.cts.map +1 -1
  482. package/dist/ws-realtime.d.ts.map +1 -1
  483. package/dist/ws-realtime.js +105 -121
  484. package/dist/ws-realtime.js.map +1 -1
  485. package/dist/ws-responses.cjs +80 -59
  486. package/dist/ws-responses.cjs.map +1 -1
  487. package/dist/ws-responses.d.cts.map +1 -1
  488. package/dist/ws-responses.d.ts.map +1 -1
  489. package/dist/ws-responses.js +79 -58
  490. package/dist/ws-responses.js.map +1 -1
  491. package/package.json +4 -1
  492. package/skills/write-fixtures/SKILL.md +57 -16
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-recorder.cjs","names":["Logger","readBody","resolveMcpIdentity","build","plainText","path","writeFakeToolAnswer","resolveStrictMode","fakeFailure","MCP_FAKE_ERROR_CODES","isAuthenticatedRequest","buildForwardHeaders","https","http","fixed","StringDecoder","sanitizeHeaderValue","sanitizeRecording","aimockVersion","knownSecrets","RecordUnsafeError","persistServiceFakes","validateMcpFakes","slugifyTestId","slugifyContext","crypto","flattenHeaders","MCP_FAKES_DEFAULT_TEST_ID","fs","scrubUrl","REDACTED"],"sources":["../src/mcp-recorder.ts"],"sourcesContent":["/**\n * MCP record mode (rr: MR1-MR17, par: AM1-AM9): a pass-through proxy inside\n * `MCPMock` that forwards to a real upstream MCP server, relays its answers,\n * and writes the `tools/list` and `tools/call` exchanges it sees as an\n * `mcpFakes` file (FA1-FA5), sanitized (S1-S7).\n *\n * Every file this module writes goes through `sanitizeRecording` and then\n * `persistServiceFakes`; there is no other filesystem write here.\n */\nimport * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport { StringDecoder } from \"node:string_decoder\";\nimport type {\n JournalEntry,\n McpFakeIdentity,\n McpFakeSource,\n McpFakeUndeclaredPolicy,\n McpRecordConfig,\n Mountable,\n} from \"./types.js\";\nimport { MCP_FAKE_ERROR_CODES } from \"./types.js\";\nimport { Logger } from \"./logger.js\";\nimport { isAuthenticatedRequest } from \"./api-key-auth.js\";\nimport {\n flattenHeaders,\n readBody,\n resolveMcpIdentity,\n resolveStrictMode,\n slugifyContext,\n slugifyTestId,\n} from \"./helpers.js\";\nimport {\n buildForwardHeaders,\n persistServiceFakes,\n removeForwardHeader,\n sanitizeHeaderValue,\n} from \"./recorder.js\";\nimport {\n MCP_FAKES_DEFAULT_TEST_ID,\n validateMcpFakes,\n type McpFakeAddOriginInternal,\n type McpFakeAddResult,\n type McpFakeClaim,\n type McpFakeStore,\n} from \"./mcp-fakes.js\";\nimport { fakeFailure, writeFakeToolAnswer, type McpFakeEvent } from \"./mcp-handler.js\";\nimport {\n REDACTED,\n RecordUnsafeError,\n knownSecrets,\n sanitizeRecording,\n scrubUrl,\n validateSecretValues,\n} from \"./record-sanitize.js\";\nimport { aimockVersion } from \"./version.js\";\nimport { build, fixed, msg, plainText } from \"./message-text.js\";\n\n/** AM4: the protocol versions this recorder writes (legacy era only). */\nexport const RECORDABLE_VERSIONS = new Set([\"2025-03-26\", \"2025-06-18\", \"2025-11-25\"]);\n/** MR2 (d): default bytes of one recordable response that are buffered. */\nexport const DEFAULT_RECORD_BUFFER_BYTES = 64 * 1024 * 1024;\n/** MR2 (d): the ceiling `maxRecordBufferBytes` is clamped to. */\nexport const MAX_RECORD_BUFFER_BYTES = 256 * 1024 * 1024;\n\n/** The MR14 body of a 502 answered for an upstream auth failure. */\nconst AUTH_FAILURE_ERROR =\n \"aimock MCP recording got an upstream auth failure; OAuth discovery is not supported through the recorder; record with a pre-acquired token via upstreamAuth or the client's Authorization header\";\n\n/** Hop-by-hop response headers that are never relayed. */\nconst HOP_BY_HOP = new Set([\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"te\",\n \"trailer\",\n \"upgrade\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n]);\n\n/** The identity query parameters aimock reads and never forwards (MR2 c). */\nconst IDENTITY_QUERY = new Set([\"testId\", \"context\", \"undeclared\"]);\n\n/** MR7: `_meta` keys dropped from a recorded result. */\nconst DROPPED_META = [\"traceparent\", \"tracestate\", \"baggage\", \"io.modelcontextprotocol/serverInfo\"];\n\nconst SERVER_INFO_META = \"io.modelcontextprotocol/serverInfo\";\nconst RELATED_TASK_META = \"io.modelcontextprotocol/related-task\";\nconst PROTOCOL_VERSION_META = \"io.modelcontextprotocol/protocolVersion\";\n/** The version assumed when nothing names one (MR4 source 4). */\nconst FALLBACK_VERSION = \"2025-03-26\";\n\n/** A header token (RFC 9110 `token`). */\nconst HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\n\n/** What the recorder needs from its `MCPMock`. */\nexport interface RecorderHost {\n /** MR4 claims, MR12 registration. */\n readonly fakes: McpFakeStore;\n addMcpFakes(blocks: McpFakeSource[], origin: McpFakeAddOriginInternal): McpFakeAddResult;\n journalEntry(entry: Omit<JournalEntry, \"id\" | \"timestamp\">): void;\n logger(): Logger | null;\n replaySpeed(): number;\n /** Reuses the mount's metric and log side channels for a fake outcome. */\n onFakeEvent(evt: McpFakeEvent): void;\n}\n\n/** A parsed `Name: value` upstream credential header (MR14, AM6). */\nexport interface UpstreamAuthHeader {\n name: string;\n value: string;\n}\n\n/**\n * MR14 / AM6: parse a `Name: value` header, from `upstreamAuth` or\n * `AIMOCK_MCP_UPSTREAM_AUTH`. `undefined` or an empty string is no header. A\n * malformed value is a start error that names `source`, never the value.\n */\nexport function parseUpstreamAuth(\n raw: string | undefined,\n source = \"AIMOCK_MCP_UPSTREAM_AUTH\",\n): UpstreamAuthHeader | undefined {\n if (raw === undefined || raw === \"\") return undefined;\n const colon = raw.indexOf(\":\");\n const name = colon === -1 ? \"\" : raw.slice(0, colon).trim();\n const value = colon === -1 ? \"\" : raw.slice(colon + 1).trim();\n if (!HEADER_NAME.test(name) || value === \"\") {\n throw new Error(`${source} must be \"Name: value\" (a header name, a colon, then a value)`);\n }\n return { name, value };\n}\n\n/** MR3 / AM9: what an upstream `initialize` bound to its session. Private to the recorder. */\ninterface RecordSession {\n protocolVersion: string | null;\n /** AM4: false when the negotiated version is not recordable. */\n recordable: boolean;\n serverInfo?: Record<string, unknown>;\n clientCapabilities?: unknown;\n clientInfo?: unknown;\n testId?: string;\n context?: string;\n undeclared?: McpFakeUndeclaredPolicy;\n}\n\n/** MR16: a `tools/list` being collected page by page. */\ninterface PendingList {\n tools: unknown[];\n /** The `nextCursor` the next page must be requested with. */\n next: string;\n testId: string | null;\n}\n\n/** What is recorded from one request's response. */\ntype RecordKind = \"initialize\" | \"tools/list\" | \"tools/call\";\n\n/** One JSON-RPC message of an upstream response, with its arrival (ms after the request arrived). */\ninterface TimedMessage {\n at: number;\n message: Record<string, unknown>;\n}\n\n/** The state of one handled request, for its journal entry. */\ninterface Exchange {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n mount: string;\n subPath: string;\n arrivedAt: number;\n identity: McpFakeIdentity | null;\n /** The resolution's supplied flags (I3: only supplied values are bound at initialize). */\n supplied: { testId: boolean; context: boolean; undeclared: boolean };\n /** The JSON-RPC request (`null` for a batch, an unparsable body, GET or DELETE). */\n message: Record<string, unknown> | null;\n rpcMethod: string | null;\n /** The JSON-RPC id of the request, when it is a request with a string or number id. */\n rpcId: string | number | undefined;\n /** The request's `params`, when it has an object one. */\n params: Record<string, unknown> | undefined;\n recordSkipped?: string;\n mcpFake?: JournalEntry[\"response\"][\"mcpFake\"];\n proxied: boolean;\n /** X-AIMock-Record-Error text, when the response is not yet sent. */\n recordError?: string;\n}\n\n/**\n * The MCP recorder of one `MCPMock`. `handle` always answers the request:\n * by a fake (MR4), a fake error (MR5), or by forwarding it (MR2).\n */\nexport class McpRecorder {\n private readonly target: URL;\n private readonly auth: UpstreamAuthHeader | undefined;\n private readonly maxBuffer: number;\n private readonly fixturePath: string;\n private readonly secretValues: readonly string[];\n private readonly fallbackLogger = new Logger(\"warn\");\n /** MR3: upstream `Mcp-Session-Id` → what its `initialize` bound. */\n private readonly sessions = new Map<string, RecordSession>();\n /** MR16: pending lists per `${mount}\\0${scope}`. */\n private readonly pendingLists = new Map<string, PendingList>();\n /** MR16: completed lists waiting for their block's first call entry. */\n private readonly completedLists = new Map<string, { tools: unknown[]; testId: string | null }>();\n /** MR13: resolved path → sha256 of the last write. */\n private readonly hashes = new Map<string, string>();\n private readonly listeners = new Set<(filepath: string, mcpFakes: unknown) => void>();\n /** RL3: mounts already warned about a request with no scope. */\n private readonly warnedNoScope = new Set<string>();\n\n constructor(\n private readonly host: RecorderHost,\n private readonly config: McpRecordConfig,\n ) {\n let target: URL;\n try {\n target = new URL(config.upstream);\n } catch {\n // Never echo the value: it can carry credentials (S2 c).\n throw new Error(\"MCP record upstream is not a valid URL\");\n }\n if (target.protocol !== \"http:\" && target.protocol !== \"https:\") {\n throw new Error(\"MCP record upstream must be an http: or https: URL\");\n }\n this.target = target;\n this.secretValues = config.secretValues ?? [];\n validateSecretValues(this.secretValues);\n this.auth = parseUpstreamAuth(config.upstreamAuth, \"upstreamAuth\");\n const max = config.maxRecordBufferBytes;\n this.maxBuffer =\n typeof max === \"number\" && Number.isFinite(max) && max > 0\n ? Math.min(Math.floor(max), MAX_RECORD_BUFFER_BYTES)\n : DEFAULT_RECORD_BUFFER_BYTES;\n this.fixturePath = config.fixturePath ?? \"./fixtures/recorded\";\n if (!config.proxyOnly) checkMcpRecordDestination(this.fixturePath);\n }\n\n /** AM1 entry point. Reads the body itself. Always handles the request. */\n async handle(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n mount: string,\n subPath: string,\n ): Promise<void> {\n const x: Exchange = {\n req,\n res,\n mount,\n subPath,\n arrivedAt: performance.now(),\n identity: null,\n supplied: { testId: false, context: false, undeclared: false },\n message: null,\n rpcMethod: null,\n rpcId: undefined,\n params: undefined,\n proxied: false,\n };\n try {\n const body = await readBody(req);\n const sessionId = headerOf(req, \"mcp-session-id\");\n const session = sessionId ? this.sessions.get(sessionId) : undefined;\n const resolution = resolveMcpIdentity(req, session);\n if (!resolution.ok) {\n res.writeHead(resolution.error.status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: resolution.error.message, code: resolution.error.code }));\n return;\n }\n x.identity = resolution.identity;\n x.supplied = resolution.supplied;\n\n let parsed: unknown;\n let parsedOk = false;\n if (body !== \"\") {\n try {\n parsed = JSON.parse(body);\n parsedOk = true;\n } catch {\n /* forwarded as is */\n }\n }\n if (parsedOk && Array.isArray(parsed)) {\n this.skip(x, \"batch\", \"batch\");\n await this.forward(x, body, null);\n return;\n }\n if (parsedOk && isObject(parsed) && typeof parsed.method === \"string\") {\n x.rpcMethod = parsed.method;\n const isRequest = \"id\" in parsed;\n if (typeof parsed.id === \"string\" || typeof parsed.id === \"number\") x.rpcId = parsed.id;\n if (isObject(parsed.params)) x.params = parsed.params;\n if (parsed.method === \"tools/call\") x.message = parsed;\n if (parsed.method === \"initialize\" && isRequest) {\n await this.forward(x, body, \"initialize\");\n return;\n }\n if (parsed.method === \"tools/list\" && isRequest) {\n await this.forward(x, body, \"tools/list\");\n return;\n }\n if (parsed.method === \"tools/call\" && isRequest) {\n if (await this.answerByFake(x, parsed)) return;\n this.logForwardedCall(x);\n await this.forward(x, body, \"tools/call\");\n return;\n }\n // MR8: every other request is forwarded and not recorded. A\n // notification has no response to record, so it is not reported.\n if (isRequest) this.skip(x, \"not-tools\", parsed.method);\n await this.forward(x, body, null);\n return;\n }\n if (body !== \"\" && req.method === \"POST\") this.skip(x, \"not-tools\", \"(no method)\");\n await this.forward(x, body, null);\n } catch (err) {\n const thrown = err instanceof Error ? err.message : String(err);\n this.log().error(\n build(msg`MCP-RECORD: request error on mount ${plainText(mount)}: ${plainText(thrown)}`),\n );\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n } else if (!res.writableEnded) {\n res.end();\n }\n } finally {\n this.journal(x);\n }\n }\n\n /** MR3 / MR16 reset doors (fakes:R1-R5, R8): one test id, or everything. */\n reset(testId?: string): void {\n for (const [id, session] of this.sessions) {\n if (testId === undefined || session.testId === testId) this.sessions.delete(id);\n }\n for (const lists of [this.pendingLists, this.completedLists]) {\n for (const [key, list] of lists) {\n if (testId !== undefined && list.testId !== testId) continue;\n lists.delete(key);\n this.log().warn(\n build(\n msg`MCP-RECORD: forwarded, not recorded (list-incomplete): tools/list on mount ${plainText(mountOfKey(key))}`,\n ),\n );\n }\n }\n }\n\n /** MR13: the sha256 of the last write to `filepath`, if this recorder wrote it. */\n lastWrittenHash(filepath: string): string | undefined {\n return this.hashes.get(path.resolve(filepath));\n }\n\n /** MR13: called after each write with the resolved path and the `mcpFakes` written. */\n onWrite(listener: (filepath: string, mcpFakes: unknown) => void): () => void {\n this.listeners.add(listener);\n return () => void this.listeners.delete(listener);\n }\n\n /** @internal MR3: whether the record-session map holds `sessionId` (tests). */\n hasRecordSession(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n /** MR16: drop pending lists (RL2 list-incomplete) and clear the session map. */\n close(): void {\n this.reset();\n this.listeners.clear();\n }\n\n // ---- MR4 / MR5 ----\n\n /**\n * MR4 / MR5 for one `tools/call` request: answer it by a fake, or by a fake\n * error (deny, strict, eviction), and return true; false to forward it.\n */\n private async answerByFake(x: Exchange, message: Record<string, unknown>): Promise<boolean> {\n const params = message.params;\n const name = isObject(params) ? params.name : undefined;\n const id = message.id;\n if (\n typeof name !== \"string\" ||\n name === \"\" ||\n (typeof id !== \"string\" && typeof id !== \"number\")\n ) {\n return false;\n }\n const identity = x.identity!;\n const args = isObject(params) ? params.arguments : undefined;\n const event = { identity, tool: name, mount: x.mount, args };\n let claim: McpFakeClaim;\n try {\n claim = this.host.fakes.claim(name, args, identity);\n } catch {\n // A claim that throws is an aimock defect: forward, never guess.\n return false;\n }\n if (claim.kind === \"answer\") {\n x.mcpFake = { id: claim.entry.id, outcome: \"answered\" };\n this.host.onFakeEvent({ ...event, kind: \"answered\", entryId: claim.entry.id });\n await writeFakeToolAnswer(\n x.req,\n x.res,\n id,\n claim.result,\n this.host.fakes.replayOf(claim.entry.id),\n params,\n this.host.replaySpeed(),\n null,\n x.arrivedAt,\n );\n return true;\n }\n const strict = resolveStrictMode(this.config.strict, x.req.headers);\n const ctx = { identity, mount: x.mount, notifications: [], calls: [] };\n let failure: ReturnType<typeof fakeFailure> | null = null;\n if (claim.kind === \"none\") {\n if (this.host.fakes.policy(identity) === \"deny\" || strict) {\n failure = fakeFailure({ kind: \"not_declared\" }, name, ctx, () =>\n this.host.fakes.declaredTools(identity),\n );\n }\n } else if (claim.kind === \"evicted\" || strict) {\n failure = fakeFailure(claim, name, ctx, () => []);\n }\n if (!failure) return false;\n x.mcpFake = { id: null, outcome: failure.outcome };\n this.host.onFakeEvent(\n failure.outcome === \"evicted\"\n ? {\n ...event,\n kind: \"evicted\",\n code: failure.code,\n message: failure.message,\n ...failure.evicted,\n }\n : { ...event, kind: failure.outcome, code: failure.code, message: failure.message },\n );\n x.res.writeHead(200, { \"Content-Type\": \"application/json\" });\n x.res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n error: {\n code: MCP_FAKE_ERROR_CODES[failure.code],\n message: failure.message,\n data: { aimock: failure.aimock },\n },\n }),\n );\n return true;\n }\n\n /** RP2: a forwarded `tools/call` is logged as unfaked, answered by the upstream. */\n private logForwardedCall(x: Exchange): void {\n const name = x.params?.name;\n if (typeof name !== \"string\" || x.identity === null) return;\n this.host.fakes.logEvent(x.identity, {\n outcome: \"unfaked\",\n answeredBy: \"upstream\",\n tool: name,\n args: x.params?.arguments,\n context: x.identity.context,\n mount: x.mount,\n });\n }\n\n // ---- MR2: forwarding ----\n\n /** Forward one request to the upstream and relay its answer; record it when `kind` says so. */\n private forward(x: Exchange, body: string, kind: RecordKind | null): Promise<void> {\n const { req, res } = x;\n x.proxied = true;\n if (isAuthenticatedRequest(req) && !this.auth) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No configured upstream credential\" }));\n return Promise.resolve();\n }\n const headers = buildForwardHeaders(req);\n removeForwardHeader(headers, \"x-aimock-mcp-undeclared\");\n if (this.auth) {\n removeForwardHeader(headers, this.auth.name);\n headers[this.auth.name] = this.auth.value;\n }\n if (body !== \"\") headers[\"content-length\"] = String(Buffer.byteLength(body));\n const url = this.upstreamUrl(req, x.subPath);\n const transport = url.protocol === \"https:\" ? https : http;\n\n return new Promise<void>((resolve) => {\n let done = false;\n const finish = (): void => {\n if (done) return;\n done = true;\n resolve();\n };\n const upstreamReq = transport.request(url, { method: req.method ?? \"POST\", headers });\n // MR2: a client that goes away aborts the upstream request.\n res.on(\"close\", () => {\n if (!upstreamReq.destroyed) upstreamReq.destroy();\n finish();\n });\n upstreamReq.on(\"error\", (err) => {\n if (done) return;\n this.log().error(\n build(\n msg`MCP-RECORD: upstream request failed for ${plainText(x.rpcMethod ?? req.method ?? \"?\")} on mount ${plainText(x.mount)}: ${plainText(err.message)}`,\n ),\n );\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"aimock MCP recorder could not reach the upstream\" }));\n } else if (!res.writableEnded) {\n res.end();\n }\n finish();\n });\n upstreamReq.on(\"response\", (up) => {\n this.relay(x, up, kind, finish);\n });\n upstreamReq.end(body === \"\" ? undefined : body);\n });\n }\n\n /** The upstream URL of a request: the endpoint plus the sub-path, the query minus identity params. */\n private upstreamUrl(req: http.IncomingMessage, subPath: string): URL {\n const url = new URL(this.target.href);\n if (subPath !== \"\" && subPath !== \"/\") {\n url.pathname = url.pathname.replace(/\\/+$/, \"\") + subPath;\n }\n // MR2 (c): the client's query pairs are forwarded byte for byte (never\n // re-serialized, which would change their encoding), minus the identity\n // parameters aimock reads.\n const raw = (req.url ?? \"\").split(\"#\")[0];\n const q = raw.indexOf(\"?\");\n const kept =\n q === -1\n ? []\n : raw\n .slice(q + 1)\n .split(\"&\")\n .filter((pair) => pair !== \"\" && !IDENTITY_QUERY.has(queryName(pair)));\n if (kept.length > 0) {\n const own = url.search.replace(/^\\?/, \"\");\n url.search = [own, ...kept].filter((part) => part !== \"\").join(\"&\");\n }\n return url;\n }\n\n /** MR2 (d), MR14: relay one upstream response, buffering a recordable one. */\n private relay(\n x: Exchange,\n up: http.IncomingMessage,\n kind: RecordKind | null,\n finish: () => void,\n ): void {\n const { req, res } = x;\n const status = up.statusCode ?? 502;\n const sessionId = headerOf(req, \"mcp-session-id\");\n const rpcId = x.rpcId;\n const UPSTREAM_ABORTED_ERROR = \"aimock MCP recorder: the upstream response failed\";\n // Set by the recordable paths below; true once the call was recorded or skipped.\n let processed = false;\n // A client that goes away makes forward() destroy the upstream request,\n // which errors `up`: that is not an upstream failure.\n let clientGone = false;\n res.on(\"close\", () => {\n if (!res.writableFinished) clientGone = true;\n });\n // Set by the SSE path: a JSON-RPC error for the open request, so a client\n // waiting on the stream fails at once instead of at its own timeout.\n let sseError: (() => string) | null = null;\n // An upstream that dies mid-body is never relayed as a complete answer, on\n // any path: a 502 before headers, a terminated stream after them.\n up.on(\"error\", (err: Error) => {\n // A client that left, or an answer already complete (MR14), has nothing to fail.\n if (clientGone || res.writableEnded) {\n finish();\n return;\n }\n // An SSE call recorded before the stream died keeps its recording; one\n // whose response was already relayed gets no second answer.\n const answered = processed;\n const recorded = answered && x.recordSkipped === undefined && x.recordError === undefined;\n processed = true;\n if (!recorded) x.recordSkipped = \"upstream-aborted\";\n this.log().error(\n build(\n msg`MCP-RECORD: the upstream response failed mid-body (${fixed(\"upstream-aborted\")}) for ${plainText(x.rpcMethod ?? req.method ?? \"?\")} on mount ${plainText(x.mount)}: ${plainText(err.message)}`,\n ),\n );\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: UPSTREAM_ABORTED_ERROR }));\n finish();\n return;\n }\n if (!answered && sseError !== null && !res.writableEnded) {\n res.write(sseError(), () => res.destroy());\n } else {\n res.destroy();\n }\n finish();\n });\n\n // MR14: a 401, or a 403 with insufficient_scope, is never relayed.\n const challenge = String(up.headers[\"www-authenticate\"] ?? \"\");\n if (status === 401 || (status === 403 && /insufficient_scope/i.test(challenge))) {\n up.resume();\n x.recordSkipped = \"upstream-auth\";\n this.log().error(\n build(\n msg`MCP-RECORD: recording refused (upstream-auth): the upstream answered ${status} for ${plainText(x.rpcMethod ?? req.method ?? \"?\")} on mount ${plainText(x.mount)}`,\n ),\n );\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: AUTH_FAILURE_ERROR, upstreamStatus: status }));\n finish();\n return;\n }\n // MR3: the session map entry goes with an upstream 404 or a deleted session.\n if (\n sessionId &&\n (status === 404 || (req.method === \"DELETE\" && status >= 200 && status < 300))\n ) {\n this.sessions.delete(sessionId);\n }\n\n const headers: Record<string, string | string[]> = {};\n for (const [name, value] of Object.entries(up.headers)) {\n if (value === undefined || HOP_BY_HOP.has(name.toLowerCase())) continue;\n headers[name] = value;\n }\n const ok = status >= 200 && status < 300;\n if (kind === \"tools/call\" && !ok) this.skip(x, \"upstream-status\", \"tools/call\");\n if (kind === \"tools/list\" && !ok) this.skip(x, \"upstream-status\", \"tools/list\");\n\n if (kind === null || !ok) {\n res.writeHead(status, headers);\n up.pipe(res);\n up.on(\"end\", finish);\n return;\n }\n\n const isSse = String(up.headers[\"content-type\"] ?? \"\").includes(\"text/event-stream\");\n const messages: TimedMessage[] = [];\n let size = 0;\n let overflow = false;\n const process = (): void => {\n if (processed) return;\n processed = true;\n if (overflow) {\n this.skip(x, \"buffer-cap\", kind);\n return;\n }\n // This runs in a stream event handler, outside handle()'s try: a throw\n // here must never reach the process (G2b A1).\n try {\n this.onRecordable(x, kind, messages, up);\n } catch (err) {\n // RL1 never carries values: an unexpected error is named, not quoted.\n this.fail(x, \"record-error\", err instanceof Error ? err.name : typeof err);\n }\n };\n\n if (isSse) {\n res.writeHead(status, headers);\n res.flushHeaders();\n const decoder = new StringDecoder(\"utf8\");\n let pending = \"\";\n if (rpcId !== undefined) {\n const error = { code: -32603, message: UPSTREAM_ABORTED_ERROR };\n sseError = () => {\n // A half-sent upstream event is closed under a type clients ignore,\n // so it never merges into the error event.\n const close =\n pending === \"\"\n ? \"\"\n : `${/[\\r\\n]$/.test(pending) ? \"\" : \"\\n\"}event: aimock-discarded\\n\\n`;\n return `${close}event: message\\ndata: ${JSON.stringify({ jsonrpc: \"2.0\", id: rpcId, error })}\\n\\n`;\n };\n }\n up.on(\"data\", (chunk: Buffer) => {\n if (!processed && !overflow) {\n size += chunk.length;\n if (size > this.maxBuffer) {\n overflow = true;\n process();\n } else {\n pending += decoder.write(chunk);\n const events = pending.split(/\\r?\\n\\r?\\n/);\n pending = events.pop() ?? \"\";\n for (const event of events) {\n for (const message of sseMessages(event)) {\n messages.push({ at: performance.now() - x.arrivedAt, message });\n }\n }\n // The response is recorded before the client sees it, so a\n // client that reads the file right after its call sees the write.\n if (messages.some((m) => isResponseTo(m.message, rpcId))) process();\n }\n }\n res.write(chunk);\n });\n up.on(\"end\", () => {\n if (!processed && !overflow) {\n for (const message of sseMessages(pending + decoder.end())) {\n messages.push({ at: performance.now() - x.arrivedAt, message });\n }\n }\n process();\n res.end();\n finish();\n });\n return;\n }\n\n // JSON: buffered whole, so a record error can still be sent as a header.\n const chunks: Buffer[] = [];\n up.on(\"data\", (chunk: Buffer) => {\n if (overflow) {\n res.write(chunk);\n return;\n }\n size += chunk.length;\n if (size > this.maxBuffer) {\n overflow = true;\n process();\n res.writeHead(status, headers);\n for (const c of chunks) res.write(c);\n res.write(chunk);\n chunks.length = 0;\n return;\n }\n chunks.push(chunk);\n });\n up.on(\"end\", () => {\n if (!overflow) {\n const text = Buffer.concat(chunks).toString(\"utf8\");\n try {\n const parsed: unknown = JSON.parse(text);\n for (const message of Array.isArray(parsed) ? parsed : [parsed]) {\n if (isObject(message)) messages.push({ at: performance.now() - x.arrivedAt, message });\n }\n } catch {\n /* not JSON: nothing to record */\n }\n process();\n if (x.recordError !== undefined) {\n headers[\"X-AIMock-Record-Error\"] = sanitizeHeaderValue(x.recordError);\n }\n res.writeHead(status, headers);\n res.end(text === \"\" ? undefined : Buffer.concat(chunks));\n } else {\n res.end();\n }\n finish();\n });\n }\n\n // ---- MR3, MR6-MR10, MR16 ----\n\n /** A recordable response, once its JSON-RPC response arrived (or its stream ended). */\n private onRecordable(\n x: Exchange,\n kind: RecordKind,\n messages: TimedMessage[],\n up: http.IncomingMessage,\n ): void {\n const response = messages.find((m) => isResponseTo(m.message, x.rpcId));\n if (kind === \"initialize\") {\n this.onInitialize(x, response?.message, up);\n return;\n }\n if (!response) {\n this.skip(x, \"upstream-error\", kind);\n return;\n }\n const reason =\n kind === \"tools/call\"\n ? callSkipReason(response.message, messages)\n : isObject(response.message.result)\n ? null\n : \"upstream-error\";\n if (reason !== null) {\n this.skip(x, reason, kind);\n return;\n }\n if (!this.versionRecordable(x)) {\n this.skip(x, \"unsupported-version\", kind);\n return;\n }\n const identity = x.identity!;\n if (identity.testId === null && identity.context === null) {\n // MR9 / RL3: forwarded, not written, warned once per mount.\n x.recordSkipped = \"no-scope\";\n if (!this.warnedNoScope.has(x.mount)) {\n this.warnedNoScope.add(x.mount);\n this.log().warn(\n build(\n msg`MCP-RECORD: forwarded, not recorded: a request on mount ${plainText(x.mount)} has no testId or context, so it has no recording scope (logged once per mount)`,\n ),\n );\n }\n return;\n }\n if (this.config.proxyOnly) return;\n if (kind === \"tools/list\") this.onToolsList(x, response.message);\n else this.recordCall(x, response, messages);\n }\n\n /** MR3 / AM4: bind what an upstream `initialize` negotiated to its session. */\n private onInitialize(\n x: Exchange,\n response: Record<string, unknown> | undefined,\n up: http.IncomingMessage,\n ): void {\n const result = response?.result;\n if (!isObject(result)) return;\n const version = typeof result.protocolVersion === \"string\" ? result.protocolVersion : null;\n const recordable = version !== null && RECORDABLE_VERSIONS.has(version);\n if (!recordable) this.skip(x, \"unsupported-version\", \"initialize\");\n const sessionId = headerOf(up, \"mcp-session-id\");\n if (!sessionId) return;\n const identity = x.identity!;\n this.sessions.set(sessionId, {\n protocolVersion: version,\n recordable,\n ...(isObject(result.serverInfo) ? { serverInfo: result.serverInfo } : {}),\n clientCapabilities: x.params?.capabilities,\n clientInfo: x.params?.clientInfo,\n // I3: only the values the initialize request itself supplied are bound.\n ...(x.supplied.testId && identity.testId !== null ? { testId: identity.testId } : {}),\n ...(x.supplied.context && identity.context !== null ? { context: identity.context } : {}),\n ...(x.supplied.undeclared && identity.undeclared !== null\n ? { undeclared: identity.undeclared }\n : {}),\n });\n }\n\n /** AM4: false when the session negotiated, or the request names, a version not recorded. */\n private versionRecordable(x: Exchange): boolean {\n const sessionId = headerOf(x.req, \"mcp-session-id\");\n const session = sessionId ? this.sessions.get(sessionId) : undefined;\n if (session && !session.recordable) return false;\n const header = headerOf(x.req, \"mcp-protocol-version\");\n return header === undefined || RECORDABLE_VERSIONS.has(header);\n }\n\n /** MR16: collect a `tools/list` page; write the list when it completes. */\n private onToolsList(x: Exchange, response: Record<string, unknown>): void {\n const result = response.result as Record<string, unknown>;\n if (!Array.isArray(result.tools)) {\n this.skip(x, \"upstream-error\", \"tools/list\");\n return;\n }\n const identity = x.identity!;\n const key = listKey(x.mount, identity);\n const cursor = x.params?.cursor;\n const next = typeof result.nextCursor === \"string\" ? result.nextCursor : undefined;\n let tools: unknown[];\n if (cursor === undefined) {\n if (this.pendingLists.has(key)) {\n this.log().warn(\n build(\n msg`MCP-RECORD: forwarded, not recorded (list-incomplete): tools/list on mount ${plainText(x.mount)}`,\n ),\n );\n }\n tools = [...result.tools];\n } else {\n const pending = this.pendingLists.get(key);\n if (!pending || pending.next !== cursor) {\n this.skip(x, \"list-orphan-page\", \"tools/list\");\n return;\n }\n tools = [...pending.tools, ...result.tools];\n }\n this.pendingLists.delete(key);\n if (next !== undefined) {\n this.pendingLists.set(key, { tools, next, testId: identity.testId });\n return;\n }\n const filepath = this.targetFile(identity);\n const scope = scopeOf(identity);\n if (!fileHasBlock(filepath, scope, x.mount)) {\n // A block needs a call entry; the list waits for the block's first one.\n this.completedLists.set(key, { tools, testId: identity.testId });\n return;\n }\n let list: { value: unknown[]; warnings: string[] };\n try {\n list = sanitizeRecording(tools, this.secrets(x), isMetaBranch);\n } catch (err) {\n this.failUnsafe(x, err);\n return;\n }\n this.persist(x, filepath, scope, (block) => ({\n block: withList(block ?? {}, list.value),\n warnings: (prefix) => list.warnings.map((w) => `${prefix}/list${w}`),\n }));\n }\n\n /** MR6 / MR7 / MR10-MR12: record one forwarded `tools/call`. */\n private recordCall(x: Exchange, response: TimedMessage, messages: TimedMessage[]): void {\n const identity = x.identity!;\n const name = String(x.params?.name ?? \"\");\n const result = cleanResult(response.message.result as Record<string, unknown>);\n const entry: Record<string, unknown> = { args: x.params?.arguments ?? {} };\n const errorText = errorTextOf(result);\n if (errorText !== null) entry.error = errorText;\n else entry.result = result;\n const notifications = messages\n .filter(\n (m) =>\n m !== response &&\n m.at <= response.at &&\n !(\"id\" in m.message) &&\n typeof m.message.method === \"string\" &&\n m.message.method.startsWith(\"notifications/\"),\n )\n .map((m) => ({\n atMs: Math.round(m.at),\n method: m.message.method as string,\n params: isObject(m.message.params) ? m.message.params : {},\n }));\n if (notifications.length > 0) entry.notifications = notifications;\n entry.durationMs = Math.round(response.at);\n\n const filepath = this.targetFile(identity);\n const scope = scopeOf(identity);\n const key = listKey(x.mount, identity);\n const waitingList = this.completedLists.get(key);\n const secrets = this.secrets(x);\n let clean: { value: Record<string, unknown>; warnings: string[] };\n let recorded: { value: Record<string, unknown>; warnings: string[] };\n let list: { value: unknown[]; warnings: string[] } | null = null;\n try {\n clean = sanitizeRecording(entry, secrets, isMetaBranch);\n recorded = sanitizeRecording(this.provenance(x, response.message), secrets, () => true);\n if (waitingList) list = sanitizeRecording(waitingList.tools, secrets, isMetaBranch);\n } catch (err) {\n this.failUnsafe(x, err);\n return;\n }\n const written = this.persist(x, filepath, scope, (block) => {\n if (!block) {\n return {\n block: {\n scope,\n mount: x.mount,\n ...(list ? { list: list.value } : {}),\n recorded: recorded.value,\n tools: [{ name, calls: [clean.value] }],\n },\n warnings: (prefix) => [\n ...(list ? list.warnings.map((w) => `${prefix}/list${w}`) : []),\n ...recorded.warnings.map((w) => `${prefix}/recorded${w}`),\n ...clean.warnings.map((w) => `${prefix}/tools/0/calls/0${w}`),\n ],\n };\n }\n const tools = Array.isArray(block.tools) ? [...(block.tools as unknown[])] : [];\n let t = tools.findIndex((tool) => isObject(tool) && tool.name === name);\n let c = 0;\n if (t === -1) {\n tools.push({ name, calls: [clean.value] });\n t = tools.length - 1;\n } else {\n const tool = tools[t] as Record<string, unknown>;\n const calls = Array.isArray(tool.calls) ? [...(tool.calls as unknown[])] : [];\n calls.push(clean.value);\n c = calls.length - 1;\n tools[t] = { ...tool, calls };\n }\n // A list that completed before this block appeared (G2b A2) replaces its list.\n const updated = list ? withList({ ...block, tools }, list.value) : { ...block, tools };\n return {\n block: updated,\n warnings: (prefix) => [\n ...(list ? list.warnings.map((w) => `${prefix}/list${w}`) : []),\n ...clean.warnings.map((w) => `${prefix}/tools/${t}/calls/${c}${w}`),\n ],\n };\n });\n if (!written) return;\n if (waitingList) this.completedLists.delete(key);\n\n // MR12: the new entry is served from memory as `record#<n>`, consumed\n // for the recording test id, so an identical call misses and is recorded.\n const before = new Set(this.host.fakes.allEntryIds());\n try {\n this.host.addMcpFakes(\n [\n {\n source: \"record\",\n blockIndex: null,\n raw: { scope, mount: x.mount, tools: [{ name, calls: [clean.value] }] },\n },\n ],\n { kind: \"record\" },\n );\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n this.log().error(\n build(\n msg`MCP-RECORD: the recorded entry was written but not registered on mount ${plainText(x.mount)}: ${plainText(detail)}`,\n ),\n );\n return;\n }\n const added = this.host.fakes.allEntryIds().filter((id) => !before.has(id));\n this.host.fakes.markConsumed(identity.testId, added);\n }\n\n /** FA2 `recorded` for a new block (MR6 3). */\n private provenance(x: Exchange, response: Record<string, unknown>): Record<string, unknown> {\n const sessionId = headerOf(x.req, \"mcp-session-id\");\n const session = sessionId ? this.sessions.get(sessionId) : undefined;\n const meta = isObject(x.params?._meta) ? x.params._meta : undefined;\n const result = isObject(response.result) ? response.result : undefined;\n const resultMeta = isObject(result?._meta) ? result._meta : undefined;\n const protocolVersion =\n session?.protocolVersion ??\n (typeof meta?.[PROTOCOL_VERSION_META] === \"string\"\n ? meta[PROTOCOL_VERSION_META]\n : undefined) ??\n headerOf(x.req, \"mcp-protocol-version\") ??\n FALLBACK_VERSION;\n const serverInfo =\n session?.serverInfo ??\n (isObject(resultMeta?.[SERVER_INFO_META]) ? resultMeta[SERVER_INFO_META] : undefined);\n return {\n upstream: this.target.origin,\n protocolVersion,\n ...(serverInfo ? { serverInfo } : {}),\n aimockVersion: aimockVersion((m) => this.log().warn(m)),\n at: new Date().toISOString(),\n };\n }\n\n /** S2 (a)-(d): the known secrets of one request. */\n private secrets(x: Exchange): string[] {\n // The raw values: `flattenHeaders` masks API-key headers for the\n // journal, which would hide the very values S2 (a) must redact.\n const headers: Record<string, string> = {};\n for (const [name, value] of Object.entries(x.req.headers)) {\n if (value !== undefined) headers[name] = Array.isArray(value) ? value.join(\", \") : value;\n }\n return knownSecrets({\n headers,\n ...(this.auth ? { upstreamAuth: this.auth } : {}),\n // G2b B1: the URL really sent upstream, so a credential in the\n // client's query string (forwarded per MR2 c) is a known secret (S2 c).\n upstreamUrl: this.upstreamUrl(x.req, x.subPath).href,\n secretValues: this.secretValues,\n });\n }\n\n /** S6 / RL1: an unsafe recording is not written. */\n private failUnsafe(x: Exchange, err: unknown): void {\n if (!(err instanceof RecordUnsafeError)) throw err;\n this.fail(x, \"unsafe\", err.reason);\n }\n\n /** RL1: a recording that was not written, with its reason (never a value). */\n private fail(x: Exchange, reason: string, detail: string): void {\n x.recordSkipped = reason;\n x.recordError = `${reason}: ${detail}`;\n this.log().error(\n build(\n msg`MCP-RECORD: recording failed (${fixed(reason)}) for ${plainText(x.rpcMethod ?? \"?\")} on mount ${plainText(x.mount)}: ${plainText(detail)}`,\n ),\n );\n }\n\n /**\n * MR10 / MR11: merge one change into the block of `scope` and this mount\n * (appending a block when none matches), validate the whole file, and write\n * it. `apply` gets that block (`undefined` when it is new) and returns the\n * new block and its sanitizer warnings under the block's pointer.\n */\n private persist(\n x: Exchange,\n filepath: string,\n scope: Record<string, string>,\n apply: (block: Record<string, unknown> | undefined) => {\n block: Record<string, unknown>;\n warnings: (blockPointer: string) => string[];\n },\n ): boolean {\n const result = persistServiceFakes({\n filepath,\n logger: this.log(),\n merge: (existing) => mergeBlock(existing, scope, x.mount, apply),\n validate: (content) => {\n const first = validateMcpFakes(content.mcpFakes, filepath).errors[0];\n if (!first) return null;\n const where = first.entryId ?? first.blockId;\n return where ? `${first.rule} (${where})` : first.rule;\n },\n });\n if (result.kind === \"failed\") {\n this.fail(x, result.stage === \"write\" ? \"write-failed\" : \"invalid-merge\", result.error);\n return false;\n }\n const resolved = path.resolve(result.filepath);\n this.hashes.set(resolved, result.sha256);\n for (const listener of this.listeners) {\n try {\n listener(resolved, result.content.mcpFakes);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n this.log().error(\n build(\n msg`MCP-RECORD: a write listener failed for ${plainText(resolved)} on mount ${plainText(x.mount)}: ${plainText(detail)}`,\n ),\n );\n }\n }\n return true;\n }\n\n /** MR10: the file a recording of `identity` goes to. */\n private targetFile(identity: McpFakeIdentity): string {\n const slug =\n identity.testId !== null\n ? slugifyTestId(identity.testId)\n : identity.context !== null\n ? slugifyContext(identity.context)\n : \"\";\n if (slug) return path.join(this.fixturePath, slug, \"mcp.json\");\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n return path.join(this.fixturePath, `mcp-${timestamp}-${crypto.randomUUID().slice(0, 8)}.json`);\n }\n\n /** MR8 / RL2: mark the request forwarded-not-recorded and warn. */\n private skip(x: Exchange, reason: string, method: string): void {\n x.recordSkipped = reason;\n this.log().warn(\n build(\n msg`MCP-RECORD: forwarded, not recorded (${fixed(reason)}): ${plainText(method)} on mount ${plainText(x.mount)}`,\n ),\n );\n }\n\n private log(): Logger {\n return this.host.logger() ?? this.fallbackLogger;\n }\n\n /** AM8: one journal entry per request. */\n private journal(x: Exchange): void {\n const { req, res } = x;\n this.host.journalEntry({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: x.message,\n service: \"mcp\",\n ...(x.identity\n ? {\n testId: x.identity.testId ?? MCP_FAKES_DEFAULT_TEST_ID,\n context: x.identity.context,\n }\n : {}),\n response: {\n status: res.statusCode,\n fixture: null,\n ...(x.proxied ? { source: \"proxy\" as const } : {}),\n ...(x.recordSkipped !== undefined ? { recordSkipped: x.recordSkipped } : {}),\n ...(x.mcpFake ? { mcpFake: x.mcpFake } : {}),\n },\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** One header's value (the first, when repeated), or undefined. */\nfunction headerOf(req: http.IncomingMessage, name: string): string | undefined {\n const value = req.headers[name];\n return Array.isArray(value) ? value[0] : value;\n}\n\n/** The JSON-RPC messages of one SSE event (its `data:` lines). */\nfunction sseMessages(event: string): Record<string, unknown>[] {\n const data = event\n .split(/\\r?\\n/)\n .filter((line) => line.startsWith(\"data:\"))\n .map((line) => line.slice(5).replace(/^ /, \"\"))\n .join(\"\\n\");\n if (data === \"\") return [];\n try {\n const parsed: unknown = JSON.parse(data);\n return (Array.isArray(parsed) ? parsed : [parsed]).filter(isObject);\n } catch {\n return [];\n }\n}\n\n/** Whether `message` is the JSON-RPC response to request `id`. */\nfunction isResponseTo(message: Record<string, unknown>, id: string | number | undefined): boolean {\n return id !== undefined && message.id === id && (\"result\" in message || \"error\" in message);\n}\n\n/** The decoded name of one raw `name=value` query pair (form rules: `+` is a space). */\nfunction queryName(pair: string): string {\n const eq = pair.indexOf(\"=\");\n const name = (eq === -1 ? pair : pair.slice(0, eq)).replace(/\\+/g, \" \");\n try {\n return decodeURIComponent(name);\n } catch {\n return name;\n }\n}\n\nfunction mountOfKey(key: string): string {\n return key.slice(0, key.indexOf(\"\\u0000\"));\n}\n\n/** MR8: why a forwarded `tools/call` response is not recorded, or null. */\nfunction callSkipReason(\n response: Record<string, unknown>,\n messages: TimedMessage[],\n): string | null {\n if (\"error\" in response) return \"upstream-error\";\n const result = response.result;\n if (!isObject(result)) return \"upstream-error\";\n if (result.resultType === \"input_required\") return \"input-required\";\n if (result.task !== undefined || (isObject(result._meta) && RELATED_TASK_META in result._meta)) {\n return \"task-handle\";\n }\n if (messages.some((m) => typeof m.message.method === \"string\" && \"id\" in m.message)) {\n return \"server-request\";\n }\n return null;\n}\n\n/** MR7: a recorded result without trace `_meta` keys, serverInfo and `resultType: \"complete\"`. */\nfunction cleanResult(result: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = { ...result };\n if (out.resultType === \"complete\") delete out.resultType;\n if (isObject(out._meta)) {\n const meta: Record<string, unknown> = { ...out._meta };\n for (const key of DROPPED_META) delete meta[key];\n if (Object.keys(meta).length > 0) out._meta = meta;\n else delete out._meta;\n }\n return out;\n}\n\n/** MR6: the `error` text of an `isError` result that is exactly one text block, else null. */\nfunction errorTextOf(result: Record<string, unknown>): string | null {\n if (result.isError !== true) return null;\n if (!Object.keys(result).every((k) => k === \"content\" || k === \"isError\")) return null;\n const content = result.content;\n if (!Array.isArray(content) || content.length !== 1) return null;\n const block: unknown = content[0];\n if (!isObject(block) || block.type !== \"text\" || typeof block.text !== \"string\") return null;\n return Object.keys(block).length === 2 ? block.text : null;\n}\n\n/** S4: credential-shaped keys are removed only inside `_meta`. */\nfunction isMetaBranch(pointer: string): boolean {\n return /(?:^|\\/)_meta(?:\\/|$)/.test(pointer);\n}\n\n/** MR10 block scope: the test id, plus the context when there is one. */\nfunction scopeOf(identity: McpFakeIdentity): Record<string, string> {\n if (identity.testId === null) return { context: identity.context ?? \"\" };\n return identity.context === null\n ? { testId: identity.testId }\n : { testId: identity.testId, context: identity.context };\n}\n\n/** MR10: a block scope exactly equal to `scope`. */\nfunction sameScope(raw: unknown, scope: Record<string, string>): boolean {\n if (!isObject(raw)) return false;\n const keys = Object.keys(scope);\n return Object.keys(raw).length === keys.length && keys.every((k) => raw[k] === scope[k]);\n}\n\n/** MR16 pending-list key: mount and scope. */\nfunction listKey(mount: string, identity: McpFakeIdentity): string {\n return `${mount}\\u0000${JSON.stringify([identity.testId, identity.context])}`;\n}\n\n/** Whether the file at `filepath` already has the block of `scope` and `mount`. */\nfunction fileHasBlock(filepath: string, scope: Record<string, string>, mount: string): boolean {\n let parsed: unknown;\n try {\n parsed = JSON.parse(fs.readFileSync(filepath, \"utf-8\"));\n } catch {\n return false;\n }\n if (!isObject(parsed)) return false;\n const raw = parsed.mcpFakes;\n const blocks: unknown[] = Array.isArray(raw) ? raw : raw === undefined ? [] : [raw];\n return blocks.some((b) => isObject(b) && sameScope(b.scope, scope) && b.mount === mount);\n}\n\n/** MR10 exception: a block with `list` replaced (or inserted after `mount`), key order kept. */\nfunction withList(block: Record<string, unknown>, list: unknown[]): Record<string, unknown> {\n if (\"list\" in block) return { ...block, list };\n const out: Record<string, unknown> = {};\n let placed = false;\n for (const [key, value] of Object.entries(block)) {\n out[key] = value;\n if (key === \"mount\") {\n out.list = list;\n placed = true;\n }\n }\n if (!placed) out.list = list;\n return out;\n}\n\n/**\n * MR10 append rule over a whole file: the first block whose scope and mount\n * are exactly equal is changed by `apply`; otherwise a block is appended.\n * Object form becomes array form only when a second block is needed. Other\n * top-level keys are kept (fakes:F9); `_warnings` gains the new pointers.\n */\nfunction mergeBlock(\n existing: Record<string, unknown> | null,\n scope: Record<string, string>,\n mount: string,\n apply: (block: Record<string, unknown> | undefined) => {\n block: Record<string, unknown>;\n warnings: (blockPointer: string) => string[];\n },\n): Record<string, unknown> {\n const content: Record<string, unknown> = existing ? { ...existing } : {};\n const raw = content.mcpFakes;\n const blocks: unknown[] = Array.isArray(raw) ? [...raw] : raw === undefined ? [] : [raw];\n let index = blocks.findIndex(\n (b) => isObject(b) && sameScope(b.scope, scope) && b.mount === mount,\n );\n const applied = apply(index === -1 ? undefined : (blocks[index] as Record<string, unknown>));\n if (index === -1) {\n blocks.push(applied.block);\n index = blocks.length - 1;\n } else {\n blocks[index] = applied.block;\n }\n const arrayForm = Array.isArray(raw) || blocks.length > 1;\n content.mcpFakes = arrayForm ? blocks : blocks[0];\n let warnings = Array.isArray(content._warnings)\n ? content._warnings.filter((w): w is string => typeof w === \"string\")\n : [];\n if (isObject(raw) && arrayForm) {\n // The file's only block moved from /mcpFakes to /mcpFakes/0.\n warnings = warnings.map((w) =>\n /^\\/mcpFakes(?=[/:])/.test(w) ? `/mcpFakes/0${w.slice(\"/mcpFakes\".length)}` : w,\n );\n }\n const added = applied.warnings(arrayForm ? `/mcpFakes/${index}` : \"/mcpFakes\");\n if (warnings.length > 0 || added.length > 0)\n content._warnings = [...new Set([...warnings, ...added])];\n return content;\n}\n\n// ---- Wiring: the `llmock` flags and `llm.record.mcp` (MR1, AM6) ----\n\n/** One `--mcp-record` / `--mcp-proxy-only` value: `<mount>=<url>`. */\nexport interface McpRecordFlag {\n mount: string;\n upstream: string;\n}\n\n/**\n * A mount key as an error message may show it: JSON-quoted, with any\n * credential (URL userinfo, a credential query parameter, a secret-looking\n * fragment) removed by the record sanitizer's URL rule (S2 c) and marked as\n * removed. A key with an `@` that the URL rule cannot parse is not shown.\n */\nexport function redactMountForError(mount: string): string {\n const absolute = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(mount);\n const prefix = absolute ? \"\" : mount.startsWith(\"/\") ? \"http://localhost\" : \"http://\";\n const scrubbed = scrubUrl(\n prefix + mount,\n () => false,\n () => {\n throw new Error(\"unreachable: no value is a secret here\");\n },\n );\n if (scrubbed !== prefix + mount) {\n const shown = scrubbed.startsWith(prefix) ? scrubbed.slice(prefix.length) : REDACTED;\n return `${JSON.stringify(shown)} (credentials removed)`;\n }\n return mount.includes(\"@\") && !URL.canParse(prefix + mount)\n ? JSON.stringify(REDACTED)\n : JSON.stringify(mount);\n}\n\n/**\n * Why a mount path can never be reached, or `undefined` when it can. The\n * server matches a request's URL pathname against the mount exactly or as a\n * `<mount>/` prefix, so a mount must be a plain, normalized URL path: it\n * starts with one `/`, has no trailing `/` (except the root `/`), and has no\n * query, fragment, whitespace, dot segment or character the URL parser escapes.\n */\nexport function unreachableMountReason(mount: string): string | undefined {\n if (!mount.startsWith(\"/\")) return 'it must start with \"/\"';\n if (mount.startsWith(\"//\")) return 'it must not start with \"//\"';\n if (mount.length > 1 && mount.endsWith(\"/\")) return 'it must not end with \"/\"';\n if (new URL(mount, \"http://localhost\").pathname !== mount) {\n return \"it must be a plain URL path, with no query, fragment, whitespace, dot segment or escaped character\";\n }\n return undefined;\n}\n\n/**\n * Why an upstream URL is unusable, or `undefined` when it is a valid http(s)\n * URL. Never includes the value: it can carry credentials (S2 c).\n */\nfunction badUpstreamReason(upstream: string): string | undefined {\n let url: URL;\n try {\n url = new URL(upstream);\n } catch {\n return \"is not a valid URL\";\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n return \"must be an http: or https: URL\";\n }\n return undefined;\n}\n\n/**\n * F2: fail at start, not at every write, when the recording destination can\n * never be written. The deepest path component that exists must be a\n * directory this process can write to and enter. A path that does not exist\n * yet is created on the first write, but not through a broken symbolic link.\n * `label` prefixes the error (for example the mount it belongs to).\n */\nexport function checkMcpRecordDestination(fixturePath: string, label?: string): void {\n const destination = path.resolve(fixturePath);\n const fail = (problem: string): never => {\n const text = `MCP record destination ${destination} ${problem}`;\n throw new Error(label ? `${label}: ${text}` : text);\n };\n const code = (err: unknown): string => (err as NodeJS.ErrnoException | null)?.code ?? String(err);\n let probe = destination;\n for (;;) {\n let stat: fs.Stats | undefined;\n let broken = false;\n try {\n const entry = fs.lstatSync(probe, { throwIfNoEntry: false });\n stat = entry?.isSymbolicLink() ? fs.statSync(probe, { throwIfNoEntry: false }) : entry;\n broken = entry !== undefined && stat === undefined;\n } catch (err) {\n // ENOTDIR: an ancestor is a file; the walk up finds it.\n if (code(err) !== \"ENOTDIR\") fail(`cannot be checked (${code(err)} at ${probe})`);\n }\n if (broken) fail(`goes through a broken symbolic link at ${probe}`);\n if (stat !== undefined) {\n if (!stat.isDirectory()) fail(\"is not a directory\");\n try {\n fs.accessSync(probe, fs.constants.W_OK | fs.constants.X_OK);\n } catch (err) {\n fail(`is not writable (${code(err)} at ${probe})`);\n }\n return;\n }\n const parent = path.dirname(probe);\n if (parent === probe) return;\n probe = parent;\n }\n}\n\n/**\n * Parse a `--mcp-record` / `--mcp-proxy-only` value at its first `=`. The\n * mount must be a reachable path and the upstream an http(s) URL. A bad value\n * throws an error that names `flag`, never the URL (it can carry credentials).\n */\nexport function parseMcpRecordFlag(value: string, flag = \"--mcp-record\"): McpRecordFlag {\n const eq = value.indexOf(\"=\");\n const mount = eq === -1 ? \"\" : value.slice(0, eq);\n const upstream = eq === -1 ? \"\" : value.slice(eq + 1);\n if (!mount.startsWith(\"/\")) {\n throw new Error(`${flag} must be <mount>=<url>, with a mount that starts with \"/\"`);\n }\n const unreachable = unreachableMountReason(mount);\n if (unreachable !== undefined) {\n throw new Error(\n `${flag} mount ${redactMountForError(mount)} can never be reached: ${unreachable}`,\n );\n }\n const badUpstream = badUpstreamReason(upstream);\n if (badUpstream !== undefined) {\n throw new Error(`${flag} ${mount}: the upstream ${badUpstream}`);\n }\n return { mount, upstream };\n}\n\n/**\n * AM6 / S2 (d): the recorder settings that come from the environment.\n * `AIMOCK_RECORD_SECRET_VALUES` is newline-separated (LF or CRLF); empty\n * entries are dropped and nothing else is trimmed. A malformed\n * `AIMOCK_MCP_UPSTREAM_AUTH` throws an error that names the variable.\n */\nexport function mcpRecordEnv(env: NodeJS.ProcessEnv): {\n secretValues: string[];\n upstreamAuth: string | undefined;\n} {\n const upstreamAuth = env.AIMOCK_MCP_UPSTREAM_AUTH;\n parseUpstreamAuth(upstreamAuth);\n return {\n secretValues: (env.AIMOCK_RECORD_SECRET_VALUES ?? \"\").split(/\\r?\\n/).filter(Boolean),\n upstreamAuth: upstreamAuth === \"\" ? undefined : upstreamAuth,\n };\n}\n\n/** A mount that records MCP: an `MCPMock`. */\nexport interface RecordableMcpMount extends Mountable {\n enableRecording(config: McpRecordConfig): unknown;\n}\n\nexport interface WireMcpRecordingOptions {\n /** What the caller already mounted, by path. */\n mounted: ReadonlyMap<string, Mountable>;\n /**\n * The `MCPMock` class. It is passed in, because mcp-mock.ts imports this\n * module: a mounted instance of it records, and a free path gets a new one.\n */\n mcpMock: new () => RecordableMcpMount;\n /** `llm.fixtures`: the recording destination is `<fixtures>/recorded`. */\n fixtures?: string;\n /** `llm.record.fixturePath`: overrides the destination. */\n fixturePath?: string;\n strict?: boolean;\n env: NodeJS.ProcessEnv;\n}\n\n/**\n * MR1: `llm.record.mcp` — record each mount path. A path the caller mounted\n * with an `MCPMock` records through it; a path held by another kind of mount\n * is a start error; a free path gets a new `MCPMock`, mounted on `llmock`.\n * A string value is `{ upstream }`. `secretValues` and `upstreamAuth` come\n * from the value, else from the environment, as on the `llmock` CLI.\n */\nexport function wireMcpRecording(\n llmock: { mount(path: string, handler: Mountable): unknown },\n record: Record<string, string | McpRecordConfig>,\n opts: WireMcpRecordingOptions,\n): void {\n // F1: the config file is untyped JSON. Before anything is mounted, check\n // every mount's path, settings, upstream URL, holder and destination. The\n // McpRecorder constructor still checks secretValues and upstreamAuth while\n // mounting, so a failure there can leave earlier mounts mounted.\n // Errors name the mount and key, never a value (S2 c).\n const where = \"llm.record.mcp\";\n const plain = (v: unknown): v is Record<string, unknown> =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n const keyTypes: Record<keyof McpRecordConfig, string> = {\n upstream: \"a string\",\n fixturePath: \"a non-empty string\",\n proxyOnly: \"a boolean\",\n maxRecordBufferBytes: `a finite number greater than 0 and at most ${MAX_RECORD_BUFFER_BYTES}`,\n upstreamAuth: \"a string\",\n secretValues: \"an array of strings\",\n strict: \"a boolean\",\n };\n const hasType = (key: keyof McpRecordConfig, v: unknown): boolean => {\n switch (key) {\n case \"proxyOnly\":\n case \"strict\":\n return typeof v === \"boolean\";\n case \"maxRecordBufferBytes\":\n return typeof v === \"number\" && Number.isFinite(v) && v > 0 && v <= MAX_RECORD_BUFFER_BYTES;\n case \"secretValues\":\n return Array.isArray(v) && v.every((s) => typeof s === \"string\");\n case \"fixturePath\":\n return typeof v === \"string\" && v.length > 0;\n default:\n return typeof v === \"string\";\n }\n };\n const isConfigKey = (key: string): key is keyof McpRecordConfig =>\n Object.prototype.hasOwnProperty.call(keyTypes, key);\n const unknownRecord: unknown = record;\n if (!plain(unknownRecord)) {\n throw new Error(`${where} must be an object of <mount>: <url or record settings>`);\n }\n const sharedFixturePath: unknown = opts.fixturePath;\n if (\n sharedFixturePath !== undefined &&\n !(typeof sharedFixturePath === \"string\" && sharedFixturePath.length > 0)\n ) {\n throw new Error(\"llm.record.fixturePath must be a non-empty string\");\n }\n const wired: {\n mountPath: string;\n config: McpRecordConfig;\n fixturePath: string | undefined;\n held: RecordableMcpMount | undefined;\n }[] = [];\n for (const [mountPath, value] of Object.entries(unknownRecord)) {\n const mount = redactMountForError(mountPath);\n if (!mountPath.startsWith(\"/\")) {\n throw new Error(\n `${where} must be <mount>: <url or object>, with a mount that starts with \"/\"; got ${mount}`,\n );\n }\n const unreachable = unreachableMountReason(mountPath);\n if (unreachable !== undefined) {\n throw new Error(`${where} mount ${mount} can never be reached: ${unreachable}`);\n }\n if (typeof value !== \"string\" && !plain(value)) {\n throw new Error(`${where} mount ${mount} must be an upstream URL string or an object`);\n }\n if (typeof value !== \"string\") {\n for (const [key, v] of Object.entries(value)) {\n if (!isConfigKey(key)) {\n throw new Error(\n `${where} mount ${mount} has an unknown key ${JSON.stringify(key)} (expected one of ${Object.keys(keyTypes).join(\", \")})`,\n );\n }\n if (!hasType(key, v)) {\n throw new Error(`${where} mount ${mount} key ${key} must be ${keyTypes[key]}`);\n }\n }\n if (!(\"upstream\" in value)) {\n throw new Error(`${where} mount ${mount} key upstream is required`);\n }\n }\n // Every key now has its declared type.\n const typed = record[mountPath];\n const config: McpRecordConfig = typeof typed === \"string\" ? { upstream: typed } : typed;\n const badUpstream = badUpstreamReason(config.upstream);\n if (badUpstream !== undefined) {\n throw new Error(`${where} mount ${mount}: the upstream ${badUpstream}`);\n }\n const held = opts.mounted.get(mountPath);\n if (held !== undefined && !(held instanceof opts.mcpMock)) {\n throw new Error(\n `llm.record.mcp mount ${mountPath} is held by a mount that is not an MCP mock`,\n );\n }\n const fixturePath =\n config.fixturePath ??\n opts.fixturePath ??\n (opts.fixtures ? path.resolve(opts.fixtures, \"recorded\") : undefined);\n if (!config.proxyOnly) {\n if (fixturePath === undefined) {\n throw new Error(\n \"llm.record.mcp requires llm.fixtures or llm.record.fixturePath for the recording destination\",\n );\n }\n checkMcpRecordDestination(fixturePath, `${where} mount ${mount}`);\n }\n wired.push({ mountPath, config, fixturePath, held });\n }\n\n const fromEnv = mcpRecordEnv(opts.env);\n for (const { mountPath, config, fixturePath, held } of wired) {\n const mcp = held ?? new opts.mcpMock();\n mcp.enableRecording({\n ...config,\n fixturePath,\n secretValues: config.secretValues ?? fromEnv.secretValues,\n upstreamAuth: config.upstreamAuth ?? fromEnv.upstreamAuth,\n strict: config.strict ?? opts.strict,\n });\n if (held === undefined) llmock.mount(mountPath, mcp);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mcp-recorder.cjs","names":["Logger","readBody","resolveMcpIdentity","build","plainText","path","writeFakeToolAnswer","resolveRequestDecisions","fakeFailure","MCP_FAKE_ERROR_CODES","isAuthenticatedRequest","buildForwardHeaders","https","http","fixed","StringDecoder","sanitizeHeaderValue","sanitizeRecording","aimockVersion","knownSecrets","RecordUnsafeError","persistServiceFakes","validateMcpFakes","slugifyTestId","slugifyContext","crypto","flattenHeaders","MCP_FAKES_DEFAULT_TEST_ID","fs","scrubUrl","REDACTED"],"sources":["../src/mcp-recorder.ts"],"sourcesContent":["/**\n * MCP record mode (rr: MR1-MR17, par: AM1-AM9): a pass-through proxy inside\n * `MCPMock` that forwards to a real upstream MCP server, relays its answers,\n * and writes the `tools/list` and `tools/call` exchanges it sees as an\n * `mcpFakes` file (FA1-FA5), sanitized (S1-S7).\n *\n * Every file this module writes goes through `sanitizeRecording` and then\n * `persistServiceFakes`; there is no other filesystem write here.\n */\nimport * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport { StringDecoder } from \"node:string_decoder\";\nimport type {\n JournalEntry,\n McpFakeIdentity,\n McpFakeSource,\n McpFakeUndeclaredPolicy,\n McpRecordConfig,\n Mountable,\n} from \"./types.js\";\nimport { MCP_FAKE_ERROR_CODES } from \"./types.js\";\nimport { Logger } from \"./logger.js\";\nimport { isAuthenticatedRequest } from \"./api-key-auth.js\";\nimport {\n flattenHeaders,\n readBody,\n resolveMcpIdentity,\n slugifyContext,\n slugifyTestId,\n} from \"./helpers.js\";\nimport { resolveRequestDecisions } from \"./response/policy.js\";\nimport {\n buildForwardHeaders,\n persistServiceFakes,\n removeForwardHeader,\n sanitizeHeaderValue,\n} from \"./recorder.js\";\nimport {\n MCP_FAKES_DEFAULT_TEST_ID,\n validateMcpFakes,\n type McpFakeAddOriginInternal,\n type McpFakeAddResult,\n type McpFakeClaim,\n type McpFakeStore,\n} from \"./mcp-fakes.js\";\nimport { fakeFailure, writeFakeToolAnswer, type McpFakeEvent } from \"./mcp-handler.js\";\nimport {\n REDACTED,\n RecordUnsafeError,\n knownSecrets,\n sanitizeRecording,\n scrubUrl,\n validateSecretValues,\n} from \"./record-sanitize.js\";\nimport { aimockVersion } from \"./version.js\";\nimport { build, fixed, msg, plainText } from \"./message-text.js\";\n\n/** AM4: the protocol versions this recorder writes (legacy era only). */\nexport const RECORDABLE_VERSIONS = new Set([\"2025-03-26\", \"2025-06-18\", \"2025-11-25\"]);\n/** MR2 (d): default bytes of one recordable response that are buffered. */\nexport const DEFAULT_RECORD_BUFFER_BYTES = 64 * 1024 * 1024;\n/** MR2 (d): the ceiling `maxRecordBufferBytes` is clamped to. */\nexport const MAX_RECORD_BUFFER_BYTES = 256 * 1024 * 1024;\n\n/** The MR14 body of a 502 answered for an upstream auth failure. */\nconst AUTH_FAILURE_ERROR =\n \"aimock MCP recording got an upstream auth failure; OAuth discovery is not supported through the recorder; record with a pre-acquired token via upstreamAuth or the client's Authorization header\";\n\n/** Hop-by-hop response headers that are never relayed. */\nconst HOP_BY_HOP = new Set([\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"te\",\n \"trailer\",\n \"upgrade\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n]);\n\n/** The identity query parameters aimock reads and never forwards (MR2 c). */\nconst IDENTITY_QUERY = new Set([\"testId\", \"context\", \"undeclared\"]);\n\n/** MR7: `_meta` keys dropped from a recorded result. */\nconst DROPPED_META = [\"traceparent\", \"tracestate\", \"baggage\", \"io.modelcontextprotocol/serverInfo\"];\n\nconst SERVER_INFO_META = \"io.modelcontextprotocol/serverInfo\";\nconst RELATED_TASK_META = \"io.modelcontextprotocol/related-task\";\nconst PROTOCOL_VERSION_META = \"io.modelcontextprotocol/protocolVersion\";\n/** The version assumed when nothing names one (MR4 source 4). */\nconst FALLBACK_VERSION = \"2025-03-26\";\n\n/** A header token (RFC 9110 `token`). */\nconst HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\n\n/** What the recorder needs from its `MCPMock`. */\nexport interface RecorderHost {\n /** MR4 claims, MR12 registration. */\n readonly fakes: McpFakeStore;\n addMcpFakes(blocks: McpFakeSource[], origin: McpFakeAddOriginInternal): McpFakeAddResult;\n journalEntry(entry: Omit<JournalEntry, \"id\" | \"timestamp\">): void;\n logger(): Logger | null;\n replaySpeed(): number;\n /** Reuses the mount's metric and log side channels for a fake outcome. */\n onFakeEvent(evt: McpFakeEvent): void;\n}\n\n/** A parsed `Name: value` upstream credential header (MR14, AM6). */\nexport interface UpstreamAuthHeader {\n name: string;\n value: string;\n}\n\n/**\n * MR14 / AM6: parse a `Name: value` header, from `upstreamAuth` or\n * `AIMOCK_MCP_UPSTREAM_AUTH`. `undefined` or an empty string is no header. A\n * malformed value is a start error that names `source`, never the value.\n */\nexport function parseUpstreamAuth(\n raw: string | undefined,\n source = \"AIMOCK_MCP_UPSTREAM_AUTH\",\n): UpstreamAuthHeader | undefined {\n if (raw === undefined || raw === \"\") return undefined;\n const colon = raw.indexOf(\":\");\n const name = colon === -1 ? \"\" : raw.slice(0, colon).trim();\n const value = colon === -1 ? \"\" : raw.slice(colon + 1).trim();\n if (!HEADER_NAME.test(name) || value === \"\") {\n throw new Error(`${source} must be \"Name: value\" (a header name, a colon, then a value)`);\n }\n return { name, value };\n}\n\n/** MR3 / AM9: what an upstream `initialize` bound to its session. Private to the recorder. */\ninterface RecordSession {\n protocolVersion: string | null;\n /** AM4: false when the negotiated version is not recordable. */\n recordable: boolean;\n serverInfo?: Record<string, unknown>;\n clientCapabilities?: unknown;\n clientInfo?: unknown;\n testId?: string;\n context?: string;\n undeclared?: McpFakeUndeclaredPolicy;\n}\n\n/** MR16: a `tools/list` being collected page by page. */\ninterface PendingList {\n tools: unknown[];\n /** The `nextCursor` the next page must be requested with. */\n next: string;\n testId: string | null;\n}\n\n/** What is recorded from one request's response. */\ntype RecordKind = \"initialize\" | \"tools/list\" | \"tools/call\";\n\n/** One JSON-RPC message of an upstream response, with its arrival (ms after the request arrived). */\ninterface TimedMessage {\n at: number;\n message: Record<string, unknown>;\n}\n\n/** The state of one handled request, for its journal entry. */\ninterface Exchange {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n mount: string;\n subPath: string;\n arrivedAt: number;\n identity: McpFakeIdentity | null;\n /** The resolution's supplied flags (I3: only supplied values are bound at initialize). */\n supplied: { testId: boolean; context: boolean; undeclared: boolean };\n /** The JSON-RPC request (`null` for a batch, an unparsable body, GET or DELETE). */\n message: Record<string, unknown> | null;\n rpcMethod: string | null;\n /** The JSON-RPC id of the request, when it is a request with a string or number id. */\n rpcId: string | number | undefined;\n /** The request's `params`, when it has an object one. */\n params: Record<string, unknown> | undefined;\n recordSkipped?: string;\n mcpFake?: JournalEntry[\"response\"][\"mcpFake\"];\n proxied: boolean;\n /** X-AIMock-Record-Error text, when the response is not yet sent. */\n recordError?: string;\n}\n\n/**\n * The MCP recorder of one `MCPMock`. `handle` always answers the request:\n * by a fake (MR4), a fake error (MR5), or by forwarding it (MR2).\n */\nexport class McpRecorder {\n private readonly target: URL;\n private readonly auth: UpstreamAuthHeader | undefined;\n private readonly maxBuffer: number;\n private readonly fixturePath: string;\n private readonly secretValues: readonly string[];\n private readonly fallbackLogger = new Logger(\"warn\");\n /** MR3: upstream `Mcp-Session-Id` → what its `initialize` bound. */\n private readonly sessions = new Map<string, RecordSession>();\n /** MR16: pending lists per `${mount}\\0${scope}`. */\n private readonly pendingLists = new Map<string, PendingList>();\n /** MR16: completed lists waiting for their block's first call entry. */\n private readonly completedLists = new Map<string, { tools: unknown[]; testId: string | null }>();\n /** MR13: resolved path → sha256 of the last write. */\n private readonly hashes = new Map<string, string>();\n private readonly listeners = new Set<(filepath: string, mcpFakes: unknown) => void>();\n /** RL3: mounts already warned about a request with no scope. */\n private readonly warnedNoScope = new Set<string>();\n\n constructor(\n private readonly host: RecorderHost,\n private readonly config: McpRecordConfig,\n ) {\n let target: URL;\n try {\n target = new URL(config.upstream);\n } catch {\n // Never echo the value: it can carry credentials (S2 c).\n throw new Error(\"MCP record upstream is not a valid URL\");\n }\n if (target.protocol !== \"http:\" && target.protocol !== \"https:\") {\n throw new Error(\"MCP record upstream must be an http: or https: URL\");\n }\n this.target = target;\n this.secretValues = config.secretValues ?? [];\n validateSecretValues(this.secretValues);\n this.auth = parseUpstreamAuth(config.upstreamAuth, \"upstreamAuth\");\n const max = config.maxRecordBufferBytes;\n this.maxBuffer =\n typeof max === \"number\" && Number.isFinite(max) && max > 0\n ? Math.min(Math.floor(max), MAX_RECORD_BUFFER_BYTES)\n : DEFAULT_RECORD_BUFFER_BYTES;\n this.fixturePath = config.fixturePath ?? \"./fixtures/recorded\";\n if (!config.proxyOnly) checkMcpRecordDestination(this.fixturePath);\n }\n\n /** AM1 entry point. Reads the body itself. Always handles the request. */\n async handle(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n mount: string,\n subPath: string,\n ): Promise<void> {\n const x: Exchange = {\n req,\n res,\n mount,\n subPath,\n arrivedAt: performance.now(),\n identity: null,\n supplied: { testId: false, context: false, undeclared: false },\n message: null,\n rpcMethod: null,\n rpcId: undefined,\n params: undefined,\n proxied: false,\n };\n try {\n const body = await readBody(req);\n const sessionId = headerOf(req, \"mcp-session-id\");\n const session = sessionId ? this.sessions.get(sessionId) : undefined;\n const resolution = resolveMcpIdentity(req, session);\n if (!resolution.ok) {\n res.writeHead(resolution.error.status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: resolution.error.message, code: resolution.error.code }));\n return;\n }\n x.identity = resolution.identity;\n x.supplied = resolution.supplied;\n\n let parsed: unknown;\n let parsedOk = false;\n if (body !== \"\") {\n try {\n parsed = JSON.parse(body);\n parsedOk = true;\n } catch {\n /* forwarded as is */\n }\n }\n if (parsedOk && Array.isArray(parsed)) {\n this.skip(x, \"batch\", \"batch\");\n await this.forward(x, body, null);\n return;\n }\n if (parsedOk && isObject(parsed) && typeof parsed.method === \"string\") {\n x.rpcMethod = parsed.method;\n const isRequest = \"id\" in parsed;\n if (typeof parsed.id === \"string\" || typeof parsed.id === \"number\") x.rpcId = parsed.id;\n if (isObject(parsed.params)) x.params = parsed.params;\n if (parsed.method === \"tools/call\") x.message = parsed;\n if (parsed.method === \"initialize\" && isRequest) {\n await this.forward(x, body, \"initialize\");\n return;\n }\n if (parsed.method === \"tools/list\" && isRequest) {\n await this.forward(x, body, \"tools/list\");\n return;\n }\n if (parsed.method === \"tools/call\" && isRequest) {\n if (await this.answerByFake(x, parsed)) return;\n this.logForwardedCall(x);\n await this.forward(x, body, \"tools/call\");\n return;\n }\n // MR8: every other request is forwarded and not recorded. A\n // notification has no response to record, so it is not reported.\n if (isRequest) this.skip(x, \"not-tools\", parsed.method);\n await this.forward(x, body, null);\n return;\n }\n if (body !== \"\" && req.method === \"POST\") this.skip(x, \"not-tools\", \"(no method)\");\n await this.forward(x, body, null);\n } catch (err) {\n const thrown = err instanceof Error ? err.message : String(err);\n this.log().error(\n build(msg`MCP-RECORD: request error on mount ${plainText(mount)}: ${plainText(thrown)}`),\n );\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n } else if (!res.writableEnded) {\n res.end();\n }\n } finally {\n this.journal(x);\n }\n }\n\n /** MR3 / MR16 reset doors (fakes:R1-R5, R8): one test id, or everything. */\n reset(testId?: string): void {\n for (const [id, session] of this.sessions) {\n if (testId === undefined || session.testId === testId) this.sessions.delete(id);\n }\n for (const lists of [this.pendingLists, this.completedLists]) {\n for (const [key, list] of lists) {\n if (testId !== undefined && list.testId !== testId) continue;\n lists.delete(key);\n this.log().warn(\n build(\n msg`MCP-RECORD: forwarded, not recorded (list-incomplete): tools/list on mount ${plainText(mountOfKey(key))}`,\n ),\n );\n }\n }\n }\n\n /** MR13: the sha256 of the last write to `filepath`, if this recorder wrote it. */\n lastWrittenHash(filepath: string): string | undefined {\n return this.hashes.get(path.resolve(filepath));\n }\n\n /** MR13: called after each write with the resolved path and the `mcpFakes` written. */\n onWrite(listener: (filepath: string, mcpFakes: unknown) => void): () => void {\n this.listeners.add(listener);\n return () => void this.listeners.delete(listener);\n }\n\n /** @internal MR3: whether the record-session map holds `sessionId` (tests). */\n hasRecordSession(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n /** MR16: drop pending lists (RL2 list-incomplete) and clear the session map. */\n close(): void {\n this.reset();\n this.listeners.clear();\n }\n\n // ---- MR4 / MR5 ----\n\n /**\n * MR4 / MR5 for one `tools/call` request: answer it by a fake, or by a fake\n * error (deny, strict, eviction), and return true; false to forward it.\n */\n private async answerByFake(x: Exchange, message: Record<string, unknown>): Promise<boolean> {\n const params = message.params;\n const name = isObject(params) ? params.name : undefined;\n const id = message.id;\n if (\n typeof name !== \"string\" ||\n name === \"\" ||\n (typeof id !== \"string\" && typeof id !== \"number\")\n ) {\n return false;\n }\n const identity = x.identity!;\n const args = isObject(params) ? params.arguments : undefined;\n const event = { identity, tool: name, mount: x.mount, args };\n let claim: McpFakeClaim;\n try {\n claim = this.host.fakes.claim(name, args, identity);\n } catch {\n // A claim that throws is an aimock defect: forward, never guess.\n return false;\n }\n if (claim.kind === \"answer\") {\n x.mcpFake = { id: claim.entry.id, outcome: \"answered\" };\n this.host.onFakeEvent({ ...event, kind: \"answered\", entryId: claim.entry.id });\n await writeFakeToolAnswer(\n x.req,\n x.res,\n id,\n claim.result,\n this.host.fakes.replayOf(claim.entry.id),\n params,\n this.host.replaySpeed(),\n null,\n x.arrivedAt,\n );\n return true;\n }\n const strict = resolveRequestDecisions(this.config.strict, x.req.headers).mcpRecordingStrict;\n const ctx = { identity, mount: x.mount, notifications: [], calls: [] };\n let failure: ReturnType<typeof fakeFailure> | null = null;\n if (claim.kind === \"none\") {\n if (this.host.fakes.policy(identity) === \"deny\" || strict) {\n failure = fakeFailure({ kind: \"not_declared\" }, name, ctx, () =>\n this.host.fakes.declaredTools(identity),\n );\n }\n } else if (claim.kind === \"evicted\" || strict) {\n failure = fakeFailure(claim, name, ctx, () => []);\n }\n if (!failure) return false;\n x.mcpFake = { id: null, outcome: failure.outcome };\n this.host.onFakeEvent(\n failure.outcome === \"evicted\"\n ? {\n ...event,\n kind: \"evicted\",\n code: failure.code,\n message: failure.message,\n ...failure.evicted,\n }\n : { ...event, kind: failure.outcome, code: failure.code, message: failure.message },\n );\n x.res.writeHead(200, { \"Content-Type\": \"application/json\" });\n x.res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n error: {\n code: MCP_FAKE_ERROR_CODES[failure.code],\n message: failure.message,\n data: { aimock: failure.aimock },\n },\n }),\n );\n return true;\n }\n\n /** RP2: a forwarded `tools/call` is logged as unfaked, answered by the upstream. */\n private logForwardedCall(x: Exchange): void {\n const name = x.params?.name;\n if (typeof name !== \"string\" || x.identity === null) return;\n this.host.fakes.logEvent(x.identity, {\n outcome: \"unfaked\",\n answeredBy: \"upstream\",\n tool: name,\n args: x.params?.arguments,\n context: x.identity.context,\n mount: x.mount,\n });\n }\n\n // ---- MR2: forwarding ----\n\n /** Forward one request to the upstream and relay its answer; record it when `kind` says so. */\n private forward(x: Exchange, body: string, kind: RecordKind | null): Promise<void> {\n const { req, res } = x;\n x.proxied = true;\n if (isAuthenticatedRequest(req) && !this.auth) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No configured upstream credential\" }));\n return Promise.resolve();\n }\n const headers = buildForwardHeaders(req);\n removeForwardHeader(headers, \"x-aimock-mcp-undeclared\");\n if (this.auth) {\n removeForwardHeader(headers, this.auth.name);\n headers[this.auth.name] = this.auth.value;\n }\n if (body !== \"\") headers[\"content-length\"] = String(Buffer.byteLength(body));\n const url = this.upstreamUrl(req, x.subPath);\n const transport = url.protocol === \"https:\" ? https : http;\n\n return new Promise<void>((resolve) => {\n let done = false;\n const finish = (): void => {\n if (done) return;\n done = true;\n resolve();\n };\n const upstreamReq = transport.request(url, { method: req.method ?? \"POST\", headers });\n // MR2: a client that goes away aborts the upstream request.\n res.on(\"close\", () => {\n if (!upstreamReq.destroyed) upstreamReq.destroy();\n finish();\n });\n upstreamReq.on(\"error\", (err) => {\n if (done) return;\n this.log().error(\n build(\n msg`MCP-RECORD: upstream request failed for ${plainText(x.rpcMethod ?? req.method ?? \"?\")} on mount ${plainText(x.mount)}: ${plainText(err.message)}`,\n ),\n );\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"aimock MCP recorder could not reach the upstream\" }));\n } else if (!res.writableEnded) {\n res.end();\n }\n finish();\n });\n upstreamReq.on(\"response\", (up) => {\n this.relay(x, up, kind, finish);\n });\n upstreamReq.end(body === \"\" ? undefined : body);\n });\n }\n\n /** The upstream URL of a request: the endpoint plus the sub-path, the query minus identity params. */\n private upstreamUrl(req: http.IncomingMessage, subPath: string): URL {\n const url = new URL(this.target.href);\n if (subPath !== \"\" && subPath !== \"/\") {\n url.pathname = url.pathname.replace(/\\/+$/, \"\") + subPath;\n }\n // MR2 (c): the client's query pairs are forwarded byte for byte (never\n // re-serialized, which would change their encoding), minus the identity\n // parameters aimock reads.\n const raw = (req.url ?? \"\").split(\"#\")[0];\n const q = raw.indexOf(\"?\");\n const kept =\n q === -1\n ? []\n : raw\n .slice(q + 1)\n .split(\"&\")\n .filter((pair) => pair !== \"\" && !IDENTITY_QUERY.has(queryName(pair)));\n if (kept.length > 0) {\n const own = url.search.replace(/^\\?/, \"\");\n url.search = [own, ...kept].filter((part) => part !== \"\").join(\"&\");\n }\n return url;\n }\n\n /** MR2 (d), MR14: relay one upstream response, buffering a recordable one. */\n private relay(\n x: Exchange,\n up: http.IncomingMessage,\n kind: RecordKind | null,\n finish: () => void,\n ): void {\n const { req, res } = x;\n const status = up.statusCode ?? 502;\n const sessionId = headerOf(req, \"mcp-session-id\");\n const rpcId = x.rpcId;\n const UPSTREAM_ABORTED_ERROR = \"aimock MCP recorder: the upstream response failed\";\n // Set by the recordable paths below; true once the call was recorded or skipped.\n let processed = false;\n // A client that goes away makes forward() destroy the upstream request,\n // which errors `up`: that is not an upstream failure.\n let clientGone = false;\n res.on(\"close\", () => {\n if (!res.writableFinished) clientGone = true;\n });\n // Set by the SSE path: a JSON-RPC error for the open request, so a client\n // waiting on the stream fails at once instead of at its own timeout.\n let sseError: (() => string) | null = null;\n // An upstream that dies mid-body is never relayed as a complete answer, on\n // any path: a 502 before headers, a terminated stream after them.\n up.on(\"error\", (err: Error) => {\n // A client that left, or an answer already complete (MR14), has nothing to fail.\n if (clientGone || res.writableEnded) {\n finish();\n return;\n }\n // An SSE call recorded before the stream died keeps its recording; one\n // whose response was already relayed gets no second answer.\n const answered = processed;\n const recorded = answered && x.recordSkipped === undefined && x.recordError === undefined;\n processed = true;\n if (!recorded) x.recordSkipped = \"upstream-aborted\";\n this.log().error(\n build(\n msg`MCP-RECORD: the upstream response failed mid-body (${fixed(\"upstream-aborted\")}) for ${plainText(x.rpcMethod ?? req.method ?? \"?\")} on mount ${plainText(x.mount)}: ${plainText(err.message)}`,\n ),\n );\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: UPSTREAM_ABORTED_ERROR }));\n finish();\n return;\n }\n if (!answered && sseError !== null && !res.writableEnded) {\n res.write(sseError(), () => res.destroy());\n } else {\n res.destroy();\n }\n finish();\n });\n\n // MR14: a 401, or a 403 with insufficient_scope, is never relayed.\n const challenge = String(up.headers[\"www-authenticate\"] ?? \"\");\n if (status === 401 || (status === 403 && /insufficient_scope/i.test(challenge))) {\n up.resume();\n x.recordSkipped = \"upstream-auth\";\n this.log().error(\n build(\n msg`MCP-RECORD: recording refused (upstream-auth): the upstream answered ${status} for ${plainText(x.rpcMethod ?? req.method ?? \"?\")} on mount ${plainText(x.mount)}`,\n ),\n );\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: AUTH_FAILURE_ERROR, upstreamStatus: status }));\n finish();\n return;\n }\n // MR3: the session map entry goes with an upstream 404 or a deleted session.\n if (\n sessionId &&\n (status === 404 || (req.method === \"DELETE\" && status >= 200 && status < 300))\n ) {\n this.sessions.delete(sessionId);\n }\n\n const headers: Record<string, string | string[]> = {};\n for (const [name, value] of Object.entries(up.headers)) {\n if (value === undefined || HOP_BY_HOP.has(name.toLowerCase())) continue;\n headers[name] = value;\n }\n const ok = status >= 200 && status < 300;\n if (kind === \"tools/call\" && !ok) this.skip(x, \"upstream-status\", \"tools/call\");\n if (kind === \"tools/list\" && !ok) this.skip(x, \"upstream-status\", \"tools/list\");\n\n if (kind === null || !ok) {\n res.writeHead(status, headers);\n up.pipe(res);\n up.on(\"end\", finish);\n return;\n }\n\n const isSse = String(up.headers[\"content-type\"] ?? \"\").includes(\"text/event-stream\");\n const messages: TimedMessage[] = [];\n let size = 0;\n let overflow = false;\n const process = (): void => {\n if (processed) return;\n processed = true;\n if (overflow) {\n this.skip(x, \"buffer-cap\", kind);\n return;\n }\n // This runs in a stream event handler, outside handle()'s try: a throw\n // here must never reach the process (G2b A1).\n try {\n this.onRecordable(x, kind, messages, up);\n } catch (err) {\n // RL1 never carries values: an unexpected error is named, not quoted.\n this.fail(x, \"record-error\", err instanceof Error ? err.name : typeof err);\n }\n };\n\n if (isSse) {\n res.writeHead(status, headers);\n res.flushHeaders();\n const decoder = new StringDecoder(\"utf8\");\n let pending = \"\";\n if (rpcId !== undefined) {\n const error = { code: -32603, message: UPSTREAM_ABORTED_ERROR };\n sseError = () => {\n // A half-sent upstream event is closed under a type clients ignore,\n // so it never merges into the error event.\n const close =\n pending === \"\"\n ? \"\"\n : `${/[\\r\\n]$/.test(pending) ? \"\" : \"\\n\"}event: aimock-discarded\\n\\n`;\n return `${close}event: message\\ndata: ${JSON.stringify({ jsonrpc: \"2.0\", id: rpcId, error })}\\n\\n`;\n };\n }\n up.on(\"data\", (chunk: Buffer) => {\n if (!processed && !overflow) {\n size += chunk.length;\n if (size > this.maxBuffer) {\n overflow = true;\n process();\n } else {\n pending += decoder.write(chunk);\n const events = pending.split(/\\r?\\n\\r?\\n/);\n pending = events.pop() ?? \"\";\n for (const event of events) {\n for (const message of sseMessages(event)) {\n messages.push({ at: performance.now() - x.arrivedAt, message });\n }\n }\n // The response is recorded before the client sees it, so a\n // client that reads the file right after its call sees the write.\n if (messages.some((m) => isResponseTo(m.message, rpcId))) process();\n }\n }\n res.write(chunk);\n });\n up.on(\"end\", () => {\n if (!processed && !overflow) {\n for (const message of sseMessages(pending + decoder.end())) {\n messages.push({ at: performance.now() - x.arrivedAt, message });\n }\n }\n process();\n res.end();\n finish();\n });\n return;\n }\n\n // JSON: buffered whole, so a record error can still be sent as a header.\n const chunks: Buffer[] = [];\n up.on(\"data\", (chunk: Buffer) => {\n if (overflow) {\n res.write(chunk);\n return;\n }\n size += chunk.length;\n if (size > this.maxBuffer) {\n overflow = true;\n process();\n res.writeHead(status, headers);\n for (const c of chunks) res.write(c);\n res.write(chunk);\n chunks.length = 0;\n return;\n }\n chunks.push(chunk);\n });\n up.on(\"end\", () => {\n if (!overflow) {\n const text = Buffer.concat(chunks).toString(\"utf8\");\n try {\n const parsed: unknown = JSON.parse(text);\n for (const message of Array.isArray(parsed) ? parsed : [parsed]) {\n if (isObject(message)) messages.push({ at: performance.now() - x.arrivedAt, message });\n }\n } catch {\n /* not JSON: nothing to record */\n }\n process();\n if (x.recordError !== undefined) {\n headers[\"X-AIMock-Record-Error\"] = sanitizeHeaderValue(x.recordError);\n }\n res.writeHead(status, headers);\n res.end(text === \"\" ? undefined : Buffer.concat(chunks));\n } else {\n res.end();\n }\n finish();\n });\n }\n\n // ---- MR3, MR6-MR10, MR16 ----\n\n /** A recordable response, once its JSON-RPC response arrived (or its stream ended). */\n private onRecordable(\n x: Exchange,\n kind: RecordKind,\n messages: TimedMessage[],\n up: http.IncomingMessage,\n ): void {\n const response = messages.find((m) => isResponseTo(m.message, x.rpcId));\n if (kind === \"initialize\") {\n this.onInitialize(x, response?.message, up);\n return;\n }\n if (!response) {\n this.skip(x, \"upstream-error\", kind);\n return;\n }\n const reason =\n kind === \"tools/call\"\n ? callSkipReason(response.message, messages)\n : isObject(response.message.result)\n ? null\n : \"upstream-error\";\n if (reason !== null) {\n this.skip(x, reason, kind);\n return;\n }\n if (!this.versionRecordable(x)) {\n this.skip(x, \"unsupported-version\", kind);\n return;\n }\n const identity = x.identity!;\n if (identity.testId === null && identity.context === null) {\n // MR9 / RL3: forwarded, not written, warned once per mount.\n x.recordSkipped = \"no-scope\";\n if (!this.warnedNoScope.has(x.mount)) {\n this.warnedNoScope.add(x.mount);\n this.log().warn(\n build(\n msg`MCP-RECORD: forwarded, not recorded: a request on mount ${plainText(x.mount)} has no testId or context, so it has no recording scope (logged once per mount)`,\n ),\n );\n }\n return;\n }\n if (this.config.proxyOnly) return;\n if (kind === \"tools/list\") this.onToolsList(x, response.message);\n else this.recordCall(x, response, messages);\n }\n\n /** MR3 / AM4: bind what an upstream `initialize` negotiated to its session. */\n private onInitialize(\n x: Exchange,\n response: Record<string, unknown> | undefined,\n up: http.IncomingMessage,\n ): void {\n const result = response?.result;\n if (!isObject(result)) return;\n const version = typeof result.protocolVersion === \"string\" ? result.protocolVersion : null;\n const recordable = version !== null && RECORDABLE_VERSIONS.has(version);\n if (!recordable) this.skip(x, \"unsupported-version\", \"initialize\");\n const sessionId = headerOf(up, \"mcp-session-id\");\n if (!sessionId) return;\n const identity = x.identity!;\n this.sessions.set(sessionId, {\n protocolVersion: version,\n recordable,\n ...(isObject(result.serverInfo) ? { serverInfo: result.serverInfo } : {}),\n clientCapabilities: x.params?.capabilities,\n clientInfo: x.params?.clientInfo,\n // I3: only the values the initialize request itself supplied are bound.\n ...(x.supplied.testId && identity.testId !== null ? { testId: identity.testId } : {}),\n ...(x.supplied.context && identity.context !== null ? { context: identity.context } : {}),\n ...(x.supplied.undeclared && identity.undeclared !== null\n ? { undeclared: identity.undeclared }\n : {}),\n });\n }\n\n /** AM4: false when the session negotiated, or the request names, a version not recorded. */\n private versionRecordable(x: Exchange): boolean {\n const sessionId = headerOf(x.req, \"mcp-session-id\");\n const session = sessionId ? this.sessions.get(sessionId) : undefined;\n if (session && !session.recordable) return false;\n const header = headerOf(x.req, \"mcp-protocol-version\");\n return header === undefined || RECORDABLE_VERSIONS.has(header);\n }\n\n /** MR16: collect a `tools/list` page; write the list when it completes. */\n private onToolsList(x: Exchange, response: Record<string, unknown>): void {\n const result = response.result as Record<string, unknown>;\n if (!Array.isArray(result.tools)) {\n this.skip(x, \"upstream-error\", \"tools/list\");\n return;\n }\n const identity = x.identity!;\n const key = listKey(x.mount, identity);\n const cursor = x.params?.cursor;\n const next = typeof result.nextCursor === \"string\" ? result.nextCursor : undefined;\n let tools: unknown[];\n if (cursor === undefined) {\n if (this.pendingLists.has(key)) {\n this.log().warn(\n build(\n msg`MCP-RECORD: forwarded, not recorded (list-incomplete): tools/list on mount ${plainText(x.mount)}`,\n ),\n );\n }\n tools = [...result.tools];\n } else {\n const pending = this.pendingLists.get(key);\n if (!pending || pending.next !== cursor) {\n this.skip(x, \"list-orphan-page\", \"tools/list\");\n return;\n }\n tools = [...pending.tools, ...result.tools];\n }\n this.pendingLists.delete(key);\n if (next !== undefined) {\n this.pendingLists.set(key, { tools, next, testId: identity.testId });\n return;\n }\n const filepath = this.targetFile(identity);\n const scope = scopeOf(identity);\n if (!fileHasBlock(filepath, scope, x.mount)) {\n // A block needs a call entry; the list waits for the block's first one.\n this.completedLists.set(key, { tools, testId: identity.testId });\n return;\n }\n let list: { value: unknown[]; warnings: string[] };\n try {\n list = sanitizeRecording(tools, this.secrets(x), isMetaBranch);\n } catch (err) {\n this.failUnsafe(x, err);\n return;\n }\n this.persist(x, filepath, scope, (block) => ({\n block: withList(block ?? {}, list.value),\n warnings: (prefix) => list.warnings.map((w) => `${prefix}/list${w}`),\n }));\n }\n\n /** MR6 / MR7 / MR10-MR12: record one forwarded `tools/call`. */\n private recordCall(x: Exchange, response: TimedMessage, messages: TimedMessage[]): void {\n const identity = x.identity!;\n const name = String(x.params?.name ?? \"\");\n const result = cleanResult(response.message.result as Record<string, unknown>);\n const entry: Record<string, unknown> = { args: x.params?.arguments ?? {} };\n const errorText = errorTextOf(result);\n if (errorText !== null) entry.error = errorText;\n else entry.result = result;\n const notifications = messages\n .filter(\n (m) =>\n m !== response &&\n m.at <= response.at &&\n !(\"id\" in m.message) &&\n typeof m.message.method === \"string\" &&\n m.message.method.startsWith(\"notifications/\"),\n )\n .map((m) => ({\n atMs: Math.round(m.at),\n method: m.message.method as string,\n params: isObject(m.message.params) ? m.message.params : {},\n }));\n if (notifications.length > 0) entry.notifications = notifications;\n entry.durationMs = Math.round(response.at);\n\n const filepath = this.targetFile(identity);\n const scope = scopeOf(identity);\n const key = listKey(x.mount, identity);\n const waitingList = this.completedLists.get(key);\n const secrets = this.secrets(x);\n let clean: { value: Record<string, unknown>; warnings: string[] };\n let recorded: { value: Record<string, unknown>; warnings: string[] };\n let list: { value: unknown[]; warnings: string[] } | null = null;\n try {\n clean = sanitizeRecording(entry, secrets, isMetaBranch);\n recorded = sanitizeRecording(this.provenance(x, response.message), secrets, () => true);\n if (waitingList) list = sanitizeRecording(waitingList.tools, secrets, isMetaBranch);\n } catch (err) {\n this.failUnsafe(x, err);\n return;\n }\n const written = this.persist(x, filepath, scope, (block) => {\n if (!block) {\n return {\n block: {\n scope,\n mount: x.mount,\n ...(list ? { list: list.value } : {}),\n recorded: recorded.value,\n tools: [{ name, calls: [clean.value] }],\n },\n warnings: (prefix) => [\n ...(list ? list.warnings.map((w) => `${prefix}/list${w}`) : []),\n ...recorded.warnings.map((w) => `${prefix}/recorded${w}`),\n ...clean.warnings.map((w) => `${prefix}/tools/0/calls/0${w}`),\n ],\n };\n }\n const tools = Array.isArray(block.tools) ? [...(block.tools as unknown[])] : [];\n let t = tools.findIndex((tool) => isObject(tool) && tool.name === name);\n let c = 0;\n if (t === -1) {\n tools.push({ name, calls: [clean.value] });\n t = tools.length - 1;\n } else {\n const tool = tools[t] as Record<string, unknown>;\n const calls = Array.isArray(tool.calls) ? [...(tool.calls as unknown[])] : [];\n calls.push(clean.value);\n c = calls.length - 1;\n tools[t] = { ...tool, calls };\n }\n // A list that completed before this block appeared (G2b A2) replaces its list.\n const updated = list ? withList({ ...block, tools }, list.value) : { ...block, tools };\n return {\n block: updated,\n warnings: (prefix) => [\n ...(list ? list.warnings.map((w) => `${prefix}/list${w}`) : []),\n ...clean.warnings.map((w) => `${prefix}/tools/${t}/calls/${c}${w}`),\n ],\n };\n });\n if (!written) return;\n if (waitingList) this.completedLists.delete(key);\n\n // MR12: the new entry is served from memory as `record#<n>`, consumed\n // for the recording test id, so an identical call misses and is recorded.\n const before = new Set(this.host.fakes.allEntryIds());\n try {\n this.host.addMcpFakes(\n [\n {\n source: \"record\",\n blockIndex: null,\n raw: { scope, mount: x.mount, tools: [{ name, calls: [clean.value] }] },\n },\n ],\n { kind: \"record\" },\n );\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n this.log().error(\n build(\n msg`MCP-RECORD: the recorded entry was written but not registered on mount ${plainText(x.mount)}: ${plainText(detail)}`,\n ),\n );\n return;\n }\n const added = this.host.fakes.allEntryIds().filter((id) => !before.has(id));\n this.host.fakes.markConsumed(identity.testId, added);\n }\n\n /** FA2 `recorded` for a new block (MR6 3). */\n private provenance(x: Exchange, response: Record<string, unknown>): Record<string, unknown> {\n const sessionId = headerOf(x.req, \"mcp-session-id\");\n const session = sessionId ? this.sessions.get(sessionId) : undefined;\n const meta = isObject(x.params?._meta) ? x.params._meta : undefined;\n const result = isObject(response.result) ? response.result : undefined;\n const resultMeta = isObject(result?._meta) ? result._meta : undefined;\n const protocolVersion =\n session?.protocolVersion ??\n (typeof meta?.[PROTOCOL_VERSION_META] === \"string\"\n ? meta[PROTOCOL_VERSION_META]\n : undefined) ??\n headerOf(x.req, \"mcp-protocol-version\") ??\n FALLBACK_VERSION;\n const serverInfo =\n session?.serverInfo ??\n (isObject(resultMeta?.[SERVER_INFO_META]) ? resultMeta[SERVER_INFO_META] : undefined);\n return {\n upstream: this.target.origin,\n protocolVersion,\n ...(serverInfo ? { serverInfo } : {}),\n aimockVersion: aimockVersion((m) => this.log().warn(m)),\n at: new Date().toISOString(),\n };\n }\n\n /** S2 (a)-(d): the known secrets of one request. */\n private secrets(x: Exchange): string[] {\n // The raw values: `flattenHeaders` masks API-key headers for the\n // journal, which would hide the very values S2 (a) must redact.\n const headers: Record<string, string> = {};\n for (const [name, value] of Object.entries(x.req.headers)) {\n if (value !== undefined) headers[name] = Array.isArray(value) ? value.join(\", \") : value;\n }\n return knownSecrets({\n headers,\n ...(this.auth ? { upstreamAuth: this.auth } : {}),\n // G2b B1: the URL really sent upstream, so a credential in the\n // client's query string (forwarded per MR2 c) is a known secret (S2 c).\n upstreamUrl: this.upstreamUrl(x.req, x.subPath).href,\n secretValues: this.secretValues,\n });\n }\n\n /** S6 / RL1: an unsafe recording is not written. */\n private failUnsafe(x: Exchange, err: unknown): void {\n if (!(err instanceof RecordUnsafeError)) throw err;\n this.fail(x, \"unsafe\", err.reason);\n }\n\n /** RL1: a recording that was not written, with its reason (never a value). */\n private fail(x: Exchange, reason: string, detail: string): void {\n x.recordSkipped = reason;\n x.recordError = `${reason}: ${detail}`;\n this.log().error(\n build(\n msg`MCP-RECORD: recording failed (${fixed(reason)}) for ${plainText(x.rpcMethod ?? \"?\")} on mount ${plainText(x.mount)}: ${plainText(detail)}`,\n ),\n );\n }\n\n /**\n * MR10 / MR11: merge one change into the block of `scope` and this mount\n * (appending a block when none matches), validate the whole file, and write\n * it. `apply` gets that block (`undefined` when it is new) and returns the\n * new block and its sanitizer warnings under the block's pointer.\n */\n private persist(\n x: Exchange,\n filepath: string,\n scope: Record<string, string>,\n apply: (block: Record<string, unknown> | undefined) => {\n block: Record<string, unknown>;\n warnings: (blockPointer: string) => string[];\n },\n ): boolean {\n const result = persistServiceFakes({\n filepath,\n logger: this.log(),\n merge: (existing) => mergeBlock(existing, scope, x.mount, apply),\n validate: (content) => {\n const first = validateMcpFakes(content.mcpFakes, filepath).errors[0];\n if (!first) return null;\n const where = first.entryId ?? first.blockId;\n return where ? `${first.rule} (${where})` : first.rule;\n },\n });\n if (result.kind === \"failed\") {\n this.fail(x, result.stage === \"write\" ? \"write-failed\" : \"invalid-merge\", result.error);\n return false;\n }\n const resolved = path.resolve(result.filepath);\n this.hashes.set(resolved, result.sha256);\n for (const listener of this.listeners) {\n try {\n listener(resolved, result.content.mcpFakes);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n this.log().error(\n build(\n msg`MCP-RECORD: a write listener failed for ${plainText(resolved)} on mount ${plainText(x.mount)}: ${plainText(detail)}`,\n ),\n );\n }\n }\n return true;\n }\n\n /** MR10: the file a recording of `identity` goes to. */\n private targetFile(identity: McpFakeIdentity): string {\n const slug =\n identity.testId !== null\n ? slugifyTestId(identity.testId)\n : identity.context !== null\n ? slugifyContext(identity.context)\n : \"\";\n if (slug) return path.join(this.fixturePath, slug, \"mcp.json\");\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n return path.join(this.fixturePath, `mcp-${timestamp}-${crypto.randomUUID().slice(0, 8)}.json`);\n }\n\n /** MR8 / RL2: mark the request forwarded-not-recorded and warn. */\n private skip(x: Exchange, reason: string, method: string): void {\n x.recordSkipped = reason;\n this.log().warn(\n build(\n msg`MCP-RECORD: forwarded, not recorded (${fixed(reason)}): ${plainText(method)} on mount ${plainText(x.mount)}`,\n ),\n );\n }\n\n private log(): Logger {\n return this.host.logger() ?? this.fallbackLogger;\n }\n\n /** AM8: one journal entry per request. */\n private journal(x: Exchange): void {\n const { req, res } = x;\n this.host.journalEntry({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/\",\n headers: flattenHeaders(req.headers),\n body: x.message,\n service: \"mcp\",\n ...(x.identity\n ? {\n testId: x.identity.testId ?? MCP_FAKES_DEFAULT_TEST_ID,\n context: x.identity.context,\n }\n : {}),\n response: {\n status: res.statusCode,\n fixture: null,\n ...(x.proxied ? { source: \"proxy\" as const } : {}),\n ...(x.recordSkipped !== undefined ? { recordSkipped: x.recordSkipped } : {}),\n ...(x.mcpFake ? { mcpFake: x.mcpFake } : {}),\n },\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** One header's value (the first, when repeated), or undefined. */\nfunction headerOf(req: http.IncomingMessage, name: string): string | undefined {\n const value = req.headers[name];\n return Array.isArray(value) ? value[0] : value;\n}\n\n/** The JSON-RPC messages of one SSE event (its `data:` lines). */\nfunction sseMessages(event: string): Record<string, unknown>[] {\n const data = event\n .split(/\\r?\\n/)\n .filter((line) => line.startsWith(\"data:\"))\n .map((line) => line.slice(5).replace(/^ /, \"\"))\n .join(\"\\n\");\n if (data === \"\") return [];\n try {\n const parsed: unknown = JSON.parse(data);\n return (Array.isArray(parsed) ? parsed : [parsed]).filter(isObject);\n } catch {\n return [];\n }\n}\n\n/** Whether `message` is the JSON-RPC response to request `id`. */\nfunction isResponseTo(message: Record<string, unknown>, id: string | number | undefined): boolean {\n return id !== undefined && message.id === id && (\"result\" in message || \"error\" in message);\n}\n\n/** The decoded name of one raw `name=value` query pair (form rules: `+` is a space). */\nfunction queryName(pair: string): string {\n const eq = pair.indexOf(\"=\");\n const name = (eq === -1 ? pair : pair.slice(0, eq)).replace(/\\+/g, \" \");\n try {\n return decodeURIComponent(name);\n } catch {\n return name;\n }\n}\n\nfunction mountOfKey(key: string): string {\n return key.slice(0, key.indexOf(\"\\u0000\"));\n}\n\n/** MR8: why a forwarded `tools/call` response is not recorded, or null. */\nfunction callSkipReason(\n response: Record<string, unknown>,\n messages: TimedMessage[],\n): string | null {\n if (\"error\" in response) return \"upstream-error\";\n const result = response.result;\n if (!isObject(result)) return \"upstream-error\";\n if (result.resultType === \"input_required\") return \"input-required\";\n if (result.task !== undefined || (isObject(result._meta) && RELATED_TASK_META in result._meta)) {\n return \"task-handle\";\n }\n if (messages.some((m) => typeof m.message.method === \"string\" && \"id\" in m.message)) {\n return \"server-request\";\n }\n return null;\n}\n\n/** MR7: a recorded result without trace `_meta` keys, serverInfo and `resultType: \"complete\"`. */\nfunction cleanResult(result: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = { ...result };\n if (out.resultType === \"complete\") delete out.resultType;\n if (isObject(out._meta)) {\n const meta: Record<string, unknown> = { ...out._meta };\n for (const key of DROPPED_META) delete meta[key];\n if (Object.keys(meta).length > 0) out._meta = meta;\n else delete out._meta;\n }\n return out;\n}\n\n/** MR6: the `error` text of an `isError` result that is exactly one text block, else null. */\nfunction errorTextOf(result: Record<string, unknown>): string | null {\n if (result.isError !== true) return null;\n if (!Object.keys(result).every((k) => k === \"content\" || k === \"isError\")) return null;\n const content = result.content;\n if (!Array.isArray(content) || content.length !== 1) return null;\n const block: unknown = content[0];\n if (!isObject(block) || block.type !== \"text\" || typeof block.text !== \"string\") return null;\n return Object.keys(block).length === 2 ? block.text : null;\n}\n\n/** S4: credential-shaped keys are removed only inside `_meta`. */\nfunction isMetaBranch(pointer: string): boolean {\n return /(?:^|\\/)_meta(?:\\/|$)/.test(pointer);\n}\n\n/** MR10 block scope: the test id, plus the context when there is one. */\nfunction scopeOf(identity: McpFakeIdentity): Record<string, string> {\n if (identity.testId === null) return { context: identity.context ?? \"\" };\n return identity.context === null\n ? { testId: identity.testId }\n : { testId: identity.testId, context: identity.context };\n}\n\n/** MR10: a block scope exactly equal to `scope`. */\nfunction sameScope(raw: unknown, scope: Record<string, string>): boolean {\n if (!isObject(raw)) return false;\n const keys = Object.keys(scope);\n return Object.keys(raw).length === keys.length && keys.every((k) => raw[k] === scope[k]);\n}\n\n/** MR16 pending-list key: mount and scope. */\nfunction listKey(mount: string, identity: McpFakeIdentity): string {\n return `${mount}\\u0000${JSON.stringify([identity.testId, identity.context])}`;\n}\n\n/** Whether the file at `filepath` already has the block of `scope` and `mount`. */\nfunction fileHasBlock(filepath: string, scope: Record<string, string>, mount: string): boolean {\n let parsed: unknown;\n try {\n parsed = JSON.parse(fs.readFileSync(filepath, \"utf-8\"));\n } catch {\n return false;\n }\n if (!isObject(parsed)) return false;\n const raw = parsed.mcpFakes;\n const blocks: unknown[] = Array.isArray(raw) ? raw : raw === undefined ? [] : [raw];\n return blocks.some((b) => isObject(b) && sameScope(b.scope, scope) && b.mount === mount);\n}\n\n/** MR10 exception: a block with `list` replaced (or inserted after `mount`), key order kept. */\nfunction withList(block: Record<string, unknown>, list: unknown[]): Record<string, unknown> {\n if (\"list\" in block) return { ...block, list };\n const out: Record<string, unknown> = {};\n let placed = false;\n for (const [key, value] of Object.entries(block)) {\n out[key] = value;\n if (key === \"mount\") {\n out.list = list;\n placed = true;\n }\n }\n if (!placed) out.list = list;\n return out;\n}\n\n/**\n * MR10 append rule over a whole file: the first block whose scope and mount\n * are exactly equal is changed by `apply`; otherwise a block is appended.\n * Object form becomes array form only when a second block is needed. Other\n * top-level keys are kept (fakes:F9); `_warnings` gains the new pointers.\n */\nfunction mergeBlock(\n existing: Record<string, unknown> | null,\n scope: Record<string, string>,\n mount: string,\n apply: (block: Record<string, unknown> | undefined) => {\n block: Record<string, unknown>;\n warnings: (blockPointer: string) => string[];\n },\n): Record<string, unknown> {\n const content: Record<string, unknown> = existing ? { ...existing } : {};\n const raw = content.mcpFakes;\n const blocks: unknown[] = Array.isArray(raw) ? [...raw] : raw === undefined ? [] : [raw];\n let index = blocks.findIndex(\n (b) => isObject(b) && sameScope(b.scope, scope) && b.mount === mount,\n );\n const applied = apply(index === -1 ? undefined : (blocks[index] as Record<string, unknown>));\n if (index === -1) {\n blocks.push(applied.block);\n index = blocks.length - 1;\n } else {\n blocks[index] = applied.block;\n }\n const arrayForm = Array.isArray(raw) || blocks.length > 1;\n content.mcpFakes = arrayForm ? blocks : blocks[0];\n let warnings = Array.isArray(content._warnings)\n ? content._warnings.filter((w): w is string => typeof w === \"string\")\n : [];\n if (isObject(raw) && arrayForm) {\n // The file's only block moved from /mcpFakes to /mcpFakes/0.\n warnings = warnings.map((w) =>\n /^\\/mcpFakes(?=[/:])/.test(w) ? `/mcpFakes/0${w.slice(\"/mcpFakes\".length)}` : w,\n );\n }\n const added = applied.warnings(arrayForm ? `/mcpFakes/${index}` : \"/mcpFakes\");\n if (warnings.length > 0 || added.length > 0)\n content._warnings = [...new Set([...warnings, ...added])];\n return content;\n}\n\n// ---- Wiring: the `llmock` flags and `llm.record.mcp` (MR1, AM6) ----\n\n/** One `--mcp-record` / `--mcp-proxy-only` value: `<mount>=<url>`. */\nexport interface McpRecordFlag {\n mount: string;\n upstream: string;\n}\n\n/**\n * A mount key as an error message may show it: JSON-quoted, with any\n * credential (URL userinfo, a credential query parameter, a secret-looking\n * fragment) removed by the record sanitizer's URL rule (S2 c) and marked as\n * removed. A key with an `@` that the URL rule cannot parse is not shown.\n */\nexport function redactMountForError(mount: string): string {\n const absolute = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(mount);\n const prefix = absolute ? \"\" : mount.startsWith(\"/\") ? \"http://localhost\" : \"http://\";\n const scrubbed = scrubUrl(\n prefix + mount,\n () => false,\n () => {\n throw new Error(\"unreachable: no value is a secret here\");\n },\n );\n if (scrubbed !== prefix + mount) {\n const shown = scrubbed.startsWith(prefix) ? scrubbed.slice(prefix.length) : REDACTED;\n return `${JSON.stringify(shown)} (credentials removed)`;\n }\n return mount.includes(\"@\") && !URL.canParse(prefix + mount)\n ? JSON.stringify(REDACTED)\n : JSON.stringify(mount);\n}\n\n/**\n * Why a mount path can never be reached, or `undefined` when it can. The\n * server matches a request's URL pathname against the mount exactly or as a\n * `<mount>/` prefix, so a mount must be a plain, normalized URL path: it\n * starts with one `/`, has no trailing `/` (except the root `/`), and has no\n * query, fragment, whitespace, dot segment or character the URL parser escapes.\n */\nexport function unreachableMountReason(mount: string): string | undefined {\n if (!mount.startsWith(\"/\")) return 'it must start with \"/\"';\n if (mount.startsWith(\"//\")) return 'it must not start with \"//\"';\n if (mount.length > 1 && mount.endsWith(\"/\")) return 'it must not end with \"/\"';\n if (new URL(mount, \"http://localhost\").pathname !== mount) {\n return \"it must be a plain URL path, with no query, fragment, whitespace, dot segment or escaped character\";\n }\n return undefined;\n}\n\n/**\n * Why an upstream URL is unusable, or `undefined` when it is a valid http(s)\n * URL. Never includes the value: it can carry credentials (S2 c).\n */\nfunction badUpstreamReason(upstream: string): string | undefined {\n let url: URL;\n try {\n url = new URL(upstream);\n } catch {\n return \"is not a valid URL\";\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n return \"must be an http: or https: URL\";\n }\n return undefined;\n}\n\n/**\n * F2: fail at start, not at every write, when the recording destination can\n * never be written. The deepest path component that exists must be a\n * directory this process can write to and enter. A path that does not exist\n * yet is created on the first write, but not through a broken symbolic link.\n * `label` prefixes the error (for example the mount it belongs to).\n */\nexport function checkMcpRecordDestination(fixturePath: string, label?: string): void {\n const destination = path.resolve(fixturePath);\n const fail = (problem: string): never => {\n const text = `MCP record destination ${destination} ${problem}`;\n throw new Error(label ? `${label}: ${text}` : text);\n };\n const code = (err: unknown): string => (err as NodeJS.ErrnoException | null)?.code ?? String(err);\n let probe = destination;\n for (;;) {\n let stat: fs.Stats | undefined;\n let broken = false;\n try {\n const entry = fs.lstatSync(probe, { throwIfNoEntry: false });\n stat = entry?.isSymbolicLink() ? fs.statSync(probe, { throwIfNoEntry: false }) : entry;\n broken = entry !== undefined && stat === undefined;\n } catch (err) {\n // ENOTDIR: an ancestor is a file; the walk up finds it.\n if (code(err) !== \"ENOTDIR\") fail(`cannot be checked (${code(err)} at ${probe})`);\n }\n if (broken) fail(`goes through a broken symbolic link at ${probe}`);\n if (stat !== undefined) {\n if (!stat.isDirectory()) fail(\"is not a directory\");\n try {\n fs.accessSync(probe, fs.constants.W_OK | fs.constants.X_OK);\n } catch (err) {\n fail(`is not writable (${code(err)} at ${probe})`);\n }\n return;\n }\n const parent = path.dirname(probe);\n if (parent === probe) return;\n probe = parent;\n }\n}\n\n/**\n * Parse a `--mcp-record` / `--mcp-proxy-only` value at its first `=`. The\n * mount must be a reachable path and the upstream an http(s) URL. A bad value\n * throws an error that names `flag`, never the URL (it can carry credentials).\n */\nexport function parseMcpRecordFlag(value: string, flag = \"--mcp-record\"): McpRecordFlag {\n const eq = value.indexOf(\"=\");\n const mount = eq === -1 ? \"\" : value.slice(0, eq);\n const upstream = eq === -1 ? \"\" : value.slice(eq + 1);\n if (!mount.startsWith(\"/\")) {\n throw new Error(`${flag} must be <mount>=<url>, with a mount that starts with \"/\"`);\n }\n const unreachable = unreachableMountReason(mount);\n if (unreachable !== undefined) {\n throw new Error(\n `${flag} mount ${redactMountForError(mount)} can never be reached: ${unreachable}`,\n );\n }\n const badUpstream = badUpstreamReason(upstream);\n if (badUpstream !== undefined) {\n throw new Error(`${flag} ${mount}: the upstream ${badUpstream}`);\n }\n return { mount, upstream };\n}\n\n/**\n * AM6 / S2 (d): the recorder settings that come from the environment.\n * `AIMOCK_RECORD_SECRET_VALUES` is newline-separated (LF or CRLF); empty\n * entries are dropped and nothing else is trimmed. A malformed\n * `AIMOCK_MCP_UPSTREAM_AUTH` throws an error that names the variable.\n */\nexport function mcpRecordEnv(env: NodeJS.ProcessEnv): {\n secretValues: string[];\n upstreamAuth: string | undefined;\n} {\n const upstreamAuth = env.AIMOCK_MCP_UPSTREAM_AUTH;\n parseUpstreamAuth(upstreamAuth);\n return {\n secretValues: (env.AIMOCK_RECORD_SECRET_VALUES ?? \"\").split(/\\r?\\n/).filter(Boolean),\n upstreamAuth: upstreamAuth === \"\" ? undefined : upstreamAuth,\n };\n}\n\n/** A mount that records MCP: an `MCPMock`. */\nexport interface RecordableMcpMount extends Mountable {\n enableRecording(config: McpRecordConfig): unknown;\n}\n\nexport interface WireMcpRecordingOptions {\n /** What the caller already mounted, by path. */\n mounted: ReadonlyMap<string, Mountable>;\n /**\n * The `MCPMock` class. It is passed in, because mcp-mock.ts imports this\n * module: a mounted instance of it records, and a free path gets a new one.\n */\n mcpMock: new () => RecordableMcpMount;\n /** `llm.fixtures`: the recording destination is `<fixtures>/recorded`. */\n fixtures?: string;\n /** `llm.record.fixturePath`: overrides the destination. */\n fixturePath?: string;\n strict?: boolean;\n env: NodeJS.ProcessEnv;\n}\n\n/**\n * MR1: `llm.record.mcp` — record each mount path. A path the caller mounted\n * with an `MCPMock` records through it; a path held by another kind of mount\n * is a start error; a free path gets a new `MCPMock`, mounted on `llmock`.\n * A string value is `{ upstream }`. `secretValues` and `upstreamAuth` come\n * from the value, else from the environment, as on the `llmock` CLI.\n */\nexport function wireMcpRecording(\n llmock: { mount(path: string, handler: Mountable): unknown },\n record: Record<string, string | McpRecordConfig>,\n opts: WireMcpRecordingOptions,\n): void {\n // F1: the config file is untyped JSON. Before anything is mounted, check\n // every mount's path, settings, upstream URL, holder and destination. The\n // McpRecorder constructor still checks secretValues and upstreamAuth while\n // mounting, so a failure there can leave earlier mounts mounted.\n // Errors name the mount and key, never a value (S2 c).\n const where = \"llm.record.mcp\";\n const plain = (v: unknown): v is Record<string, unknown> =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n const keyTypes: Record<keyof McpRecordConfig, string> = {\n upstream: \"a string\",\n fixturePath: \"a non-empty string\",\n proxyOnly: \"a boolean\",\n maxRecordBufferBytes: `a finite number greater than 0 and at most ${MAX_RECORD_BUFFER_BYTES}`,\n upstreamAuth: \"a string\",\n secretValues: \"an array of strings\",\n strict: \"a boolean\",\n };\n const hasType = (key: keyof McpRecordConfig, v: unknown): boolean => {\n switch (key) {\n case \"proxyOnly\":\n case \"strict\":\n return typeof v === \"boolean\";\n case \"maxRecordBufferBytes\":\n return typeof v === \"number\" && Number.isFinite(v) && v > 0 && v <= MAX_RECORD_BUFFER_BYTES;\n case \"secretValues\":\n return Array.isArray(v) && v.every((s) => typeof s === \"string\");\n case \"fixturePath\":\n return typeof v === \"string\" && v.length > 0;\n default:\n return typeof v === \"string\";\n }\n };\n const isConfigKey = (key: string): key is keyof McpRecordConfig =>\n Object.prototype.hasOwnProperty.call(keyTypes, key);\n const unknownRecord: unknown = record;\n if (!plain(unknownRecord)) {\n throw new Error(`${where} must be an object of <mount>: <url or record settings>`);\n }\n const sharedFixturePath: unknown = opts.fixturePath;\n if (\n sharedFixturePath !== undefined &&\n !(typeof sharedFixturePath === \"string\" && sharedFixturePath.length > 0)\n ) {\n throw new Error(\"llm.record.fixturePath must be a non-empty string\");\n }\n const wired: {\n mountPath: string;\n config: McpRecordConfig;\n fixturePath: string | undefined;\n held: RecordableMcpMount | undefined;\n }[] = [];\n for (const [mountPath, value] of Object.entries(unknownRecord)) {\n const mount = redactMountForError(mountPath);\n if (!mountPath.startsWith(\"/\")) {\n throw new Error(\n `${where} must be <mount>: <url or object>, with a mount that starts with \"/\"; got ${mount}`,\n );\n }\n const unreachable = unreachableMountReason(mountPath);\n if (unreachable !== undefined) {\n throw new Error(`${where} mount ${mount} can never be reached: ${unreachable}`);\n }\n if (typeof value !== \"string\" && !plain(value)) {\n throw new Error(`${where} mount ${mount} must be an upstream URL string or an object`);\n }\n if (typeof value !== \"string\") {\n for (const [key, v] of Object.entries(value)) {\n if (!isConfigKey(key)) {\n throw new Error(\n `${where} mount ${mount} has an unknown key ${JSON.stringify(key)} (expected one of ${Object.keys(keyTypes).join(\", \")})`,\n );\n }\n if (!hasType(key, v)) {\n throw new Error(`${where} mount ${mount} key ${key} must be ${keyTypes[key]}`);\n }\n }\n if (!(\"upstream\" in value)) {\n throw new Error(`${where} mount ${mount} key upstream is required`);\n }\n }\n // Every key now has its declared type.\n const typed = record[mountPath];\n const config: McpRecordConfig = typeof typed === \"string\" ? { upstream: typed } : typed;\n const badUpstream = badUpstreamReason(config.upstream);\n if (badUpstream !== undefined) {\n throw new Error(`${where} mount ${mount}: the upstream ${badUpstream}`);\n }\n const held = opts.mounted.get(mountPath);\n if (held !== undefined && !(held instanceof opts.mcpMock)) {\n throw new Error(\n `llm.record.mcp mount ${mountPath} is held by a mount that is not an MCP mock`,\n );\n }\n const fixturePath =\n config.fixturePath ??\n opts.fixturePath ??\n (opts.fixtures ? path.resolve(opts.fixtures, \"recorded\") : undefined);\n if (!config.proxyOnly) {\n if (fixturePath === undefined) {\n throw new Error(\n \"llm.record.mcp requires llm.fixtures or llm.record.fixturePath for the recording destination\",\n );\n }\n checkMcpRecordDestination(fixturePath, `${where} mount ${mount}`);\n }\n wired.push({ mountPath, config, fixturePath, held });\n }\n\n const fromEnv = mcpRecordEnv(opts.env);\n for (const { mountPath, config, fixturePath, held } of wired) {\n const mcp = held ?? new opts.mcpMock();\n mcp.enableRecording({\n ...config,\n fixturePath,\n secretValues: config.secretValues ?? fromEnv.secretValues,\n upstreamAuth: config.upstreamAuth ?? fromEnv.upstreamAuth,\n strict: config.strict ?? opts.strict,\n });\n if (held === undefined) llmock.mount(mountPath, mcp);\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { build, fixed, msg, plainText } from "./message-text.js";
2
2
  import { isAuthenticatedRequest } from "./api-key-auth.js";
3
- import { flattenHeaders, readBody, resolveMcpIdentity, resolveStrictMode, slugifyContext, slugifyTestId } from "./helpers.js";
3
+ import { resolveRequestDecisions } from "./response/policy.js";
4
+ import { flattenHeaders, readBody, resolveMcpIdentity, slugifyContext, slugifyTestId } from "./helpers.js";
4
5
  import { buildForwardHeaders, persistServiceFakes, removeForwardHeader, sanitizeHeaderValue } from "./recorder.js";
5
6
  import { Logger } from "./logger.js";
6
7
  import { REDACTED, RecordUnsafeError, knownSecrets, sanitizeRecording, scrubUrl, validateSecretValues } from "./record-sanitize.js";
@@ -270,7 +271,7 @@ var McpRecorder = class {
270
271
  await writeFakeToolAnswer(x.req, x.res, id, claim.result, this.host.fakes.replayOf(claim.entry.id), params, this.host.replaySpeed(), null, x.arrivedAt);
271
272
  return true;
272
273
  }
273
- const strict = resolveStrictMode(this.config.strict, x.req.headers);
274
+ const strict = resolveRequestDecisions(this.config.strict, x.req.headers).mcpRecordingStrict;
274
275
  const ctx = {
275
276
  identity,
276
277
  mount: x.mount,