@yolk-sdk/emulators 0.1.0-canary.96

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 (333) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2135 -0
  3. package/dist/anthropic.d.mts +269 -0
  4. package/dist/anthropic.d.mts.map +1 -0
  5. package/dist/anthropic.mjs +177 -0
  6. package/dist/anthropic.mjs.map +1 -0
  7. package/dist/chat-completions.d.mts +288 -0
  8. package/dist/chat-completions.d.mts.map +1 -0
  9. package/dist/chat-completions.mjs +451 -0
  10. package/dist/chat-completions.mjs.map +1 -0
  11. package/dist/codex.d.mts +265 -0
  12. package/dist/codex.d.mts.map +1 -0
  13. package/dist/codex.mjs +199 -0
  14. package/dist/codex.mjs.map +1 -0
  15. package/dist/dropbox/api.d.mts +63 -0
  16. package/dist/dropbox/api.d.mts.map +1 -0
  17. package/dist/dropbox/api.mjs +565 -0
  18. package/dist/dropbox/api.mjs.map +1 -0
  19. package/dist/dropbox/state.d.mts +136 -0
  20. package/dist/dropbox/state.d.mts.map +1 -0
  21. package/dist/dropbox/state.mjs +209 -0
  22. package/dist/dropbox/state.mjs.map +1 -0
  23. package/dist/dropbox.d.mts +79 -0
  24. package/dist/dropbox.d.mts.map +1 -0
  25. package/dist/dropbox.mjs +124 -0
  26. package/dist/dropbox.mjs.map +1 -0
  27. package/dist/email-fixtures.d.mts +36 -0
  28. package/dist/email-fixtures.d.mts.map +1 -0
  29. package/dist/email-fixtures.mjs +1080 -0
  30. package/dist/email-fixtures.mjs.map +1 -0
  31. package/dist/email.d.mts +160 -0
  32. package/dist/email.d.mts.map +1 -0
  33. package/dist/email.mjs +608 -0
  34. package/dist/email.mjs.map +1 -0
  35. package/dist/emulator-compose.d.mts +40 -0
  36. package/dist/emulator-compose.d.mts.map +1 -0
  37. package/dist/emulator-compose.mjs +91 -0
  38. package/dist/emulator-compose.mjs.map +1 -0
  39. package/dist/emulator-http.d.mts +53 -0
  40. package/dist/emulator-http.d.mts.map +1 -0
  41. package/dist/emulator-http.mjs +117 -0
  42. package/dist/emulator-http.mjs.map +1 -0
  43. package/dist/emulator-kernel.d.mts +176 -0
  44. package/dist/emulator-kernel.d.mts.map +1 -0
  45. package/dist/emulator-kernel.mjs +413 -0
  46. package/dist/emulator-kernel.mjs.map +1 -0
  47. package/dist/fixture-route.d.mts +129 -0
  48. package/dist/fixture-route.d.mts.map +1 -0
  49. package/dist/fixture-route.mjs +257 -0
  50. package/dist/fixture-route.mjs.map +1 -0
  51. package/dist/fortnox/api.d.mts +92 -0
  52. package/dist/fortnox/api.d.mts.map +1 -0
  53. package/dist/fortnox/api.mjs +750 -0
  54. package/dist/fortnox/api.mjs.map +1 -0
  55. package/dist/fortnox/state.d.mts +533 -0
  56. package/dist/fortnox/state.d.mts.map +1 -0
  57. package/dist/fortnox/state.mjs +612 -0
  58. package/dist/fortnox/state.mjs.map +1 -0
  59. package/dist/fortnox.d.mts +128 -0
  60. package/dist/fortnox.d.mts.map +1 -0
  61. package/dist/fortnox.mjs +403 -0
  62. package/dist/fortnox.mjs.map +1 -0
  63. package/dist/gateway-evaluate-recordings.d.mts +12 -0
  64. package/dist/gateway-evaluate-recordings.d.mts.map +1 -0
  65. package/dist/gateway-evaluate-recordings.mjs +128 -0
  66. package/dist/gateway-evaluate-recordings.mjs.map +1 -0
  67. package/dist/gateway.d.mts +272 -0
  68. package/dist/gateway.d.mts.map +1 -0
  69. package/dist/gateway.mjs +400 -0
  70. package/dist/gateway.mjs.map +1 -0
  71. package/dist/github/api.d.mts +63 -0
  72. package/dist/github/api.d.mts.map +1 -0
  73. package/dist/github/api.mjs +527 -0
  74. package/dist/github/api.mjs.map +1 -0
  75. package/dist/github/state.d.mts +272 -0
  76. package/dist/github/state.d.mts.map +1 -0
  77. package/dist/github/state.mjs +303 -0
  78. package/dist/github/state.mjs.map +1 -0
  79. package/dist/github.d.mts +77 -0
  80. package/dist/github.d.mts.map +1 -0
  81. package/dist/github.mjs +180 -0
  82. package/dist/github.mjs.map +1 -0
  83. package/dist/google/calendar.d.mts +10 -0
  84. package/dist/google/calendar.d.mts.map +1 -0
  85. package/dist/google/calendar.mjs +252 -0
  86. package/dist/google/calendar.mjs.map +1 -0
  87. package/dist/google/drive.d.mts +8 -0
  88. package/dist/google/drive.d.mts.map +1 -0
  89. package/dist/google/drive.mjs +252 -0
  90. package/dist/google/drive.mjs.map +1 -0
  91. package/dist/google/gmail.d.mts +10 -0
  92. package/dist/google/gmail.d.mts.map +1 -0
  93. package/dist/google/gmail.mjs +591 -0
  94. package/dist/google/gmail.mjs.map +1 -0
  95. package/dist/google/shared.d.mts +107 -0
  96. package/dist/google/shared.d.mts.map +1 -0
  97. package/dist/google/shared.mjs +146 -0
  98. package/dist/google/shared.mjs.map +1 -0
  99. package/dist/google/state.d.mts +385 -0
  100. package/dist/google/state.d.mts.map +1 -0
  101. package/dist/google/state.mjs +496 -0
  102. package/dist/google/state.mjs.map +1 -0
  103. package/dist/google.d.mts +75 -0
  104. package/dist/google.d.mts.map +1 -0
  105. package/dist/google.mjs +210 -0
  106. package/dist/google.mjs.map +1 -0
  107. package/dist/linkedin-search/api.d.mts +45 -0
  108. package/dist/linkedin-search/api.d.mts.map +1 -0
  109. package/dist/linkedin-search/api.mjs +181 -0
  110. package/dist/linkedin-search/api.mjs.map +1 -0
  111. package/dist/linkedin-search/state.d.mts +123 -0
  112. package/dist/linkedin-search/state.d.mts.map +1 -0
  113. package/dist/linkedin-search/state.mjs +261 -0
  114. package/dist/linkedin-search/state.mjs.map +1 -0
  115. package/dist/linkedin-search.d.mts +69 -0
  116. package/dist/linkedin-search.d.mts.map +1 -0
  117. package/dist/linkedin-search.mjs +158 -0
  118. package/dist/linkedin-search.mjs.map +1 -0
  119. package/dist/mcp/api.d.mts +44 -0
  120. package/dist/mcp/api.d.mts.map +1 -0
  121. package/dist/mcp/api.mjs +557 -0
  122. package/dist/mcp/api.mjs.map +1 -0
  123. package/dist/mcp/recordings.d.mts +40 -0
  124. package/dist/mcp/recordings.d.mts.map +1 -0
  125. package/dist/mcp/recordings.mjs +2390 -0
  126. package/dist/mcp/recordings.mjs.map +1 -0
  127. package/dist/mcp/state.d.mts +71 -0
  128. package/dist/mcp/state.d.mts.map +1 -0
  129. package/dist/mcp/state.mjs +83 -0
  130. package/dist/mcp/state.mjs.map +1 -0
  131. package/dist/mcp.d.mts +84 -0
  132. package/dist/mcp.d.mts.map +1 -0
  133. package/dist/mcp.mjs +241 -0
  134. package/dist/mcp.mjs.map +1 -0
  135. package/dist/messages.d.mts +183 -0
  136. package/dist/messages.d.mts.map +1 -0
  137. package/dist/messages.mjs +532 -0
  138. package/dist/messages.mjs.map +1 -0
  139. package/dist/microsoft/api.d.mts +47 -0
  140. package/dist/microsoft/api.d.mts.map +1 -0
  141. package/dist/microsoft/api.mjs +178 -0
  142. package/dist/microsoft/api.mjs.map +1 -0
  143. package/dist/microsoft/calendar.d.mts +30 -0
  144. package/dist/microsoft/calendar.d.mts.map +1 -0
  145. package/dist/microsoft/calendar.mjs +271 -0
  146. package/dist/microsoft/calendar.mjs.map +1 -0
  147. package/dist/microsoft/drive.d.mts +36 -0
  148. package/dist/microsoft/drive.d.mts.map +1 -0
  149. package/dist/microsoft/drive.mjs +298 -0
  150. package/dist/microsoft/drive.mjs.map +1 -0
  151. package/dist/microsoft/graph.d.mts +198 -0
  152. package/dist/microsoft/graph.d.mts.map +1 -0
  153. package/dist/microsoft/graph.mjs +264 -0
  154. package/dist/microsoft/graph.mjs.map +1 -0
  155. package/dist/microsoft/mail.d.mts +48 -0
  156. package/dist/microsoft/mail.d.mts.map +1 -0
  157. package/dist/microsoft/mail.mjs +488 -0
  158. package/dist/microsoft/mail.mjs.map +1 -0
  159. package/dist/microsoft/state.d.mts +480 -0
  160. package/dist/microsoft/state.d.mts.map +1 -0
  161. package/dist/microsoft/state.mjs +625 -0
  162. package/dist/microsoft/state.mjs.map +1 -0
  163. package/dist/microsoft.d.mts +168 -0
  164. package/dist/microsoft.d.mts.map +1 -0
  165. package/dist/microsoft.mjs +483 -0
  166. package/dist/microsoft.mjs.map +1 -0
  167. package/dist/node.d.mts +47 -0
  168. package/dist/node.d.mts.map +1 -0
  169. package/dist/node.mjs +178 -0
  170. package/dist/node.mjs.map +1 -0
  171. package/dist/notion/api.d.mts +51 -0
  172. package/dist/notion/api.d.mts.map +1 -0
  173. package/dist/notion/api.mjs +507 -0
  174. package/dist/notion/api.mjs.map +1 -0
  175. package/dist/notion/state.d.mts +329 -0
  176. package/dist/notion/state.d.mts.map +1 -0
  177. package/dist/notion/state.mjs +432 -0
  178. package/dist/notion/state.mjs.map +1 -0
  179. package/dist/notion.d.mts +72 -0
  180. package/dist/notion.d.mts.map +1 -0
  181. package/dist/notion.mjs +127 -0
  182. package/dist/notion.mjs.map +1 -0
  183. package/dist/openai.d.mts +165 -0
  184. package/dist/openai.d.mts.map +1 -0
  185. package/dist/openai.mjs +141 -0
  186. package/dist/openai.mjs.map +1 -0
  187. package/dist/opencode-recordings.d.mts +7 -0
  188. package/dist/opencode-recordings.d.mts.map +1 -0
  189. package/dist/opencode-recordings.mjs +222 -0
  190. package/dist/opencode-recordings.mjs.map +1 -0
  191. package/dist/opencode.d.mts +70 -0
  192. package/dist/opencode.d.mts.map +1 -0
  193. package/dist/opencode.mjs +217 -0
  194. package/dist/opencode.mjs.map +1 -0
  195. package/dist/r2-fixtures.d.mts +36 -0
  196. package/dist/r2-fixtures.d.mts.map +1 -0
  197. package/dist/r2-fixtures.mjs +245 -0
  198. package/dist/r2-fixtures.mjs.map +1 -0
  199. package/dist/r2-guard.d.mts +43 -0
  200. package/dist/r2-guard.d.mts.map +1 -0
  201. package/dist/r2-guard.mjs +215 -0
  202. package/dist/r2-guard.mjs.map +1 -0
  203. package/dist/r2.d.mts +166 -0
  204. package/dist/r2.d.mts.map +1 -0
  205. package/dist/r2.mjs +517 -0
  206. package/dist/r2.mjs.map +1 -0
  207. package/dist/responses.d.mts +231 -0
  208. package/dist/responses.d.mts.map +1 -0
  209. package/dist/responses.mjs +556 -0
  210. package/dist/responses.mjs.map +1 -0
  211. package/dist/route-evidence.d.mts +45 -0
  212. package/dist/route-evidence.d.mts.map +1 -0
  213. package/dist/route-evidence.mjs +56 -0
  214. package/dist/route-evidence.mjs.map +1 -0
  215. package/dist/router.d.mts +93 -0
  216. package/dist/router.d.mts.map +1 -0
  217. package/dist/router.mjs +259 -0
  218. package/dist/router.mjs.map +1 -0
  219. package/dist/stateful-core.d.mts +17 -0
  220. package/dist/stateful-core.d.mts.map +1 -0
  221. package/dist/stateful-core.mjs +41 -0
  222. package/dist/stateful-core.mjs.map +1 -0
  223. package/dist/stateful-emulator.d.mts +565 -0
  224. package/dist/stateful-emulator.d.mts.map +1 -0
  225. package/dist/stateful-emulator.mjs +1228 -0
  226. package/dist/stateful-emulator.mjs.map +1 -0
  227. package/dist/stateful-secrets.d.mts +84 -0
  228. package/dist/stateful-secrets.d.mts.map +1 -0
  229. package/dist/stateful-secrets.mjs +216 -0
  230. package/dist/stateful-secrets.mjs.map +1 -0
  231. package/dist/subscription-usage-recordings.d.mts +9 -0
  232. package/dist/subscription-usage-recordings.d.mts.map +1 -0
  233. package/dist/subscription-usage-recordings.mjs +53 -0
  234. package/dist/subscription-usage-recordings.mjs.map +1 -0
  235. package/dist/subscription-usage.d.mts +53 -0
  236. package/dist/subscription-usage.d.mts.map +1 -0
  237. package/dist/subscription-usage.mjs +21 -0
  238. package/dist/subscription-usage.mjs.map +1 -0
  239. package/dist/telegram/api.d.mts +34 -0
  240. package/dist/telegram/api.d.mts.map +1 -0
  241. package/dist/telegram/api.mjs +257 -0
  242. package/dist/telegram/api.mjs.map +1 -0
  243. package/dist/telegram/state.d.mts +128 -0
  244. package/dist/telegram/state.d.mts.map +1 -0
  245. package/dist/telegram/state.mjs +154 -0
  246. package/dist/telegram/state.mjs.map +1 -0
  247. package/dist/telegram.d.mts +62 -0
  248. package/dist/telegram.d.mts.map +1 -0
  249. package/dist/telegram.mjs +127 -0
  250. package/dist/telegram.mjs.map +1 -0
  251. package/dist/todoist/api.d.mts +55 -0
  252. package/dist/todoist/api.d.mts.map +1 -0
  253. package/dist/todoist/api.mjs +415 -0
  254. package/dist/todoist/api.mjs.map +1 -0
  255. package/dist/todoist/state.d.mts +275 -0
  256. package/dist/todoist/state.d.mts.map +1 -0
  257. package/dist/todoist/state.mjs +345 -0
  258. package/dist/todoist/state.mjs.map +1 -0
  259. package/dist/todoist.d.mts +68 -0
  260. package/dist/todoist.d.mts.map +1 -0
  261. package/dist/todoist.mjs +181 -0
  262. package/dist/todoist.mjs.map +1 -0
  263. package/dist/xai.d.mts +267 -0
  264. package/dist/xai.d.mts.map +1 -0
  265. package/dist/xai.mjs +252 -0
  266. package/dist/xai.mjs.map +1 -0
  267. package/package.json +157 -0
  268. package/src/anthropic.ts +289 -0
  269. package/src/chat-completions.ts +924 -0
  270. package/src/codex.ts +296 -0
  271. package/src/dropbox/api.ts +1084 -0
  272. package/src/dropbox/state.ts +364 -0
  273. package/src/dropbox.ts +203 -0
  274. package/src/email-fixtures.ts +1297 -0
  275. package/src/email.ts +1108 -0
  276. package/src/emulator-compose.ts +147 -0
  277. package/src/emulator-http.ts +184 -0
  278. package/src/emulator-kernel.ts +844 -0
  279. package/src/fixture-route.ts +561 -0
  280. package/src/fortnox/api.ts +1352 -0
  281. package/src/fortnox/state.ts +798 -0
  282. package/src/fortnox.ts +801 -0
  283. package/src/gateway-evaluate-recordings.ts +153 -0
  284. package/src/gateway.ts +530 -0
  285. package/src/github/api.ts +986 -0
  286. package/src/github/state.ts +439 -0
  287. package/src/github.ts +271 -0
  288. package/src/google/calendar.ts +486 -0
  289. package/src/google/drive.ts +471 -0
  290. package/src/google/gmail.ts +1011 -0
  291. package/src/google/shared.ts +321 -0
  292. package/src/google/state.ts +686 -0
  293. package/src/google.ts +298 -0
  294. package/src/linkedin-search/api.ts +363 -0
  295. package/src/linkedin-search/state.ts +373 -0
  296. package/src/linkedin-search.ts +241 -0
  297. package/src/mcp/api.ts +995 -0
  298. package/src/mcp/recordings.ts +2665 -0
  299. package/src/mcp/state.ts +125 -0
  300. package/src/mcp.ts +319 -0
  301. package/src/messages.ts +844 -0
  302. package/src/microsoft/api.ts +426 -0
  303. package/src/microsoft/calendar.ts +491 -0
  304. package/src/microsoft/drive.ts +541 -0
  305. package/src/microsoft/graph.ts +550 -0
  306. package/src/microsoft/mail.ts +833 -0
  307. package/src/microsoft/state.ts +822 -0
  308. package/src/microsoft.ts +982 -0
  309. package/src/node.ts +293 -0
  310. package/src/notion/api.ts +976 -0
  311. package/src/notion/state.ts +512 -0
  312. package/src/notion.ts +210 -0
  313. package/src/openai.ts +198 -0
  314. package/src/opencode-recordings.ts +257 -0
  315. package/src/opencode.ts +271 -0
  316. package/src/r2-fixtures.ts +295 -0
  317. package/src/r2-guard.ts +323 -0
  318. package/src/r2.ts +890 -0
  319. package/src/responses.ts +901 -0
  320. package/src/route-evidence.ts +90 -0
  321. package/src/router.ts +490 -0
  322. package/src/stateful-core.ts +70 -0
  323. package/src/stateful-emulator.ts +2571 -0
  324. package/src/stateful-secrets.ts +299 -0
  325. package/src/subscription-usage-recordings.ts +77 -0
  326. package/src/subscription-usage.ts +68 -0
  327. package/src/telegram/api.ts +437 -0
  328. package/src/telegram/state.ts +229 -0
  329. package/src/telegram.ts +227 -0
  330. package/src/todoist/api.ts +736 -0
  331. package/src/todoist/state.ts +456 -0
  332. package/src/todoist.ts +316 -0
  333. package/src/xai.ts +341 -0
