@copilotkit/aimock 1.43.0 → 1.45.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 (396) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +46 -0
  4. package/README.md +14 -2
  5. package/dist/agui-handler.d.ts.map +1 -1
  6. package/dist/aimock-cli.cjs +31 -5
  7. package/dist/aimock-cli.cjs.map +1 -1
  8. package/dist/aimock-cli.d.cts.map +1 -1
  9. package/dist/aimock-cli.d.ts.map +1 -1
  10. package/dist/aimock-cli.js +31 -5
  11. package/dist/aimock-cli.js.map +1 -1
  12. package/dist/bedrock-converse.cjs +344 -43
  13. package/dist/bedrock-converse.cjs.map +1 -1
  14. package/dist/bedrock-converse.d.cts.map +1 -1
  15. package/dist/bedrock-converse.d.ts.map +1 -1
  16. package/dist/bedrock-converse.js +345 -44
  17. package/dist/bedrock-converse.js.map +1 -1
  18. package/dist/bedrock.cjs +195 -42
  19. package/dist/bedrock.cjs.map +1 -1
  20. package/dist/bedrock.d.cts.map +1 -1
  21. package/dist/bedrock.d.ts.map +1 -1
  22. package/dist/bedrock.js +196 -43
  23. package/dist/bedrock.js.map +1 -1
  24. package/dist/chaos.cjs +9 -5
  25. package/dist/chaos.cjs.map +1 -1
  26. package/dist/chaos.d.cts.map +1 -1
  27. package/dist/chaos.d.ts.map +1 -1
  28. package/dist/chaos.js +10 -7
  29. package/dist/chaos.js.map +1 -1
  30. package/dist/cli.cjs +238 -9
  31. package/dist/cli.cjs.map +1 -1
  32. package/dist/cli.js +242 -13
  33. package/dist/cli.js.map +1 -1
  34. package/dist/cohere.cjs +159 -72
  35. package/dist/cohere.cjs.map +1 -1
  36. package/dist/cohere.d.cts.map +1 -1
  37. package/dist/cohere.d.ts.map +1 -1
  38. package/dist/cohere.js +160 -73
  39. package/dist/cohere.js.map +1 -1
  40. package/dist/config-loader.cjs +63 -5
  41. package/dist/config-loader.cjs.map +1 -1
  42. package/dist/config-loader.d.cts +37 -4
  43. package/dist/config-loader.d.cts.map +1 -1
  44. package/dist/config-loader.d.ts +37 -4
  45. package/dist/config-loader.d.ts.map +1 -1
  46. package/dist/config-loader.js +63 -5
  47. package/dist/config-loader.js.map +1 -1
  48. package/dist/constants.cjs +8 -0
  49. package/dist/constants.cjs.map +1 -1
  50. package/dist/constants.d.cts +6 -2
  51. package/dist/constants.d.ts +6 -2
  52. package/dist/constants.js +8 -1
  53. package/dist/constants.js.map +1 -1
  54. package/dist/echo-text.cjs +65 -0
  55. package/dist/echo-text.cjs.map +1 -0
  56. package/dist/echo-text.d.cts +22 -0
  57. package/dist/echo-text.d.cts.map +1 -0
  58. package/dist/echo-text.d.ts +22 -0
  59. package/dist/echo-text.d.ts.map +1 -0
  60. package/dist/echo-text.js +65 -0
  61. package/dist/echo-text.js.map +1 -0
  62. package/dist/embeddings.cjs +18 -1
  63. package/dist/embeddings.cjs.map +1 -1
  64. package/dist/embeddings.d.cts +1 -1
  65. package/dist/embeddings.d.cts.map +1 -1
  66. package/dist/embeddings.d.ts +1 -1
  67. package/dist/embeddings.d.ts.map +1 -1
  68. package/dist/embeddings.js +18 -1
  69. package/dist/embeddings.js.map +1 -1
  70. package/dist/fakes-plugin.cjs +131 -0
  71. package/dist/fakes-plugin.cjs.map +1 -0
  72. package/dist/fakes-plugin.d.cts +23 -0
  73. package/dist/fakes-plugin.d.cts.map +1 -0
  74. package/dist/fakes-plugin.d.ts +23 -0
  75. package/dist/fakes-plugin.d.ts.map +1 -0
  76. package/dist/fakes-plugin.js +127 -0
  77. package/dist/fakes-plugin.js.map +1 -0
  78. package/dist/files.cjs +9 -11
  79. package/dist/files.cjs.map +1 -1
  80. package/dist/files.d.cts.map +1 -1
  81. package/dist/files.d.ts.map +1 -1
  82. package/dist/files.js +10 -13
  83. package/dist/files.js.map +1 -1
  84. package/dist/fine-tuning.cjs +1 -19
  85. package/dist/fine-tuning.cjs.map +1 -1
  86. package/dist/fine-tuning.d.cts.map +1 -1
  87. package/dist/fine-tuning.d.ts.map +1 -1
  88. package/dist/fine-tuning.js +2 -20
  89. package/dist/fine-tuning.js.map +1 -1
  90. package/dist/fixture-loader-services.cjs +116 -0
  91. package/dist/fixture-loader-services.cjs.map +1 -0
  92. package/dist/fixture-loader-services.d.cts +48 -0
  93. package/dist/fixture-loader-services.d.cts.map +1 -0
  94. package/dist/fixture-loader-services.d.ts +48 -0
  95. package/dist/fixture-loader-services.d.ts.map +1 -0
  96. package/dist/fixture-loader-services.js +114 -0
  97. package/dist/fixture-loader-services.js.map +1 -0
  98. package/dist/fixture-loader.cjs +552 -29
  99. package/dist/fixture-loader.cjs.map +1 -1
  100. package/dist/fixture-loader.d.cts +140 -2
  101. package/dist/fixture-loader.d.cts.map +1 -1
  102. package/dist/fixture-loader.d.ts +140 -2
  103. package/dist/fixture-loader.d.ts.map +1 -1
  104. package/dist/fixture-loader.js +542 -31
  105. package/dist/fixture-loader.js.map +1 -1
  106. package/dist/gemini-embeddings.cjs +42 -2
  107. package/dist/gemini-embeddings.cjs.map +1 -1
  108. package/dist/gemini-embeddings.js +43 -3
  109. package/dist/gemini-embeddings.js.map +1 -1
  110. package/dist/gemini-interactions.cjs +288 -52
  111. package/dist/gemini-interactions.cjs.map +1 -1
  112. package/dist/gemini-interactions.d.cts.map +1 -1
  113. package/dist/gemini-interactions.d.ts.map +1 -1
  114. package/dist/gemini-interactions.js +289 -53
  115. package/dist/gemini-interactions.js.map +1 -1
  116. package/dist/gemini.cjs +234 -38
  117. package/dist/gemini.cjs.map +1 -1
  118. package/dist/gemini.d.cts.map +1 -1
  119. package/dist/gemini.d.ts.map +1 -1
  120. package/dist/gemini.js +235 -39
  121. package/dist/gemini.js.map +1 -1
  122. package/dist/helpers.cjs +1157 -12
  123. package/dist/helpers.cjs.map +1 -1
  124. package/dist/helpers.d.cts +85 -4
  125. package/dist/helpers.d.cts.map +1 -1
  126. package/dist/helpers.d.ts +85 -4
  127. package/dist/helpers.d.ts.map +1 -1
  128. package/dist/helpers.js +1113 -14
  129. package/dist/helpers.js.map +1 -1
  130. package/dist/images.cjs +3 -2
  131. package/dist/images.cjs.map +1 -1
  132. package/dist/images.js +3 -2
  133. package/dist/images.js.map +1 -1
  134. package/dist/index.cjs +39 -1
  135. package/dist/index.d.cts +11 -4
  136. package/dist/index.d.ts +11 -4
  137. package/dist/index.js +11 -4
  138. package/dist/jest.cjs +43 -14
  139. package/dist/jest.cjs.map +1 -1
  140. package/dist/jest.d.cts +47 -5
  141. package/dist/jest.d.cts.map +1 -1
  142. package/dist/jest.d.ts +47 -5
  143. package/dist/jest.d.ts.map +1 -1
  144. package/dist/jest.js +45 -16
  145. package/dist/jest.js.map +1 -1
  146. package/dist/journal.cjs +155 -13
  147. package/dist/journal.cjs.map +1 -1
  148. package/dist/journal.d.cts +54 -4
  149. package/dist/journal.d.cts.map +1 -1
  150. package/dist/journal.d.ts +54 -4
  151. package/dist/journal.d.ts.map +1 -1
  152. package/dist/journal.js +155 -13
  153. package/dist/journal.js.map +1 -1
  154. package/dist/live-sanitize.cjs +4 -32
  155. package/dist/live-sanitize.cjs.map +1 -1
  156. package/dist/live-sanitize.js +4 -32
  157. package/dist/live-sanitize.js.map +1 -1
  158. package/dist/llmock.cjs +204 -39
  159. package/dist/llmock.cjs.map +1 -1
  160. package/dist/llmock.d.cts +51 -1
  161. package/dist/llmock.d.cts.map +1 -1
  162. package/dist/llmock.d.ts +51 -1
  163. package/dist/llmock.d.ts.map +1 -1
  164. package/dist/llmock.js +206 -41
  165. package/dist/llmock.js.map +1 -1
  166. package/dist/mcp-fakes-mount.cjs +217 -0
  167. package/dist/mcp-fakes-mount.cjs.map +1 -0
  168. package/dist/mcp-fakes-mount.js +211 -0
  169. package/dist/mcp-fakes-mount.js.map +1 -0
  170. package/dist/mcp-fakes-report.cjs +111 -0
  171. package/dist/mcp-fakes-report.cjs.map +1 -0
  172. package/dist/mcp-fakes-report.d.cts +40 -0
  173. package/dist/mcp-fakes-report.d.cts.map +1 -0
  174. package/dist/mcp-fakes-report.d.ts +40 -0
  175. package/dist/mcp-fakes-report.d.ts.map +1 -0
  176. package/dist/mcp-fakes-report.js +107 -0
  177. package/dist/mcp-fakes-report.js.map +1 -0
  178. package/dist/mcp-fakes.cjs +1942 -0
  179. package/dist/mcp-fakes.cjs.map +1 -0
  180. package/dist/mcp-fakes.d.cts +504 -0
  181. package/dist/mcp-fakes.d.cts.map +1 -0
  182. package/dist/mcp-fakes.d.ts +504 -0
  183. package/dist/mcp-fakes.d.ts.map +1 -0
  184. package/dist/mcp-fakes.js +1937 -0
  185. package/dist/mcp-fakes.js.map +1 -0
  186. package/dist/mcp-handler.cjs +534 -27
  187. package/dist/mcp-handler.cjs.map +1 -1
  188. package/dist/mcp-handler.d.cts +49 -0
  189. package/dist/mcp-handler.d.cts.map +1 -0
  190. package/dist/mcp-handler.d.ts +49 -0
  191. package/dist/mcp-handler.d.ts.map +1 -0
  192. package/dist/mcp-handler.js +530 -28
  193. package/dist/mcp-handler.js.map +1 -1
  194. package/dist/mcp-mock.cjs +326 -47
  195. package/dist/mcp-mock.cjs.map +1 -1
  196. package/dist/mcp-mock.d.cts +97 -1
  197. package/dist/mcp-mock.d.cts.map +1 -1
  198. package/dist/mcp-mock.d.ts +97 -1
  199. package/dist/mcp-mock.d.ts.map +1 -1
  200. package/dist/mcp-mock.js +327 -48
  201. package/dist/mcp-mock.js.map +1 -1
  202. package/dist/mcp-recorder.cjs +1224 -0
  203. package/dist/mcp-recorder.cjs.map +1 -0
  204. package/dist/mcp-recorder.d.cts +108 -0
  205. package/dist/mcp-recorder.d.cts.map +1 -0
  206. package/dist/mcp-recorder.d.ts +108 -0
  207. package/dist/mcp-recorder.d.ts.map +1 -0
  208. package/dist/mcp-recorder.js +1215 -0
  209. package/dist/mcp-recorder.js.map +1 -0
  210. package/dist/mcp-stub.cjs +5 -1
  211. package/dist/mcp-stub.d.cts +3 -1
  212. package/dist/mcp-stub.d.ts +3 -1
  213. package/dist/mcp-stub.js +3 -1
  214. package/dist/mcp-types.d.cts +6 -0
  215. package/dist/mcp-types.d.ts +6 -0
  216. package/dist/message-text.cjs +192 -0
  217. package/dist/message-text.cjs.map +1 -0
  218. package/dist/message-text.d.cts +31 -0
  219. package/dist/message-text.d.cts.map +1 -0
  220. package/dist/message-text.d.ts +31 -0
  221. package/dist/message-text.d.ts.map +1 -0
  222. package/dist/message-text.js +184 -0
  223. package/dist/message-text.js.map +1 -0
  224. package/dist/messages.cjs +400 -56
  225. package/dist/messages.cjs.map +1 -1
  226. package/dist/messages.d.cts.map +1 -1
  227. package/dist/messages.d.ts.map +1 -1
  228. package/dist/messages.js +401 -58
  229. package/dist/messages.js.map +1 -1
  230. package/dist/metrics.cjs +46 -66
  231. package/dist/metrics.cjs.map +1 -1
  232. package/dist/metrics.d.cts +14 -6
  233. package/dist/metrics.d.cts.map +1 -1
  234. package/dist/metrics.d.ts +14 -6
  235. package/dist/metrics.d.ts.map +1 -1
  236. package/dist/metrics.js +32 -40
  237. package/dist/metrics.js.map +1 -1
  238. package/dist/misbehavior.cjs +1523 -0
  239. package/dist/misbehavior.cjs.map +1 -0
  240. package/dist/misbehavior.d.cts +43 -0
  241. package/dist/misbehavior.d.cts.map +1 -0
  242. package/dist/misbehavior.d.ts +43 -0
  243. package/dist/misbehavior.d.ts.map +1 -0
  244. package/dist/misbehavior.js +1511 -0
  245. package/dist/misbehavior.js.map +1 -0
  246. package/dist/ollama.cjs +315 -113
  247. package/dist/ollama.cjs.map +1 -1
  248. package/dist/ollama.d.cts.map +1 -1
  249. package/dist/ollama.d.ts.map +1 -1
  250. package/dist/ollama.js +316 -114
  251. package/dist/ollama.js.map +1 -1
  252. package/dist/openapi.cjs +3405 -0
  253. package/dist/openapi.cjs.map +1 -0
  254. package/dist/openapi.d.cts +30 -0
  255. package/dist/openapi.d.cts.map +1 -0
  256. package/dist/openapi.d.ts +30 -0
  257. package/dist/openapi.d.ts.map +1 -0
  258. package/dist/openapi.js +3404 -0
  259. package/dist/openapi.js.map +1 -0
  260. package/dist/openrouter-video.cjs +4 -4
  261. package/dist/openrouter-video.cjs.map +1 -1
  262. package/dist/openrouter-video.d.cts.map +1 -1
  263. package/dist/openrouter-video.d.ts.map +1 -1
  264. package/dist/openrouter-video.js +4 -4
  265. package/dist/openrouter-video.js.map +1 -1
  266. package/dist/record-sanitize.cjs +231 -0
  267. package/dist/record-sanitize.cjs.map +1 -0
  268. package/dist/record-sanitize.js +224 -0
  269. package/dist/record-sanitize.js.map +1 -0
  270. package/dist/recorder.cjs +115 -6
  271. package/dist/recorder.cjs.map +1 -1
  272. package/dist/recorder.d.cts +2 -1
  273. package/dist/recorder.d.cts.map +1 -1
  274. package/dist/recorder.d.ts +2 -1
  275. package/dist/recorder.d.ts.map +1 -1
  276. package/dist/recorder.js +116 -8
  277. package/dist/recorder.js.map +1 -1
  278. package/dist/responses.cjs +701 -166
  279. package/dist/responses.cjs.map +1 -1
  280. package/dist/responses.d.cts +5 -13
  281. package/dist/responses.d.cts.map +1 -1
  282. package/dist/responses.d.ts +5 -13
  283. package/dist/responses.d.ts.map +1 -1
  284. package/dist/responses.js +699 -169
  285. package/dist/responses.js.map +1 -1
  286. package/dist/route-registry.cjs +951 -0
  287. package/dist/route-registry.cjs.map +1 -0
  288. package/dist/route-registry.d.cts +33 -0
  289. package/dist/route-registry.d.cts.map +1 -0
  290. package/dist/route-registry.d.ts +33 -0
  291. package/dist/route-registry.d.ts.map +1 -0
  292. package/dist/route-registry.js +866 -0
  293. package/dist/route-registry.js.map +1 -0
  294. package/dist/router.cjs +24 -2
  295. package/dist/router.cjs.map +1 -1
  296. package/dist/router.d.cts.map +1 -1
  297. package/dist/router.d.ts.map +1 -1
  298. package/dist/router.js +25 -4
  299. package/dist/router.js.map +1 -1
  300. package/dist/server.cjs +1159 -264
  301. package/dist/server.cjs.map +1 -1
  302. package/dist/server.d.cts +4 -1
  303. package/dist/server.d.cts.map +1 -1
  304. package/dist/server.d.ts +4 -1
  305. package/dist/server.d.ts.map +1 -1
  306. package/dist/server.js +1067 -172
  307. package/dist/server.js.map +1 -1
  308. package/dist/speech.cjs +4 -2
  309. package/dist/speech.cjs.map +1 -1
  310. package/dist/speech.js +4 -2
  311. package/dist/speech.js.map +1 -1
  312. package/dist/stream-collapse.cjs +236 -34
  313. package/dist/stream-collapse.cjs.map +1 -1
  314. package/dist/stream-collapse.d.cts +39 -4
  315. package/dist/stream-collapse.d.cts.map +1 -1
  316. package/dist/stream-collapse.d.ts +39 -4
  317. package/dist/stream-collapse.d.ts.map +1 -1
  318. package/dist/stream-collapse.js +235 -35
  319. package/dist/stream-collapse.js.map +1 -1
  320. package/dist/suite.cjs +1 -1
  321. package/dist/suite.js +1 -1
  322. package/dist/transcription.cjs +2 -2
  323. package/dist/transcription.cjs.map +1 -1
  324. package/dist/transcription.d.cts.map +1 -1
  325. package/dist/transcription.d.ts.map +1 -1
  326. package/dist/transcription.js +2 -2
  327. package/dist/transcription.js.map +1 -1
  328. package/dist/types.cjs +18 -0
  329. package/dist/types.cjs.map +1 -0
  330. package/dist/types.d.cts +516 -5
  331. package/dist/types.d.cts.map +1 -1
  332. package/dist/types.d.ts +516 -5
  333. package/dist/types.d.ts.map +1 -1
  334. package/dist/types.js +17 -0
  335. package/dist/types.js.map +1 -0
  336. package/dist/validate-cli.cjs +123 -33
  337. package/dist/validate-cli.cjs.map +1 -1
  338. package/dist/validate-cli.js +125 -35
  339. package/dist/validate-cli.js.map +1 -1
  340. package/dist/vector-stores.cjs +1180 -0
  341. package/dist/vector-stores.cjs.map +1 -0
  342. package/dist/vector-stores.d.cts +106 -0
  343. package/dist/vector-stores.d.cts.map +1 -0
  344. package/dist/vector-stores.d.ts +106 -0
  345. package/dist/vector-stores.d.ts.map +1 -0
  346. package/dist/vector-stores.js +1164 -0
  347. package/dist/vector-stores.js.map +1 -0
  348. package/dist/vector-types.d.cts.map +1 -1
  349. package/dist/version.cjs +45 -0
  350. package/dist/version.cjs.map +1 -0
  351. package/dist/version.js +44 -0
  352. package/dist/version.js.map +1 -0
  353. package/dist/video.cjs +19 -0
  354. package/dist/video.cjs.map +1 -1
  355. package/dist/video.d.cts.map +1 -1
  356. package/dist/video.d.ts.map +1 -1
  357. package/dist/video.js +19 -0
  358. package/dist/video.js.map +1 -1
  359. package/dist/vitest.cjs +77 -16
  360. package/dist/vitest.cjs.map +1 -1
  361. package/dist/vitest.d.cts +38 -5
  362. package/dist/vitest.d.cts.map +1 -1
  363. package/dist/vitest.d.ts +38 -5
  364. package/dist/vitest.d.ts.map +1 -1
  365. package/dist/vitest.js +78 -17
  366. package/dist/vitest.js.map +1 -1
  367. package/dist/watcher.cjs +6 -2
  368. package/dist/watcher.cjs.map +1 -1
  369. package/dist/watcher.js +6 -2
  370. package/dist/watcher.js.map +1 -1
  371. package/dist/ws-gemini-live.cjs +210 -101
  372. package/dist/ws-gemini-live.cjs.map +1 -1
  373. package/dist/ws-gemini-live.d.cts +9 -3
  374. package/dist/ws-gemini-live.d.cts.map +1 -1
  375. package/dist/ws-gemini-live.d.ts +9 -3
  376. package/dist/ws-gemini-live.d.ts.map +1 -1
  377. package/dist/ws-gemini-live.js +211 -102
  378. package/dist/ws-gemini-live.js.map +1 -1
  379. package/dist/ws-realtime.cjs +175 -62
  380. package/dist/ws-realtime.cjs.map +1 -1
  381. package/dist/ws-realtime.d.cts +8 -3
  382. package/dist/ws-realtime.d.cts.map +1 -1
  383. package/dist/ws-realtime.d.ts +8 -3
  384. package/dist/ws-realtime.d.ts.map +1 -1
  385. package/dist/ws-realtime.js +176 -63
  386. package/dist/ws-realtime.js.map +1 -1
  387. package/dist/ws-responses.cjs +121 -16
  388. package/dist/ws-responses.cjs.map +1 -1
  389. package/dist/ws-responses.d.cts +8 -12
  390. package/dist/ws-responses.d.cts.map +1 -1
  391. package/dist/ws-responses.d.ts +8 -12
  392. package/dist/ws-responses.d.ts.map +1 -1
  393. package/dist/ws-responses.js +123 -18
  394. package/dist/ws-responses.js.map +1 -1
  395. package/package.json +13 -2
  396. package/skills/write-fixtures/SKILL.md +179 -21
@@ -1,7 +1,46 @@
1
+ import { build, fixed, jsonText, msg, plainText, quote } from "./message-text.js";
2
+ import { decodeMcpHeaderValue, resolveMcpIdentity } from "./helpers.js";
1
3
  import { createJsonRpcDispatcher } from "./jsonrpc.js";
4
+ import { MCP_FAKE_ERROR_CODES } from "./types.js";
2
5
  import { randomUUID } from "node:crypto";
3
6
 
4
7
  //#region src/mcp-handler.ts
8
+ /**
9
+ * `data.aimock.code` of a `tools/call` whose fake claim threw: an aimock
10
+ * defect (a validated entry whose result could not be built or sent), not a
11
+ * scenario failure. Answered with the JSON-RPC internal-error code `-32603`;
12
+ * the entry stays unconsumed.
13
+ */
14
+ const MCP_FAKE_INTERNAL_ERROR = "MCP_FAKE_INTERNAL_ERROR";
15
+ const MCP_FAKE_INTERNAL_ERROR_RPC_CODE = -32603;
16
+ /** An empty record, for the caller to pass in and read even if the handler throws. */
17
+ function newMCPRequestRecord() {
18
+ return {
19
+ identity: null,
20
+ messages: []
21
+ };
22
+ }
23
+ /**
24
+ * C8: the `onToolCall` handlers that answer a config `mcp.tools[].result`.
25
+ * `tools/call` reports such an answer as `answeredBy: "config"`. Internal:
26
+ * not exported from the package.
27
+ */
28
+ const configToolHandlers = /* @__PURE__ */ new WeakSet();
29
+ /** C7: the syslog levels of `logging/setLevel`, lowest first. */
30
+ const LOG_LEVELS = [
31
+ "debug",
32
+ "info",
33
+ "notice",
34
+ "warning",
35
+ "error",
36
+ "critical",
37
+ "alert",
38
+ "emergency"
39
+ ];
40
+ /** C7: the rank of a syslog level name, or -1 when it is not one. */
41
+ function logLevelRank(level) {
42
+ return typeof level === "string" ? LOG_LEVELS.indexOf(level) : -1;
43
+ }
5
44
  function jsonRpcResult(id, result) {
6
45
  return {
7
46
  jsonrpc: "2.0",
@@ -9,26 +48,195 @@ function jsonRpcResult(id, result) {
9
48
  result
10
49
  };
11
50
  }
12
- function jsonRpcError(id, code, message) {
51
+ function jsonRpcError(id, code, message, data) {
13
52
  return {
14
53
  jsonrpc: "2.0",
15
54
  id,
16
- error: {
55
+ error: data === void 0 ? {
17
56
  code,
18
57
  message
58
+ } : {
59
+ code,
60
+ message,
61
+ data
19
62
  }
20
63
  };
21
64
  }
65
+ /**
66
+ * The identity part of a fake message or log line (I9): `testId "<id>"`,
67
+ * `context "<ctx>"`, both, or `no testId or context`.
68
+ */
69
+ function describeMcpIdentity(identity) {
70
+ const { testId, context } = identity;
71
+ if (testId !== null && context !== null) return msg`testId ${quote(testId)}, context ${quote(context)}`;
72
+ if (testId !== null) return msg`testId ${quote(testId)}`;
73
+ if (context !== null) return msg`context ${quote(context)}`;
74
+ return msg`no testId or context`;
75
+ }
76
+ /** A value as JSON text for a message part. */
77
+ function jsonPart(value) {
78
+ return jsonText(JSON.stringify(value) ?? "null");
79
+ }
80
+ /**
81
+ * The 9.2 error for a failed claim (`mismatch`, `exhausted`, `evicted`), or
82
+ * for an undeclared tool under `deny` (`not_declared`).
83
+ */
84
+ function fakeFailure(claim, tool, ctx, declaredTools) {
85
+ const who = describeMcpIdentity(ctx.identity);
86
+ const base = {
87
+ tool,
88
+ testId: ctx.identity.testId,
89
+ context: ctx.identity.context,
90
+ mount: ctx.mount
91
+ };
92
+ switch (claim.kind) {
93
+ case "mismatch": {
94
+ const list = claim.declared.map((d) => d.args !== void 0 ? msg`${plainText(d.id)} ${jsonPart(d.args)}` : msg`${plainText(d.id)} anyArgs`).reduce((acc, part, i) => i === 0 ? part : msg`${acc}; ${part}`, msg`none`);
95
+ return {
96
+ code: "MCP_FAKE_MISMATCH",
97
+ outcome: "mismatch",
98
+ message: build(msg`aimock MCP fake mismatch: tool ${quote(tool)} was called with arguments that match no declared fake (${who}). Received ${jsonPart(claim.received)}. Declared: ${list}.`),
99
+ aimock: {
100
+ code: "MCP_FAKE_MISMATCH",
101
+ ...base,
102
+ received: claim.received,
103
+ declared: claim.declared,
104
+ firstDifference: claim.firstDifference
105
+ }
106
+ };
107
+ }
108
+ case "exhausted": {
109
+ const used = claim.matchingDeclared === 1 ? msg`the 1 matching fake is` : msg`all ${claim.matchingDeclared} matching fakes are`;
110
+ return {
111
+ code: "MCP_FAKE_EXHAUSTED",
112
+ outcome: "exhausted",
113
+ message: build(msg`aimock MCP fake exhausted: tool ${quote(tool)} with ${jsonPart(claim.received)} was called again, but ${used} already used (${who}).`),
114
+ aimock: {
115
+ code: "MCP_FAKE_EXHAUSTED",
116
+ ...base,
117
+ received: claim.received,
118
+ matchingDeclared: claim.matchingDeclared,
119
+ matchingConsumed: claim.matchingConsumed,
120
+ matchingIds: claim.matchingIds
121
+ }
122
+ };
123
+ }
124
+ case "evicted": return {
125
+ code: "MCP_FAKE_EVICTED",
126
+ outcome: "evicted",
127
+ evicted: {
128
+ testId: claim.testId,
129
+ cap: claim.maxTestIds
130
+ },
131
+ message: build(msg`aimock MCP fake evicted: consumption state for testId ${quote(claim.testId)} was evicted by the per-mount test-id cap (${claim.maxTestIds}); reset before reusing this test id (${who}).`),
132
+ aimock: {
133
+ code: "MCP_FAKE_EVICTED",
134
+ ...base,
135
+ testId: claim.testId,
136
+ cap: claim.maxTestIds
137
+ }
138
+ };
139
+ case "not_declared": return {
140
+ code: "MCP_FAKE_NOT_DECLARED",
141
+ outcome: "not_declared",
142
+ message: build(msg`Unknown tool: ${plainText(tool)} (aimock MCP fake not declared: this scenario denies undeclared tools; ${who}).`),
143
+ aimock: {
144
+ code: "MCP_FAKE_NOT_DECLARED",
145
+ ...base,
146
+ declaredTools: declaredTools()
147
+ }
148
+ };
149
+ }
150
+ }
22
151
  function createMCPRequestHandler(state) {
152
+ /** Per-request fake context, set by the outer handler for the dispatcher's methods. */
153
+ const contexts = /* @__PURE__ */ new WeakMap();
154
+ /** C7: each session's `logging/setLevel` level; dropped on DELETE. */
155
+ const logLevels = /* @__PURE__ */ new Map();
156
+ /** The initialize capabilities: `logging` only on a mount with recorded logs (C7). */
157
+ const capabilities = () => state.fakes.hasRecordedLogs() ? {
158
+ tools: {},
159
+ resources: {},
160
+ prompts: {},
161
+ logging: {}
162
+ } : {
163
+ tools: {},
164
+ resources: {},
165
+ prompts: {}
166
+ };
167
+ /**
168
+ * Spec 10 steps 1-2 for `tools/call`: a fake answer or fake failure, or
169
+ * `null` to fall through to today's behavior (step 3).
170
+ */
171
+ const callFake = (ctx, outcome, tool, args, id) => {
172
+ const event = {
173
+ identity: ctx.identity,
174
+ tool,
175
+ mount: ctx.mount,
176
+ args
177
+ };
178
+ let claim;
179
+ try {
180
+ claim = state.fakes.claim(tool, args, ctx.identity);
181
+ } catch (err) {
182
+ const thrown = err instanceof Error ? err.message : String(err);
183
+ const message = build(msg`aimock MCP fake internal error: tool ${quote(tool)} could not be answered (${describeMcpIdentity(ctx.identity)}): ${plainText(thrown)}`);
184
+ outcome.error = message;
185
+ state.onFakeEvent({
186
+ ...event,
187
+ kind: "internal_error",
188
+ code: MCP_FAKE_INTERNAL_ERROR,
189
+ message
190
+ });
191
+ return jsonRpcError(id, MCP_FAKE_INTERNAL_ERROR_RPC_CODE, message, { aimock: {
192
+ code: MCP_FAKE_INTERNAL_ERROR,
193
+ tool,
194
+ testId: ctx.identity.testId,
195
+ context: ctx.identity.context,
196
+ mount: ctx.mount,
197
+ error: thrown
198
+ } });
199
+ }
200
+ if (claim.kind === "answer") {
201
+ outcome.mcpFake = {
202
+ id: claim.entry.id,
203
+ outcome: "answered"
204
+ };
205
+ state.onFakeEvent({
206
+ ...event,
207
+ kind: "answered",
208
+ entryId: claim.entry.id
209
+ });
210
+ return jsonRpcResult(id, claim.result);
211
+ }
212
+ let failure;
213
+ if (claim.kind === "none") {
214
+ if (state.fakes.policy(ctx.identity) !== "deny") return null;
215
+ failure = fakeFailure({ kind: "not_declared" }, tool, ctx, () => state.fakes.declaredTools(ctx.identity));
216
+ } else failure = fakeFailure(claim, tool, ctx, () => []);
217
+ outcome.mcpFake = {
218
+ id: null,
219
+ outcome: failure.outcome
220
+ };
221
+ state.onFakeEvent(failure.outcome === "evicted" ? {
222
+ ...event,
223
+ kind: "evicted",
224
+ code: failure.code,
225
+ message: failure.message,
226
+ ...failure.evicted
227
+ } : {
228
+ ...event,
229
+ kind: failure.outcome,
230
+ code: failure.code,
231
+ message: failure.message
232
+ });
233
+ return jsonRpcError(id, MCP_FAKE_ERROR_CODES[failure.code], failure.message, { aimock: failure.aimock });
234
+ };
23
235
  const dispatcher = createJsonRpcDispatcher({ methods: {
24
236
  initialize: async (_params, id) => {
25
237
  return jsonRpcResult(id, {
26
238
  protocolVersion: "2025-03-26",
27
- capabilities: {
28
- tools: {},
29
- resources: {},
30
- prompts: {}
31
- },
239
+ capabilities: capabilities(),
32
240
  serverInfo: state.serverInfo
33
241
  });
34
242
  },
@@ -41,15 +249,68 @@ function createMCPRequestHandler(state) {
41
249
  ping: async (_params, id) => {
42
250
  return jsonRpcResult(id, {});
43
251
  },
44
- "tools/list": async (_params, id) => {
252
+ "logging/setLevel": async (params, id, req) => {
253
+ if (!state.fakes.hasRecordedLogs()) return jsonRpcError(id, -32601, "Method not found");
254
+ const level = params ?? {};
255
+ if (logLevelRank(level.level) === -1) return jsonRpcError(id, -32602, "Invalid log level");
256
+ const sessionId = req.headers["mcp-session-id"];
257
+ if (typeof sessionId === "string") logLevels.set(sessionId, String(level.level));
258
+ return jsonRpcResult(id, {});
259
+ },
260
+ "tools/list": async (_params, id, req) => {
45
261
  const tools = [];
46
262
  for (const { def } of state.tools.values()) tools.push(def);
263
+ const ctx = contexts.get(req);
264
+ const recorded = ctx ? state.fakes.recordedList(ctx.identity) : null;
265
+ if (ctx && recorded) {
266
+ const listed = [...recorded];
267
+ const names = new Set(recorded.map((t) => t.name));
268
+ for (const def of tools) if (!names.has(def.name)) {
269
+ names.add(def.name);
270
+ listed.push(def);
271
+ }
272
+ for (const fake of state.fakes.listTools(ctx.identity)) if (!names.has(fake.name)) {
273
+ names.add(fake.name);
274
+ listed.push(fake);
275
+ }
276
+ return jsonRpcResult(id, { tools: listed });
277
+ }
278
+ if (ctx) {
279
+ const registered = new Set(tools.map((t) => t.name));
280
+ for (const fake of state.fakes.listTools(ctx.identity)) if (!registered.has(fake.name)) tools.push(fake);
281
+ }
47
282
  return jsonRpcResult(id, { tools });
48
283
  },
49
- "tools/call": async (params, id) => {
50
- const { name, arguments: args } = params ?? {};
51
- if (!name) return jsonRpcError(id, -32602, "Missing tool name");
284
+ "tools/call": async (params, id, req) => {
285
+ const ctx = contexts.get(req);
286
+ const precomputed = ctx?.precomputed;
287
+ if (ctx) ctx.precomputed = void 0;
288
+ const isNotification = !precomputed && ctx?.notifications[ctx.calls.length] === true;
289
+ if (!precomputed) ctx?.calls.push({
290
+ mcpFake: null,
291
+ error: null
292
+ });
293
+ const outcome = ctx?.calls[ctx.calls.length - 1] ?? {
294
+ mcpFake: null,
295
+ error: null
296
+ };
297
+ const { name: rawName, arguments: args } = params ?? {};
298
+ if (!rawName) return jsonRpcError(id, -32602, "Missing tool name");
299
+ if (typeof rawName !== "string") return jsonRpcError(id, -32602, build(msg`Invalid tool name: expected a string, got ${fixed(Array.isArray(rawName) ? "array" : typeof rawName)}`));
300
+ const name = rawName;
301
+ if (precomputed?.kind === "reply") return precomputed.value;
302
+ if (ctx && !isNotification && !precomputed) {
303
+ const fake = callFake(ctx, outcome, name, args, id);
304
+ if (fake) return fake;
305
+ }
52
306
  const entry = state.tools.get(name);
307
+ if (ctx && !isNotification) state.onUnfaked({
308
+ identity: ctx.identity,
309
+ tool: name,
310
+ mount: ctx.mount,
311
+ args,
312
+ answeredBy: !entry ? "unknown-tool" : entry.handler && configToolHandlers.has(entry.handler) ? "config" : entry.handler ? "handler" : "empty"
313
+ });
53
314
  if (!entry) return jsonRpcError(id, -32602, `Unknown tool: ${name}`);
54
315
  if (entry.handler) try {
55
316
  const result = await entry.handler(args);
@@ -109,50 +370,126 @@ function createMCPRequestHandler(state) {
109
370
  return jsonRpcResult(id, { messages: [] });
110
371
  }
111
372
  } });
112
- return async (req, res, body) => {
373
+ /** Answer a rejected identity (H4, H5) with HTTP 400, in the session-error shape. */
374
+ const rejectIdentity = (res, resolution) => {
375
+ res.writeHead(resolution.error.status, { "Content-Type": "application/json" });
376
+ res.end(JSON.stringify({
377
+ error: resolution.error.message,
378
+ code: resolution.error.code
379
+ }));
380
+ };
381
+ /**
382
+ * L2: report each identity value that fell back to the raw value (H1),
383
+ * naming its source (header or query) and the value itself, and any query
384
+ * name that did not decode. `fellBack` says only that some value of the
385
+ * field fell back; the header is checked on its own, and the query by the
386
+ * same resolver given the query alone, so the rules are the resolver's.
387
+ */
388
+ const reportFallback = (sessionId, req, resolution) => {
389
+ if (!state.hasFakes()) return;
390
+ const { fellBack } = resolution;
391
+ if (fellBack.testId || fellBack.context) {
392
+ const queryOnly = resolveMcpIdentity({
393
+ headers: {},
394
+ url: req.url
395
+ });
396
+ for (const field of ["testId", "context"]) {
397
+ if (!fellBack[field]) continue;
398
+ const header = req.headers[IDENTITY_HEADERS[field]];
399
+ const headerSent = typeof header === "string" && header !== "";
400
+ if (headerSent && decodeMcpHeaderValue(header).fellBack) state.onDecodeFallback(sessionId, {
401
+ kind: "value",
402
+ field,
403
+ source: "header",
404
+ raw: header,
405
+ used: true
406
+ });
407
+ const fromQuery = queryOnly.ok && queryOnly.fellBack[field] ? queryOnly.identity[field] : null;
408
+ if (fromQuery !== null) state.onDecodeFallback(sessionId, {
409
+ kind: "value",
410
+ field,
411
+ source: "query",
412
+ raw: fromQuery,
413
+ used: !headerSent
414
+ });
415
+ }
416
+ }
417
+ if (resolution.undecodedQueryNames) state.onDecodeFallback(sessionId, {
418
+ kind: "names",
419
+ count: resolution.undecodedQueryNames
420
+ });
421
+ };
422
+ /**
423
+ * Handle one MCP request. `mount` is the path the mount is served at (I9;
424
+ * `/` for a standalone server). Resolves with what the request resolved,
425
+ * for the mount's journal entry.
426
+ */
427
+ return async (req, res, body, mount = "/", record = newMCPRequestRecord()) => {
428
+ /** T1: recorded `atMs` / `durationMs` are measured from here. */
429
+ const arrivedAt = performance.now();
113
430
  if (req.method === "DELETE") {
114
431
  const sessionId = req.headers["mcp-session-id"];
115
432
  if (!sessionId) {
116
433
  res.writeHead(400, { "Content-Type": "application/json" });
117
434
  res.end(JSON.stringify({ error: "Missing mcp-session-id header" }));
118
- return;
435
+ return record;
119
436
  }
120
437
  if (!state.sessions.has(sessionId)) {
121
438
  res.writeHead(404, { "Content-Type": "application/json" });
122
439
  res.end(JSON.stringify({ error: "Session not found" }));
123
- return;
440
+ return record;
124
441
  }
442
+ const resolution = resolveMcpIdentity(req, state.sessions.get(sessionId));
443
+ if (resolution.ok) record.identity = resolution.identity;
125
444
  state.sessions.delete(sessionId);
445
+ logLevels.delete(sessionId);
446
+ state.onSessionClosed(sessionId);
126
447
  res.writeHead(200, { "Content-Type": "application/json" });
127
448
  res.end(JSON.stringify({ ok: true }));
128
- return;
449
+ return record;
129
450
  }
451
+ const headerSessionId = req.headers["mcp-session-id"];
452
+ const knownSession = headerSessionId ? state.sessions.get(headerSessionId) : void 0;
130
453
  let parsed;
131
454
  try {
132
455
  parsed = JSON.parse(body);
133
456
  } catch {
457
+ const resolution = resolveMcpIdentity(req, knownSession);
458
+ if (!resolution.ok) {
459
+ rejectIdentity(res, resolution);
460
+ return record;
461
+ }
462
+ record.identity = resolution.identity;
463
+ if (headerSessionId && knownSession) reportFallback(headerSessionId, req, resolution);
134
464
  await dispatcher(req, res, body);
135
- return;
465
+ return record;
136
466
  }
137
467
  const method = typeof parsed === "object" && parsed !== null && "method" in parsed ? parsed.method : void 0;
138
468
  if (method === "initialize") {
139
469
  const id = typeof parsed === "object" && parsed !== null && "id" in parsed ? parsed.id : null;
470
+ const resolution = resolveMcpIdentity(req);
471
+ if (!resolution.ok) {
472
+ rejectIdentity(res, resolution);
473
+ return record;
474
+ }
475
+ record.identity = resolution.identity;
476
+ const { identity, supplied } = resolution;
140
477
  const sessionId = randomUUID();
141
478
  state.sessions.set(sessionId, {
142
479
  id: sessionId,
143
480
  initialized: false,
144
- createdAt: Date.now()
481
+ createdAt: Date.now(),
482
+ ...supplied.testId && identity.testId !== null ? { testId: identity.testId } : {},
483
+ ...supplied.context && identity.context !== null ? { context: identity.context } : {},
484
+ ...supplied.undeclared && identity.undeclared !== null ? { undeclared: identity.undeclared } : {}
145
485
  });
486
+ reportFallback(sessionId, req, resolution);
146
487
  const response = {
147
488
  jsonrpc: "2.0",
148
489
  id,
149
490
  result: {
150
491
  protocolVersion: "2025-03-26",
151
- capabilities: {
152
- tools: {},
153
- resources: {},
154
- prompts: {}
155
- },
492
+ capabilities: capabilities(),
156
493
  serverInfo: state.serverInfo
157
494
  }
158
495
  };
@@ -161,28 +498,193 @@ function createMCPRequestHandler(state) {
161
498
  "Mcp-Session-Id": sessionId
162
499
  });
163
500
  res.end(JSON.stringify(response));
164
- return;
501
+ return record;
502
+ }
503
+ const resolution = resolveMcpIdentity(req, knownSession);
504
+ if (!resolution.ok) {
505
+ rejectIdentity(res, resolution);
506
+ return record;
165
507
  }
166
- const sessionId = req.headers["mcp-session-id"];
508
+ record.identity = resolution.identity;
509
+ const sessionId = headerSessionId;
167
510
  if (!sessionId) {
168
511
  res.writeHead(400, { "Content-Type": "application/json" });
169
512
  res.end(JSON.stringify({ error: "Missing mcp-session-id header" }));
170
- return;
513
+ return record;
171
514
  }
172
515
  if (!state.sessions.has(sessionId)) {
173
516
  res.writeHead(404, { "Content-Type": "application/json" });
174
517
  res.end(JSON.stringify({ error: "Session not found" }));
175
- return;
518
+ return record;
176
519
  }
520
+ reportFallback(sessionId, req, resolution);
177
521
  if (!state.sessions.get(sessionId).initialized && method !== "notifications/initialized") {
178
522
  res.writeHead(400, { "Content-Type": "application/json" });
179
523
  res.end(JSON.stringify(jsonRpcError(typeof parsed === "object" && parsed !== null && "id" in parsed ? parsed.id : null, -32002, "Session not initialized")));
180
- return;
524
+ return record;
525
+ }
526
+ const notifications = (Array.isArray(parsed) ? parsed : [parsed]).filter((m) => isToolsCall(m) && m.jsonrpc === "2.0").map((m) => !("id" in m));
527
+ const ctx = {
528
+ identity: resolution.identity,
529
+ mount,
530
+ notifications,
531
+ calls: []
532
+ };
533
+ contexts.set(req, ctx);
534
+ const single = singleToolsCall(parsed);
535
+ if (single) {
536
+ const outcome = {
537
+ mcpFake: null,
538
+ error: null
539
+ };
540
+ ctx.calls.push(outcome);
541
+ const fake = callFake(ctx, outcome, single.name, single.args, single.id);
542
+ const replay = fake && outcome.mcpFake?.outcome === "answered" && outcome.mcpFake.id !== null ? state.fakes.replayOf(outcome.mcpFake.id) : null;
543
+ if (fake && "result" in fake && replay && (replay.notifications.length > 0 || replay.timing === "recorded" && replay.durationMs !== void 0)) {
544
+ await writeFakeToolAnswer(req, res, single.id, fake.result, replay, single.params, state.replaySpeed(), logLevels.get(sessionId) ?? null, arrivedAt);
545
+ record.messages = [{
546
+ body: single.message,
547
+ mcpFake: outcome.mcpFake,
548
+ error: outcome.error
549
+ }];
550
+ return record;
551
+ }
552
+ ctx.precomputed = fake ? {
553
+ kind: "reply",
554
+ value: fake
555
+ } : { kind: "none" };
181
556
  }
182
557
  await dispatcher(req, res, body);
558
+ let next = 0;
559
+ const messageOf = (message) => {
560
+ if (!isToolsCall(message)) return {
561
+ body: null,
562
+ mcpFake: null,
563
+ error: null
564
+ };
565
+ const outcome = message.jsonrpc === "2.0" ? ctx.calls[next++] : void 0;
566
+ return {
567
+ body: message,
568
+ mcpFake: outcome?.mcpFake ?? null,
569
+ error: outcome?.error ?? null
570
+ };
571
+ };
572
+ record.messages = Array.isArray(parsed) ? parsed.map(messageOf) : [messageOf(parsed)];
573
+ return record;
574
+ };
575
+ }
576
+ /**
577
+ * A single JSON-RPC `tools/call` request (not a batch, not a notification)
578
+ * with a string or number id and a string tool name, else `null`. Only such
579
+ * a message is claimed by the outer handler (MR15).
580
+ */
581
+ function singleToolsCall(parsed) {
582
+ if (!isToolsCall(parsed) || parsed.jsonrpc !== "2.0" || !("id" in parsed)) return null;
583
+ const id = parsed.id;
584
+ if (typeof id !== "string" && typeof id !== "number") return null;
585
+ const params = parsed.params;
586
+ if (typeof params !== "object" || params === null) return null;
587
+ const name = params.name;
588
+ if (typeof name !== "string" || name === "") return null;
589
+ return {
590
+ message: parsed,
591
+ id,
592
+ name,
593
+ args: params.arguments,
594
+ params
595
+ };
596
+ }
597
+ /** Resolve after `ms` (no wait when `ms <= 0`). */
598
+ function sleep(ms) {
599
+ return ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
600
+ }
601
+ /** The client's `_meta.progressToken` of a request's params, else `undefined`. */
602
+ function progressTokenOf(params) {
603
+ if (typeof params !== "object" || params === null) return void 0;
604
+ const meta = params._meta;
605
+ if (typeof meta !== "object" || meta === null) return void 0;
606
+ const token = meta.progressToken;
607
+ return typeof token === "string" || typeof token === "number" ? token : void 0;
608
+ }
609
+ /**
610
+ * FA3 gate of one recorded notification for this request: the params to send,
611
+ * or `null` when it does not replay. `notifications/progress` replays only
612
+ * when the request carries a progress token, which replaces the recorded one;
613
+ * `notifications/message` only when the session set a log level (C7) and the
614
+ * line is at or above it. No other method replays.
615
+ */
616
+ function replayedParams(notification, progressToken, logLevel) {
617
+ if (notification.method === "notifications/progress") return progressToken === void 0 ? null : {
618
+ ...notification.params,
619
+ progressToken
183
620
  };
621
+ if (notification.method === "notifications/message") {
622
+ const min = logLevelRank(logLevel);
623
+ const rank = logLevelRank(notification.params.level);
624
+ return min !== -1 && rank >= min ? { ...notification.params } : null;
625
+ }
626
+ return null;
627
+ }
628
+ /**
629
+ * Write a fake's tools/call answer. SSE iff the entry has a notification whose
630
+ * FA3 gate passes AND Accept lists text/event-stream; else JSON. Timing per T1
631
+ * (value / speed) unless the block's timing is "immediate". `logLevel` is the
632
+ * session's `logging/setLevel` level (C7), `arrivedAt` the `performance.now()`
633
+ * of the request's arrival, which `atMs` and `durationMs` are measured from.
634
+ */
635
+ async function writeFakeToolAnswer(req, res, id, result, replay, params, speed, logLevel = null, arrivedAt = performance.now()) {
636
+ const timed = replay !== null && replay.timing === "recorded";
637
+ const waitUntil = (atMs) => timed ? sleep(atMs / speed - (performance.now() - arrivedAt)) : Promise.resolve();
638
+ const token = progressTokenOf(params);
639
+ const notifications = [...replay?.notifications ?? []].sort((a, b) => a.atMs - b.atMs).flatMap((n) => {
640
+ const sent = replayedParams(n, token, logLevel);
641
+ return sent ? [{
642
+ atMs: n.atMs,
643
+ method: n.method,
644
+ params: sent
645
+ }] : [];
646
+ });
647
+ const accept = req.headers.accept ?? "";
648
+ const answer = {
649
+ jsonrpc: "2.0",
650
+ id,
651
+ result
652
+ };
653
+ if (notifications.length > 0 && accept.includes("text/event-stream")) {
654
+ res.writeHead(200, {
655
+ "Content-Type": "text/event-stream",
656
+ "Cache-Control": "no-cache"
657
+ });
658
+ res.flushHeaders();
659
+ for (const n of notifications) {
660
+ await waitUntil(n.atMs);
661
+ if (res.destroyed) return;
662
+ res.write(`event: message\ndata: ${JSON.stringify({
663
+ jsonrpc: "2.0",
664
+ method: n.method,
665
+ params: n.params
666
+ })}\n\n`);
667
+ }
668
+ if (replay?.durationMs !== void 0) await waitUntil(replay.durationMs);
669
+ if (res.destroyed) return;
670
+ res.end(`event: message\ndata: ${JSON.stringify(answer)}\n\n`);
671
+ return;
672
+ }
673
+ if (replay?.durationMs !== void 0) await waitUntil(replay.durationMs);
674
+ if (res.destroyed) return;
675
+ res.writeHead(200, { "Content-Type": "application/json" });
676
+ res.end(JSON.stringify(answer));
677
+ }
678
+ /** The identity header of each field whose value can fall back (H1). */
679
+ const IDENTITY_HEADERS = {
680
+ testId: "x-test-id",
681
+ context: "x-aimock-context"
682
+ };
683
+ /** A JSON-RPC message object whose method is `tools/call`. */
684
+ function isToolsCall(message) {
685
+ return typeof message === "object" && message !== null && !Array.isArray(message) && "method" in message && message.method === "tools/call";
184
686
  }
185
687
 
186
688
  //#endregion
187
- export { createMCPRequestHandler };
689
+ export { configToolHandlers, createMCPRequestHandler, describeMcpIdentity, fakeFailure, newMCPRequestRecord, writeFakeToolAnswer };
188
690
  //# sourceMappingURL=mcp-handler.js.map