@@ -0,0 +1,557 @@
1
+ import { isJsonObject, isNotEmulated, notEmulated, parseJsonText, streamedCommit } from "../stateful-emulator.mjs";
2
+ import { mcpEmulatorFixtures } from "./recordings.mjs";
3
+ import { mcpEmulatorLegacyPath, mcpEmulatorModernPath, mcpEmulatorOrigin, mcpEmulatorSessionPrefix } from "./state.mjs";
4
+ import { Predicate } from "effect";
5
+ //#region src/mcp/api.ts
6
+ /**
7
+ * MCP emulator route table (internal): one wire route per recorded HTTP shape (`POST /modern/mcp`,
8
+ * `POST /legacy/mcp`, `GET /legacy/mcp`), each answering only the JSON-RPC methods its profile's
9
+ * fixtures record, as manifest variants (`RPC <origin><path>#<method>`, plus the `GET` row).
10
+ *
11
+ * A request is admitted only when it equals a recorded request (`src/mcp/recordings.ts`) within
12
+ * the latitude: the MCP headers (`accept`, `content-type`, `mcp-method`, `mcp-protocol-version`,
13
+ * `mcp-name`, `last-event-id`) equal the recorded values or are absent where the recording has
14
+ * none; `mcp-session-id` is present exactly where recorded; the body has exactly the recorded
15
+ * keys; the id is any JSON-RPC id of the accepted form where one is recorded; and `params` equal
16
+ * the recorded ones except for JSON key order and the `_meta` client info's name and version
17
+ * (and, on the second page of the paged listing, the cursor, checked by issuance). Everything
18
+ * else is refused with a constant reason. The answer is the recorded one with only the request id
19
+ * substituted where the recording carries the recorded request id (the top-level `id` of a JSON
20
+ * answer, or of the SSE response event's payload), the session id (minted, or the request's) in
21
+ * the `mcp-session-id` header, and the cursor of the current generation in `nextCursor`.
22
+ *
23
+ * @experimental
24
+ */
25
+ const mcpEmulatorDrillKnobs = [
26
+ "discoverCarriesErrorResponse",
27
+ "discoverWithoutResultType",
28
+ "sessionIdNotVisibleAscii",
29
+ "discoverAnsweredTwice",
30
+ "writeToolMarkedReadOnly",
31
+ "readCallAnswersToolError",
32
+ "invalidCallAnswersRpcError",
33
+ "absentCallAnswersResult",
34
+ "unauthorizedWithoutChallenge"
35
+ ];
36
+ const fieldOf = (value, key) => isJsonObject(value) ? value[key] : void 0;
37
+ const exchangeOf = (fixtureId, index) => {
38
+ const exchange = mcpEmulatorFixtures.find((fixture) => fixture.id === fixtureId)?.exchanges.at(index);
39
+ if (exchange === void 0) throw new Error(`the MCP recordings have no exchange ${index} in ${fixtureId}`);
40
+ return exchange;
41
+ };
42
+ const modern = (caseId) => `${caseId}.modern.synthetic`;
43
+ const legacy = (caseId) => `${caseId}.legacy.synthetic`;
44
+ const recorded = {
45
+ modernDiscover: exchangeOf(modern("mcp.negotiation.era"), 0),
46
+ modernRejected: exchangeOf(modern("mcp.auth.rejected"), 0),
47
+ modernList: exchangeOf(modern("mcp.negotiation.era"), 1),
48
+ modernFirstPage: exchangeOf(modern("mcp.tools.list"), 1),
49
+ modernSecondPage: exchangeOf(modern("mcp.tools.list"), 2),
50
+ modernCallRead: exchangeOf(modern("mcp.tools.call-read"), -1),
51
+ modernCallToolError: exchangeOf(modern("mcp.tools.call-tool-error"), -1),
52
+ modernCallAbsent: exchangeOf(modern("mcp.errors.unknown-tool"), -1),
53
+ legacyDiscover: exchangeOf(legacy("mcp.negotiation.era"), 0),
54
+ legacyRejected: exchangeOf(legacy("mcp.auth.rejected"), 0),
55
+ legacyInitialize: exchangeOf(legacy("mcp.negotiation.era"), 1),
56
+ legacyInitialized: exchangeOf(legacy("mcp.negotiation.era"), 2),
57
+ legacyGet: exchangeOf(legacy("mcp.negotiation.era"), 3),
58
+ legacyList: exchangeOf(legacy("mcp.negotiation.era"), 4),
59
+ legacyCallRead: exchangeOf(legacy("mcp.tools.call-read"), -1),
60
+ legacyCallToolError: exchangeOf(legacy("mcp.tools.call-tool-error"), -1),
61
+ legacyCallAbsent: exchangeOf(legacy("mcp.errors.unknown-tool"), -1)
62
+ };
63
+ /** The cursor the paged listing records between its two pages. */
64
+ const recordedCursor = (() => {
65
+ const cursor = fieldOf(fieldOf(recorded.modernSecondPage.request.body, "params"), "cursor");
66
+ if (!Predicate.isString(cursor)) throw new Error("the paged MCP listing records no cursor");
67
+ return cursor;
68
+ })();
69
+ const casesSending = (path, sends) => [...new Set(mcpEmulatorFixtures.flatMap((fixture) => fixture.endpoint === `https://mcp.example.test${path}` && fixture.exchanges.some((exchange) => sends(exchange.request)) ? [fixture.caseId] : []))];
70
+ const rpcMethodOf = (request) => {
71
+ const method = fieldOf(request.body, "method");
72
+ return Predicate.isString(method) ? method : void 0;
73
+ };
74
+ const rpcRow = (path, method) => ({
75
+ method: "RPC",
76
+ path: `${mcpEmulatorOrigin}${path}#${method}`,
77
+ kind: "connector",
78
+ write: false,
79
+ caseIds: casesSending(path, (request) => request.method === "POST" && rpcMethodOf(request) === method),
80
+ evidence: "unverified"
81
+ });
82
+ const modernMethods = [
83
+ "server/discover",
84
+ "tools/list",
85
+ "tools/call"
86
+ ];
87
+ const legacyMethods = [
88
+ "server/discover",
89
+ "initialize",
90
+ "notifications/initialized",
91
+ "tools/list",
92
+ "tools/call"
93
+ ];
94
+ const modernRows = modernMethods.map((method) => rpcRow(mcpEmulatorModernPath, method));
95
+ const legacyRows = legacyMethods.map((method) => rpcRow(mcpEmulatorLegacyPath, method));
96
+ const getRow = {
97
+ method: "GET",
98
+ path: `${mcpEmulatorOrigin}${mcpEmulatorLegacyPath}`,
99
+ kind: "connector",
100
+ write: false,
101
+ caseIds: casesSending(mcpEmulatorLegacyPath, (request) => request.method === "GET"),
102
+ evidence: "unverified"
103
+ };
104
+ const rowOf = (path, method) => `${mcpEmulatorOrigin}${path}#${method}`;
105
+ const isEventStream = (headers) => headers["content-type"] === "text/event-stream";
106
+ /** A recorded answer as status, headers, and chunks (a JSON body is one chunk; none is none). */
107
+ const answerOf = (response) => ({
108
+ status: response.status,
109
+ headers: response.headers,
110
+ chunks: "chunks" in response ? response.chunks : response.body === "" ? [] : [response.body]
111
+ });
112
+ const isResponseTo = (message, id) => isJsonObject(message) && message["id"] === id && ("result" in message || "error" in message);
113
+ const dataPrefix = "data: ";
114
+ /**
115
+ * `answer` with the JSON-RPC response to `recordedId` (the top-level message of a JSON answer, or
116
+ * the payload of the SSE event that carries it) replaced by `change` of it; every other chunk,
117
+ * line, and event (notifications, `id:` lines) stays byte for byte.
118
+ */
119
+ const mapResponse = (answer, recordedId, change) => {
120
+ const eventStream = isEventStream(answer.headers);
121
+ const chunks = answer.chunks.map((chunk) => {
122
+ if (!eventStream) {
123
+ const message = parseJsonText(chunk);
124
+ return isResponseTo(message, recordedId) ? JSON.stringify(change(message)) : chunk;
125
+ }
126
+ return chunk.split("\n").map((line) => {
127
+ if (!line.startsWith(dataPrefix)) return line;
128
+ const message = parseJsonText(line.slice(6));
129
+ return isResponseTo(message, recordedId) ? `${dataPrefix}${JSON.stringify(change(message))}` : line;
130
+ }).join("\n");
131
+ });
132
+ return {
133
+ ...answer,
134
+ chunks
135
+ };
136
+ };
137
+ const recordedIdOf = (exchange) => {
138
+ const id = fieldOf(exchange.request.body, "id");
139
+ return Predicate.isString(id) || Predicate.isNumber(id) ? id : void 0;
140
+ };
141
+ /** The recorded answer of `exchange` with the request's id where it carries the recorded id. */
142
+ const answerWithId = (exchange, id) => {
143
+ const answer = answerOf(exchange.response);
144
+ const recordedId = recordedIdOf(exchange);
145
+ if (recordedId === void 0 || id === void 0 || id === recordedId) return answer;
146
+ return mapResponse(answer, recordedId, (message) => ({
147
+ ...message,
148
+ id
149
+ }));
150
+ };
151
+ /** `answer` with its `mcp-session-id` header (recorded on SSE answers) set to `session`. */
152
+ const withSession = (answer, session) => answer.headers["mcp-session-id"] === void 0 ? answer : {
153
+ ...answer,
154
+ headers: {
155
+ ...answer.headers,
156
+ "mcp-session-id": session
157
+ }
158
+ };
159
+ /** The response message of an answer to `id` with `change` applied to its `result`. */
160
+ const mapResult = (change) => (message) => {
161
+ const result = message["result"];
162
+ return isJsonObject(result) ? {
163
+ ...message,
164
+ result: change(result)
165
+ } : message;
166
+ };
167
+ /**
168
+ * Where a copied recording is not in the canonical form id substitution relies on: every JSON
169
+ * answer body, and every SSE `data:` payload, must equal `JSON.stringify(JSON.parse(text))`, and
170
+ * every SSE data line must start with `data: `. `makeMcpEmulator` throws on any problem, so a
171
+ * recording copied in another form (escapes such as `\u00e9` or `\/`, numbers such as `1.0`,
172
+ * spacing) fails loudly instead of answering bytes the substitution would rewrite.
173
+ */
174
+ const mcpRecordingProblems = (fixtures) => fixtures.flatMap((fixture) => fixture.exchanges.flatMap(({ response }, index) => {
175
+ const where = `${fixture.id} exchange ${index}`;
176
+ const canonical = (text) => {
177
+ const parsed = parseJsonText(text);
178
+ return parsed !== void 0 && JSON.stringify(parsed) === text;
179
+ };
180
+ if (!("chunks" in response)) return response.body === "" || canonical(response.body) ? [] : [`${where}: the JSON body is not canonical`];
181
+ return response.chunks.flatMap((chunk, at) => chunk.split("\n").flatMap((line) => !line.startsWith("data") || line.startsWith(dataPrefix) && canonical(line.slice(6)) ? [] : [`${where} chunk ${at}: an SSE data line is not canonical`]));
182
+ }));
183
+ /** The MCP headers compared with the recording (absent where the recording has none). */
184
+ const comparedHeaders = [
185
+ "accept",
186
+ "content-type",
187
+ "last-event-id",
188
+ "mcp-method",
189
+ "mcp-name",
190
+ "mcp-protocol-version"
191
+ ];
192
+ const sessionHeader = "mcp-session-id";
193
+ /** The `mcp-*` headers a recording carries; any other (such as `mcp-param-*`) is refused. */
194
+ const recordedMcpHeaders = new Set([
195
+ "mcp-method",
196
+ "mcp-name",
197
+ "mcp-protocol-version",
198
+ sessionHeader
199
+ ]);
200
+ /** Refuse an `mcp-*` header no recording carries (fail closed when the names are unknown). */
201
+ const unrecordedMcpHeader = (request) => request.headerNames === void 0 || request.headerNames.some((name) => name.startsWith("mcp-") && !recordedMcpHeaders.has(name)) ? notEmulated("an mcp-* header no recording carries is not emulated") : void 0;
202
+ const headerProblem = (request, recordedRequest, names) => {
203
+ for (const name of names) if (request.header(name) !== recordedRequest.headers[name]) return notEmulated(`the ${name} header must be the recorded value, or absent where none is`);
204
+ if (request.header(sessionHeader) === void 0 !== (recordedRequest.headers[sessionHeader] === void 0)) return notEmulated("mcp-session-id must be sent exactly where the recording sends one");
205
+ };
206
+ /**
207
+ * A JSON-RPC request id of the emulated form: an integer from 0 to 2^53 - 1, or 1 to 64 printable
208
+ * ASCII characters.
209
+ */
210
+ const isMcpEmulatedRequestId = (id) => Predicate.isNumber(id) && Number.isSafeInteger(id) && id >= 0 || Predicate.isString(id) && /^[\x20-\x7E]{1,64}$/.test(id);
211
+ /** Structural JSON equality, ignoring object key order. */
212
+ const jsonEqual = (left, right) => {
213
+ if (Array.isArray(left)) return Array.isArray(right) && left.length === right.length && left.every((item, index) => {
214
+ const other = right[index];
215
+ return other !== void 0 && jsonEqual(item, other);
216
+ });
217
+ if (isJsonObject(left)) {
218
+ if (!isJsonObject(right)) return false;
219
+ const keys = Object.keys(left);
220
+ return keys.length === Object.keys(right).length && keys.every((key) => {
221
+ const value = left[key];
222
+ const other = right[key];
223
+ return Object.hasOwn(right, key) && value !== void 0 && other !== void 0 && jsonEqual(value, other);
224
+ });
225
+ }
226
+ return left === right;
227
+ };
228
+ const clientInfoKey = "io.modelcontextprotocol/clientInfo";
229
+ const isClientInfo = (value) => isJsonObject(value) && Object.keys(value).length === 2 && Predicate.isString(value["name"]) && value["name"].length > 0 && Predicate.isString(value["version"]) && value["version"].length > 0;
230
+ /**
231
+ * The request params normalised for comparison with `recordedParams`: the `_meta` client info
232
+ * replaced by the recorded one (when it is any non-empty name and version), and with `cursorFree`
233
+ * the cursor replaced by the recorded one (when it is a string; checked by issuance at plan).
234
+ */
235
+ const normalisedParams = (params, recordedParams, cursorFree) => {
236
+ if (!isJsonObject(params)) return notEmulated("params must be the recorded JSON object");
237
+ const meta = params["_meta"];
238
+ const recordedMeta = recordedParams["_meta"];
239
+ let normalised = params;
240
+ if (isJsonObject(recordedMeta) && recordedMeta[clientInfoKey] !== void 0) {
241
+ if (!isJsonObject(meta) || !isClientInfo(meta[clientInfoKey])) return notEmulated("the _meta client info must be a non-empty name and version only");
242
+ normalised = {
243
+ ...normalised,
244
+ _meta: {
245
+ ...meta,
246
+ [clientInfoKey]: recordedMeta[clientInfoKey]
247
+ }
248
+ };
249
+ }
250
+ if (cursorFree) {
251
+ if (!Predicate.isString(params["cursor"])) return notEmulated("the second page must send the cursor the first page issued");
252
+ normalised = {
253
+ ...normalised,
254
+ cursor: recordedCursor
255
+ };
256
+ }
257
+ return normalised;
258
+ };
259
+ /**
260
+ * The first candidate whose recorded request the JSON-RPC POST equals within the latitude, or
261
+ * not emulated. Every candidate records the same method; candidates differ only in their params
262
+ * and `mcp-name` (the recorded tool calls).
263
+ */
264
+ const matchRpc = (request, candidates, options = {}) => {
265
+ const [first] = candidates;
266
+ if (first === void 0) return notEmulated("this JSON-RPC method is not emulated here");
267
+ const firstRequest = recorded[first].request;
268
+ const body = request.json;
269
+ const recordedBody = firstRequest.body;
270
+ if (!isJsonObject(recordedBody)) return notEmulated("this JSON-RPC method is not emulated here");
271
+ const common = unrecordedMcpHeader(request) ?? headerProblem(request, firstRequest, comparedHeaders.filter((name) => name !== "mcp-name"));
272
+ if (common !== void 0) return common;
273
+ if (!isJsonObject(body)) return notEmulated("JSON-RPC batches and non-object bodies are not emulated");
274
+ const keys = Object.keys(body);
275
+ const recordedKeys = Object.keys(recordedBody);
276
+ if (keys.length !== recordedKeys.length || keys.some((key) => !recordedKeys.includes(key))) return notEmulated("the JSON-RPC message must have exactly the recorded members");
277
+ if (body["jsonrpc"] !== "2.0") return notEmulated("jsonrpc must be \"2.0\"");
278
+ const id = body["id"];
279
+ if ("id" in recordedBody && !isMcpEmulatedRequestId(id)) return notEmulated("the request id must be an integer from 0 to 2^53 - 1 or 1 to 64 printable ASCII characters");
280
+ const cursor = options.cursorFree === true ? fieldOf(body["params"], "cursor") : void 0;
281
+ for (const key of candidates) {
282
+ const candidate = recorded[key].request;
283
+ const candidateBody = candidate.body;
284
+ if (!isJsonObject(candidateBody)) continue;
285
+ if (headerProblem(request, candidate, ["mcp-name"]) !== void 0) continue;
286
+ const recordedParams = candidateBody["params"];
287
+ if (recordedParams !== void 0) {
288
+ const params = isJsonObject(recordedParams) ? normalisedParams(body["params"], recordedParams, options.cursorFree === true) : notEmulated("params must be the recorded value");
289
+ if (isNotEmulated(params)) return params;
290
+ if (!jsonEqual(params, recordedParams)) continue;
291
+ }
292
+ return {
293
+ key,
294
+ id: isMcpEmulatedRequestId(id) ? id : void 0,
295
+ cursor: Predicate.isString(cursor) ? cursor : void 0
296
+ };
297
+ }
298
+ return notEmulated("params other than the recorded ones are not emulated");
299
+ };
300
+ /** Refuse any query parameter (no fixture records one). */
301
+ const noQuery = (request) => request.rawQuery === void 0 || request.rawQuery === "" ? void 0 : notEmulated("query parameters are not emulated");
302
+ const isReserved = (request, env) => request.bearerDigest === env.reservedDigest;
303
+ const reservedOnlyOnProbe = "the reserved invalid credential is answered only on the era probe";
304
+ const admission = (variant, admitted, plan) => ({
305
+ variant,
306
+ plan: (state, context) => plan(state, admitted, context.env)
307
+ });
308
+ /** A commit that answers `answer` and writes nothing. */
309
+ const answerOnly = (answer) => streamedCommit(answer);
310
+ const sessionOf = (state, admitted, phase) => state.sessions.find((session) => session.id === admitted.session && session.phase === phase);
311
+ const unknownSession = (phase) => notEmulated(phase === "ready" ? "a request on no ready session of this emulator is not emulated" : "notifications/initialized on no initializing session of this emulator is not emulated");
312
+ /** The JSON-RPC error the era drill adds to the discover answer (another id: the SDK skips it). */
313
+ const drillErrorResponse = JSON.stringify({
314
+ jsonrpc: "2.0",
315
+ id: "yolk-emu-drill",
316
+ error: {
317
+ code: -32603,
318
+ message: "Synthetic drill error."
319
+ }
320
+ });
321
+ /** Drill transforms of an answer, by the recorded exchange it answers. */
322
+ const drilled = (key, answer, id, env) => {
323
+ const drills = env.drills;
324
+ switch (key) {
325
+ case "modernDiscover": {
326
+ let next = answer;
327
+ if (drills.discoverWithoutResultType) next = mapResponse(next, id, mapResult(({ resultType: _resultType, ...result }) => result));
328
+ if (drills.discoverCarriesErrorResponse) next = {
329
+ ...next,
330
+ chunks: next.chunks.map((chunk) => `[${drillErrorResponse},${chunk}]`)
331
+ };
332
+ if (drills.discoverAnsweredTwice) next = {
333
+ ...next,
334
+ headers: {
335
+ ...next.headers,
336
+ "content-type": "text/event-stream"
337
+ },
338
+ chunks: next.chunks.flatMap((chunk) => [`event: message\ndata: ${chunk}\n\n`, `event: message\ndata: ${chunk}\n\n`])
339
+ };
340
+ return next;
341
+ }
342
+ case "modernList":
343
+ case "modernFirstPage":
344
+ case "modernSecondPage":
345
+ case "legacyList": return drills.writeToolMarkedReadOnly ? mapResponse(answer, id, mapResult((result) => {
346
+ const tools = result["tools"];
347
+ return Array.isArray(tools) ? {
348
+ ...result,
349
+ tools: tools.map((tool) => isJsonObject(tool) && tool["name"] === "create_synthetic_note" ? {
350
+ ...tool,
351
+ annotations: { readOnlyHint: true }
352
+ } : tool)
353
+ } : result;
354
+ })) : answer;
355
+ case "modernCallRead":
356
+ case "legacyCallRead": return drills.readCallAnswersToolError ? mapResponse(answer, id, mapResult((result) => ({
357
+ ...result,
358
+ isError: true
359
+ }))) : answer;
360
+ case "modernCallToolError":
361
+ case "legacyCallToolError": return drills.invalidCallAnswersRpcError ? mapResponse(answer, id, () => ({
362
+ jsonrpc: "2.0",
363
+ id,
364
+ error: {
365
+ code: -32602,
366
+ message: "Invalid arguments: noteId must be a string."
367
+ }
368
+ })) : answer;
369
+ case "modernCallAbsent":
370
+ case "legacyCallAbsent": {
371
+ if (!drills.absentCallAnswersResult) return answer;
372
+ const result = {
373
+ content: [{
374
+ type: "text",
375
+ text: "Synthetic drill result."
376
+ }],
377
+ isError: true
378
+ };
379
+ return mapResponse({
380
+ ...answer,
381
+ status: 200
382
+ }, id, () => ({
383
+ jsonrpc: "2.0",
384
+ id,
385
+ result: key === "modernCallAbsent" ? {
386
+ ...result,
387
+ resultType: "complete"
388
+ } : result
389
+ }));
390
+ }
391
+ case "modernRejected":
392
+ case "legacyRejected": {
393
+ if (!drills.unauthorizedWithoutChallenge) return answer;
394
+ const { "www-authenticate": _challenge, ...headers } = answer.headers;
395
+ return {
396
+ ...answer,
397
+ headers
398
+ };
399
+ }
400
+ default: return answer;
401
+ }
402
+ };
403
+ /** The id the answer's response carries: the request's (substituted), else the recorded one. */
404
+ const responseIdOf = (key, id) => id ?? recordedIdOf(recorded[key]);
405
+ /** The recorded answer for `key`, with the request id substituted and any drill applied. */
406
+ const recordedAnswer = (key, id, env) => {
407
+ const answer = answerWithId(recorded[key], id);
408
+ const responseId = responseIdOf(key, id);
409
+ return responseId === void 0 ? answer : drilled(key, answer, responseId, env);
410
+ };
411
+ /** The cursor value issued in the current generation (the recorded one in its first). */
412
+ const cursorFor = (runtime) => (runtime.cursorFirstGeneration ?? runtime.generation) === runtime.generation ? recordedCursor : `${recordedCursor}.g${runtime.generation}`;
413
+ const planModernList = (state, admitted, env) => {
414
+ if (state.modernListing === "one-page") return answerOnly(recordedAnswer("modernList", admitted.id, env));
415
+ const cursor = cursorFor(env.runtime);
416
+ const recordedPage = recordedAnswer("modernFirstPage", admitted.id, env);
417
+ const responseId = responseIdOf("modernFirstPage", admitted.id);
418
+ return streamedCommit(cursor === recordedCursor || responseId === void 0 ? recordedPage : mapResponse(recordedPage, responseId, mapResult((result) => ({
419
+ ...result,
420
+ nextCursor: cursor
421
+ }))), () => {
422
+ env.runtime.cursorFirstGeneration ??= env.runtime.generation;
423
+ env.runtime.issuedCursor = cursor;
424
+ }, [cursor]);
425
+ };
426
+ const planModernSecondPage = (state, admitted, env) => state.modernListing === "two-pages" && env.runtime.issuedCursor !== void 0 && admitted.cursor === env.runtime.issuedCursor ? answerOnly(recordedAnswer("modernSecondPage", admitted.id, env)) : notEmulated("a cursor this emulator did not issue since the last reset or seed is not emulated");
427
+ const planInitialize = (state, admitted, env) => {
428
+ const number = env.runtime.nextSession;
429
+ if (state.sessions.length >= 256 || !Number.isSafeInteger(number + 1)) return notEmulated("the emulator holds as many sessions as it takes");
430
+ const session = env.drills.sessionIdNotVisibleAscii ? `yolk-emu-session ${number}` : `${mcpEmulatorSessionPrefix}${number}`;
431
+ return streamedCommit(withSession(recordedAnswer("legacyInitialize", admitted.id, env), session), () => {
432
+ env.runtime.nextSession = number + 1;
433
+ state.sessions = [...state.sessions, {
434
+ id: session,
435
+ phase: "initializing"
436
+ }];
437
+ }, [session]);
438
+ };
439
+ const planInitialized = (state, admitted, env) => {
440
+ const session = sessionOf(state, admitted, "initializing");
441
+ if (session === void 0) return unknownSession("initializing");
442
+ return streamedCommit(recordedAnswer("legacyInitialized", void 0, env), () => {
443
+ state.sessions = state.sessions.map((held) => held.id === session.id ? {
444
+ id: held.id,
445
+ phase: "ready"
446
+ } : held);
447
+ });
448
+ };
449
+ /** A legacy answer on a ready session: the recorded answer with the session's id. */
450
+ const planOnSession = (key) => (state, admitted, env) => {
451
+ const session = sessionOf(state, admitted, "ready");
452
+ return session === void 0 ? unknownSession("ready") : answerOnly(withSession(recordedAnswer(key, admitted.id, env), session.id));
453
+ };
454
+ const planAnswer = (key) => (_state, admitted, env) => answerOnly(recordedAnswer(key, admitted.id, env));
455
+ const routeEvidenceOf = (method, path, variants) => ({
456
+ method,
457
+ path,
458
+ kind: "connector",
459
+ write: false,
460
+ caseIds: [...new Set(variants.flatMap((variant) => variant.caseIds))],
461
+ evidence: "unverified",
462
+ origin: mcpEmulatorOrigin,
463
+ variants
464
+ });
465
+ const admitted = (matched, request) => ({
466
+ ...matched,
467
+ session: request.header(sessionHeader)
468
+ });
469
+ /** The route table: one wire route per recorded HTTP shape, each with its manifest variants. */
470
+ const mcpApiRoutes = [
471
+ {
472
+ ...routeEvidenceOf("POST", mcpEmulatorModernPath, modernRows),
473
+ body: "json",
474
+ admit: (request, env) => {
475
+ const query = noQuery(request);
476
+ if (query !== void 0) return query;
477
+ const method = fieldOf(request.json, "method");
478
+ const variant = (name) => rowOf(mcpEmulatorModernPath, name);
479
+ if (method === "server/discover") {
480
+ const matched = matchRpc(request, ["modernDiscover"]);
481
+ if (isNotEmulated(matched)) return matched;
482
+ return isReserved(request, env) ? admission(variant(method), admitted(matched, request), planAnswer("modernRejected")) : admission(variant(method), admitted(matched, request), planAnswer("modernDiscover"));
483
+ }
484
+ if (isReserved(request, env)) return notEmulated(reservedOnlyOnProbe);
485
+ if (method === "tools/list") {
486
+ const paged = fieldOf(fieldOf(request.json, "params"), "cursor") !== void 0;
487
+ const matched = paged ? matchRpc(request, ["modernSecondPage"], { cursorFree: true }) : matchRpc(request, ["modernList"]);
488
+ if (isNotEmulated(matched)) return matched;
489
+ return admission(variant(method), admitted(matched, request), paged ? planModernSecondPage : planModernList);
490
+ }
491
+ if (method === "tools/call") {
492
+ const matched = matchRpc(request, [
493
+ "modernCallRead",
494
+ "modernCallToolError",
495
+ "modernCallAbsent"
496
+ ]);
497
+ if (isNotEmulated(matched)) return matched;
498
+ return admission(variant(method), admitted(matched, request), planAnswer(matched.key));
499
+ }
500
+ return notEmulated("this JSON-RPC method is not emulated on the modern profile");
501
+ }
502
+ },
503
+ {
504
+ ...routeEvidenceOf("POST", mcpEmulatorLegacyPath, legacyRows),
505
+ body: "json",
506
+ admit: (request, env) => {
507
+ const query = noQuery(request);
508
+ if (query !== void 0) return query;
509
+ const method = fieldOf(request.json, "method");
510
+ const variant = (name) => rowOf(mcpEmulatorLegacyPath, name);
511
+ if (method === "server/discover") {
512
+ const matched = matchRpc(request, ["legacyDiscover"]);
513
+ if (isNotEmulated(matched)) return matched;
514
+ return isReserved(request, env) ? admission(variant(method), admitted(matched, request), planAnswer("legacyRejected")) : admission(variant(method), admitted(matched, request), planAnswer("legacyDiscover"));
515
+ }
516
+ if (isReserved(request, env)) return notEmulated(reservedOnlyOnProbe);
517
+ const single = (key, name, plan) => {
518
+ const matched = matchRpc(request, [key]);
519
+ return isNotEmulated(matched) ? matched : admission(variant(name), admitted(matched, request), plan);
520
+ };
521
+ if (method === "initialize") return single("legacyInitialize", method, planInitialize);
522
+ if (method === "notifications/initialized") return single("legacyInitialized", method, planInitialized);
523
+ if (method === "tools/list") return single("legacyList", method, planOnSession("legacyList"));
524
+ if (method === "tools/call") {
525
+ const matched = matchRpc(request, [
526
+ "legacyCallRead",
527
+ "legacyCallToolError",
528
+ "legacyCallAbsent"
529
+ ]);
530
+ if (isNotEmulated(matched)) return matched;
531
+ return admission(variant(method), admitted(matched, request), planOnSession(matched.key));
532
+ }
533
+ return notEmulated("this JSON-RPC method is not emulated on the legacy profile");
534
+ }
535
+ },
536
+ {
537
+ ...routeEvidenceOf("GET", mcpEmulatorLegacyPath, [getRow]),
538
+ body: "none",
539
+ admit: (request, env) => {
540
+ const query = noQuery(request);
541
+ if (query !== void 0) return query;
542
+ if (isReserved(request, env)) return notEmulated(reservedOnlyOnProbe);
543
+ const problem = unrecordedMcpHeader(request) ?? headerProblem(request, recorded.legacyGet.request, comparedHeaders);
544
+ if (problem !== void 0) return problem;
545
+ return admission(getRow.path, {
546
+ key: "legacyGet",
547
+ id: void 0,
548
+ cursor: void 0,
549
+ session: request.header(sessionHeader)
550
+ }, planOnSession("legacyGet"));
551
+ }
552
+ }
553
+ ];
554
+ //#endregion
555
+ export { isMcpEmulatedRequestId, mcpApiRoutes, mcpEmulatorDrillKnobs, mcpRecordingProblems };
556
+
557
+ //# sourceMappingURL=api.mjs.map