agentfootprint 12.0.1 → 12.2.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 (296) hide show
  1. package/AGENTS.md +1 -1
  2. package/CAPABILITIES.md +3 -0
  3. package/ai-instructions/claude-code/SKILL.md +1 -1
  4. package/dist/adapters/mcp/agentcoreRuntime/invoke.js +113 -7
  5. package/dist/adapters/mcp/agentcoreRuntime/invoke.js.map +1 -1
  6. package/dist/adapters/mcp/agentcoreRuntime/session.js +21 -21
  7. package/dist/adapters/mcp/agentcoreRuntime/session.js.map +1 -1
  8. package/dist/adapters/mcp/agentcoreRuntime/transport.js +79 -5
  9. package/dist/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
  10. package/dist/core/Agent.js +6 -0
  11. package/dist/core/Agent.js.map +1 -1
  12. package/dist/core/agent/stages/toolCalls.js +4 -0
  13. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  14. package/dist/core/slots/buildToolsSlot.js +8 -0
  15. package/dist/core/slots/buildToolsSlot.js.map +1 -1
  16. package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.d.ts +23 -4
  17. package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.js +112 -6
  18. package/dist/esm/adapters/mcp/agentcoreRuntime/invoke.js.map +1 -1
  19. package/dist/esm/adapters/mcp/agentcoreRuntime/session.d.ts +20 -6
  20. package/dist/esm/adapters/mcp/agentcoreRuntime/session.js +21 -21
  21. package/dist/esm/adapters/mcp/agentcoreRuntime/session.js.map +1 -1
  22. package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js +81 -7
  23. package/dist/esm/adapters/mcp/agentcoreRuntime/transport.js.map +1 -1
  24. package/dist/esm/core/Agent.js +6 -0
  25. package/dist/esm/core/Agent.js.map +1 -1
  26. package/dist/esm/core/agent/stages/toolCalls.js +4 -0
  27. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  28. package/dist/esm/core/slots/buildToolsSlot.d.ts +12 -0
  29. package/dist/esm/core/slots/buildToolsSlot.js +8 -0
  30. package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
  31. package/dist/esm/events/payloads.d.ts +41 -0
  32. package/dist/esm/events/registry.d.ts +3 -1
  33. package/dist/esm/events/registry.js +2 -0
  34. package/dist/esm/events/registry.js.map +1 -1
  35. package/dist/esm/identity/types.d.ts +7 -3
  36. package/dist/esm/identity/types.js.map +1 -1
  37. package/dist/esm/index.d.ts +1 -1
  38. package/dist/esm/index.js +4 -1
  39. package/dist/esm/index.js.map +1 -1
  40. package/dist/esm/lib/boundedJson.d.ts +14 -7
  41. package/dist/esm/lib/boundedJson.js +14 -7
  42. package/dist/esm/lib/boundedJson.js.map +1 -1
  43. package/dist/esm/mcp/callChecks.d.ts +160 -0
  44. package/dist/esm/mcp/callChecks.js +237 -0
  45. package/dist/esm/mcp/callChecks.js.map +1 -0
  46. package/dist/esm/mcp/callLifetime.js +5 -2
  47. package/dist/esm/mcp/callLifetime.js.map +1 -1
  48. package/dist/esm/mcp/clientPort.d.ts +78 -0
  49. package/dist/esm/mcp/clientPort.js +33 -0
  50. package/dist/esm/mcp/clientPort.js.map +1 -0
  51. package/dist/esm/mcp/gatewayTransport.js +13 -3
  52. package/dist/esm/mcp/gatewayTransport.js.map +1 -1
  53. package/dist/esm/mcp/index.d.ts +11 -1
  54. package/dist/esm/mcp/index.js +15 -0
  55. package/dist/esm/mcp/index.js.map +1 -1
  56. package/dist/esm/mcp/listingGenerations.d.ts +54 -0
  57. package/dist/esm/mcp/listingGenerations.js +75 -0
  58. package/dist/esm/mcp/listingGenerations.js.map +1 -0
  59. package/dist/esm/mcp/mcpClient.d.ts +18 -1
  60. package/dist/esm/mcp/mcpClient.js +302 -57
  61. package/dist/esm/mcp/mcpClient.js.map +1 -1
  62. package/dist/esm/mcp/mockMcpClient.js +13 -0
  63. package/dist/esm/mcp/mockMcpClient.js.map +1 -1
  64. package/dist/esm/mcp/pool/boundTool.d.ts +58 -0
  65. package/dist/esm/mcp/pool/boundTool.js +218 -0
  66. package/dist/esm/mcp/pool/boundTool.js.map +1 -0
  67. package/dist/esm/mcp/pool/clientSet.d.ts +73 -0
  68. package/dist/esm/mcp/pool/clientSet.js +152 -0
  69. package/dist/esm/mcp/pool/clientSet.js.map +1 -0
  70. package/dist/esm/mcp/pool/definitions.d.ts +103 -0
  71. package/dist/esm/mcp/pool/definitions.js +168 -0
  72. package/dist/esm/mcp/pool/definitions.js.map +1 -0
  73. package/dist/esm/mcp/pool/errors.d.ts +76 -0
  74. package/dist/esm/mcp/pool/errors.js +91 -0
  75. package/dist/esm/mcp/pool/errors.js.map +1 -0
  76. package/dist/esm/mcp/pool/identityKey.d.ts +57 -0
  77. package/dist/esm/mcp/pool/identityKey.js +94 -0
  78. package/dist/esm/mcp/pool/identityKey.js.map +1 -0
  79. package/dist/esm/mcp/pool/lease.d.ts +36 -0
  80. package/dist/esm/mcp/pool/lease.js +51 -0
  81. package/dist/esm/mcp/pool/lease.js.map +1 -0
  82. package/dist/esm/mcp/pool/listTools.d.ts +33 -0
  83. package/dist/esm/mcp/pool/listTools.js +146 -0
  84. package/dist/esm/mcp/pool/listTools.js.map +1 -0
  85. package/dist/esm/mcp/pool/mcpClientPool.d.ts +121 -0
  86. package/dist/esm/mcp/pool/mcpClientPool.js +151 -0
  87. package/dist/esm/mcp/pool/mcpClientPool.js.map +1 -0
  88. package/dist/esm/mcp/pool/options.d.ts +183 -0
  89. package/dist/esm/mcp/pool/options.js +119 -0
  90. package/dist/esm/mcp/pool/options.js.map +1 -0
  91. package/dist/esm/mcp/pool/plan.d.ts +77 -0
  92. package/dist/esm/mcp/pool/plan.js +134 -0
  93. package/dist/esm/mcp/pool/plan.js.map +1 -0
  94. package/dist/esm/mcp/pool/pool.d.ts +307 -0
  95. package/dist/esm/mcp/pool/pool.js +1071 -0
  96. package/dist/esm/mcp/pool/pool.js.map +1 -0
  97. package/dist/esm/mcp/pool/pooledClient.d.ts +93 -0
  98. package/dist/esm/mcp/pool/pooledClient.js +90 -0
  99. package/dist/esm/mcp/pool/pooledClient.js.map +1 -0
  100. package/dist/esm/mcp/pool/record.d.ts +80 -0
  101. package/dist/esm/mcp/pool/record.js +75 -0
  102. package/dist/esm/mcp/pool/record.js.map +1 -0
  103. package/dist/esm/mcp/pool/sessionEnded.d.ts +24 -0
  104. package/dist/esm/mcp/pool/sessionEnded.js +31 -0
  105. package/dist/esm/mcp/pool/sessionEnded.js.map +1 -0
  106. package/dist/esm/mcp/pool/work.d.ts +116 -0
  107. package/dist/esm/mcp/pool/work.js +213 -0
  108. package/dist/esm/mcp/pool/work.js.map +1 -0
  109. package/dist/esm/mcp/requestGuard.d.ts +70 -0
  110. package/dist/esm/mcp/requestGuard.js +98 -0
  111. package/dist/esm/mcp/requestGuard.js.map +1 -0
  112. package/dist/esm/mcp/requestLedger.d.ts +35 -1
  113. package/dist/esm/mcp/requestLedger.js +76 -3
  114. package/dist/esm/mcp/requestLedger.js.map +1 -1
  115. package/dist/esm/mcp/seriesEvidence.d.ts +10 -6
  116. package/dist/esm/mcp/seriesEvidence.js +16 -7
  117. package/dist/esm/mcp/seriesEvidence.js.map +1 -1
  118. package/dist/esm/mcp/terminate.d.ts +94 -0
  119. package/dist/esm/mcp/terminate.js +184 -0
  120. package/dist/esm/mcp/terminate.js.map +1 -0
  121. package/dist/esm/mcp/throttleRetry.js +71 -7
  122. package/dist/esm/mcp/throttleRetry.js.map +1 -1
  123. package/dist/esm/mcp/toolList.d.ts +90 -0
  124. package/dist/esm/mcp/toolList.js +123 -0
  125. package/dist/esm/mcp/toolList.js.map +1 -0
  126. package/dist/esm/mcp/types.d.ts +197 -3
  127. package/dist/esm/redaction/knownStrings.js +34 -0
  128. package/dist/esm/redaction/knownStrings.js.map +1 -1
  129. package/dist/esm/tool-providers/index.d.ts +2 -2
  130. package/dist/esm/tool-providers/index.js +15 -1
  131. package/dist/esm/tool-providers/index.js.map +1 -1
  132. package/dist/esm/tools/mcpClientUse.d.ts +59 -0
  133. package/dist/esm/tools/mcpClientUse.js +216 -0
  134. package/dist/esm/tools/mcpClientUse.js.map +1 -0
  135. package/dist/esm/tools/toolProvider.d.ts +16 -0
  136. package/dist/esm/tools/toolSessions.d.ts +41 -0
  137. package/dist/esm/tools/toolSessions.js +41 -1
  138. package/dist/esm/tools/toolSessions.js.map +1 -1
  139. package/dist/events/registry.js +2 -0
  140. package/dist/events/registry.js.map +1 -1
  141. package/dist/identity/types.js.map +1 -1
  142. package/dist/index.js +6 -3
  143. package/dist/index.js.map +1 -1
  144. package/dist/lib/boundedJson.js +14 -7
  145. package/dist/lib/boundedJson.js.map +1 -1
  146. package/dist/mcp/callChecks.js +247 -0
  147. package/dist/mcp/callChecks.js.map +1 -0
  148. package/dist/mcp/callLifetime.js +5 -2
  149. package/dist/mcp/callLifetime.js.map +1 -1
  150. package/dist/mcp/clientPort.js +38 -0
  151. package/dist/mcp/clientPort.js.map +1 -0
  152. package/dist/mcp/gatewayTransport.js +13 -3
  153. package/dist/mcp/gatewayTransport.js.map +1 -1
  154. package/dist/mcp/index.js +25 -1
  155. package/dist/mcp/index.js.map +1 -1
  156. package/dist/mcp/listingGenerations.js +80 -0
  157. package/dist/mcp/listingGenerations.js.map +1 -0
  158. package/dist/mcp/mcpClient.js +306 -59
  159. package/dist/mcp/mcpClient.js.map +1 -1
  160. package/dist/mcp/mockMcpClient.js +13 -0
  161. package/dist/mcp/mockMcpClient.js.map +1 -1
  162. package/dist/mcp/pool/boundTool.js +223 -0
  163. package/dist/mcp/pool/boundTool.js.map +1 -0
  164. package/dist/mcp/pool/clientSet.js +157 -0
  165. package/dist/mcp/pool/clientSet.js.map +1 -0
  166. package/dist/mcp/pool/definitions.js +174 -0
  167. package/dist/mcp/pool/definitions.js.map +1 -0
  168. package/dist/mcp/pool/errors.js +96 -0
  169. package/dist/mcp/pool/errors.js.map +1 -0
  170. package/dist/mcp/pool/identityKey.js +99 -0
  171. package/dist/mcp/pool/identityKey.js.map +1 -0
  172. package/dist/mcp/pool/lease.js +55 -0
  173. package/dist/mcp/pool/lease.js.map +1 -0
  174. package/dist/mcp/pool/listTools.js +150 -0
  175. package/dist/mcp/pool/listTools.js.map +1 -0
  176. package/dist/mcp/pool/mcpClientPool.js +155 -0
  177. package/dist/mcp/pool/mcpClientPool.js.map +1 -0
  178. package/dist/mcp/pool/options.js +123 -0
  179. package/dist/mcp/pool/options.js.map +1 -0
  180. package/dist/mcp/pool/plan.js +140 -0
  181. package/dist/mcp/pool/plan.js.map +1 -0
  182. package/dist/mcp/pool/pool.js +1075 -0
  183. package/dist/mcp/pool/pool.js.map +1 -0
  184. package/dist/mcp/pool/pooledClient.js +94 -0
  185. package/dist/mcp/pool/pooledClient.js.map +1 -0
  186. package/dist/mcp/pool/record.js +80 -0
  187. package/dist/mcp/pool/record.js.map +1 -0
  188. package/dist/mcp/pool/sessionEnded.js +35 -0
  189. package/dist/mcp/pool/sessionEnded.js.map +1 -0
  190. package/dist/mcp/pool/work.js +219 -0
  191. package/dist/mcp/pool/work.js.map +1 -0
  192. package/dist/mcp/requestGuard.js +106 -0
  193. package/dist/mcp/requestGuard.js.map +1 -0
  194. package/dist/mcp/requestLedger.js +76 -3
  195. package/dist/mcp/requestLedger.js.map +1 -1
  196. package/dist/mcp/seriesEvidence.js +16 -7
  197. package/dist/mcp/seriesEvidence.js.map +1 -1
  198. package/dist/mcp/terminate.js +193 -0
  199. package/dist/mcp/terminate.js.map +1 -0
  200. package/dist/mcp/throttleRetry.js +71 -7
  201. package/dist/mcp/throttleRetry.js.map +1 -1
  202. package/dist/mcp/toolList.js +128 -0
  203. package/dist/mcp/toolList.js.map +1 -0
  204. package/dist/redaction/knownStrings.js +34 -0
  205. package/dist/redaction/knownStrings.js.map +1 -1
  206. package/dist/tool-providers/index.js +20 -1
  207. package/dist/tool-providers/index.js.map +1 -1
  208. package/dist/tools/mcpClientUse.js +225 -0
  209. package/dist/tools/mcpClientUse.js.map +1 -0
  210. package/dist/tools/toolSessions.js +44 -1
  211. package/dist/tools/toolSessions.js.map +1 -1
  212. package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts +23 -4
  213. package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts.map +1 -1
  214. package/dist/types/adapters/mcp/agentcoreRuntime/session.d.ts +20 -6
  215. package/dist/types/adapters/mcp/agentcoreRuntime/session.d.ts.map +1 -1
  216. package/dist/types/adapters/mcp/agentcoreRuntime/transport.d.ts.map +1 -1
  217. package/dist/types/core/Agent.d.ts.map +1 -1
  218. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  219. package/dist/types/core/slots/buildToolsSlot.d.ts +12 -0
  220. package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
  221. package/dist/types/events/payloads.d.ts +41 -0
  222. package/dist/types/events/payloads.d.ts.map +1 -1
  223. package/dist/types/events/registry.d.ts +3 -1
  224. package/dist/types/events/registry.d.ts.map +1 -1
  225. package/dist/types/identity/types.d.ts +7 -3
  226. package/dist/types/identity/types.d.ts.map +1 -1
  227. package/dist/types/index.d.ts +1 -1
  228. package/dist/types/index.d.ts.map +1 -1
  229. package/dist/types/lib/boundedJson.d.ts +14 -7
  230. package/dist/types/lib/boundedJson.d.ts.map +1 -1
  231. package/dist/types/mcp/callChecks.d.ts +161 -0
  232. package/dist/types/mcp/callChecks.d.ts.map +1 -0
  233. package/dist/types/mcp/callLifetime.d.ts.map +1 -1
  234. package/dist/types/mcp/clientPort.d.ts +79 -0
  235. package/dist/types/mcp/clientPort.d.ts.map +1 -0
  236. package/dist/types/mcp/gatewayTransport.d.ts.map +1 -1
  237. package/dist/types/mcp/index.d.ts +11 -1
  238. package/dist/types/mcp/index.d.ts.map +1 -1
  239. package/dist/types/mcp/listingGenerations.d.ts +55 -0
  240. package/dist/types/mcp/listingGenerations.d.ts.map +1 -0
  241. package/dist/types/mcp/mcpClient.d.ts +18 -1
  242. package/dist/types/mcp/mcpClient.d.ts.map +1 -1
  243. package/dist/types/mcp/mockMcpClient.d.ts.map +1 -1
  244. package/dist/types/mcp/pool/boundTool.d.ts +59 -0
  245. package/dist/types/mcp/pool/boundTool.d.ts.map +1 -0
  246. package/dist/types/mcp/pool/clientSet.d.ts +74 -0
  247. package/dist/types/mcp/pool/clientSet.d.ts.map +1 -0
  248. package/dist/types/mcp/pool/definitions.d.ts +104 -0
  249. package/dist/types/mcp/pool/definitions.d.ts.map +1 -0
  250. package/dist/types/mcp/pool/errors.d.ts +77 -0
  251. package/dist/types/mcp/pool/errors.d.ts.map +1 -0
  252. package/dist/types/mcp/pool/identityKey.d.ts +58 -0
  253. package/dist/types/mcp/pool/identityKey.d.ts.map +1 -0
  254. package/dist/types/mcp/pool/lease.d.ts +37 -0
  255. package/dist/types/mcp/pool/lease.d.ts.map +1 -0
  256. package/dist/types/mcp/pool/listTools.d.ts +34 -0
  257. package/dist/types/mcp/pool/listTools.d.ts.map +1 -0
  258. package/dist/types/mcp/pool/mcpClientPool.d.ts +122 -0
  259. package/dist/types/mcp/pool/mcpClientPool.d.ts.map +1 -0
  260. package/dist/types/mcp/pool/options.d.ts +184 -0
  261. package/dist/types/mcp/pool/options.d.ts.map +1 -0
  262. package/dist/types/mcp/pool/plan.d.ts +78 -0
  263. package/dist/types/mcp/pool/plan.d.ts.map +1 -0
  264. package/dist/types/mcp/pool/pool.d.ts +308 -0
  265. package/dist/types/mcp/pool/pool.d.ts.map +1 -0
  266. package/dist/types/mcp/pool/pooledClient.d.ts +94 -0
  267. package/dist/types/mcp/pool/pooledClient.d.ts.map +1 -0
  268. package/dist/types/mcp/pool/record.d.ts +81 -0
  269. package/dist/types/mcp/pool/record.d.ts.map +1 -0
  270. package/dist/types/mcp/pool/sessionEnded.d.ts +25 -0
  271. package/dist/types/mcp/pool/sessionEnded.d.ts.map +1 -0
  272. package/dist/types/mcp/pool/work.d.ts +117 -0
  273. package/dist/types/mcp/pool/work.d.ts.map +1 -0
  274. package/dist/types/mcp/requestGuard.d.ts +71 -0
  275. package/dist/types/mcp/requestGuard.d.ts.map +1 -0
  276. package/dist/types/mcp/requestLedger.d.ts +35 -1
  277. package/dist/types/mcp/requestLedger.d.ts.map +1 -1
  278. package/dist/types/mcp/seriesEvidence.d.ts +10 -6
  279. package/dist/types/mcp/seriesEvidence.d.ts.map +1 -1
  280. package/dist/types/mcp/terminate.d.ts +95 -0
  281. package/dist/types/mcp/terminate.d.ts.map +1 -0
  282. package/dist/types/mcp/throttleRetry.d.ts.map +1 -1
  283. package/dist/types/mcp/toolList.d.ts +91 -0
  284. package/dist/types/mcp/toolList.d.ts.map +1 -0
  285. package/dist/types/mcp/types.d.ts +197 -3
  286. package/dist/types/mcp/types.d.ts.map +1 -1
  287. package/dist/types/redaction/knownStrings.d.ts.map +1 -1
  288. package/dist/types/tool-providers/index.d.ts +2 -2
  289. package/dist/types/tool-providers/index.d.ts.map +1 -1
  290. package/dist/types/tools/mcpClientUse.d.ts +60 -0
  291. package/dist/types/tools/mcpClientUse.d.ts.map +1 -0
  292. package/dist/types/tools/toolProvider.d.ts +16 -0
  293. package/dist/types/tools/toolProvider.d.ts.map +1 -1
  294. package/dist/types/tools/toolSessions.d.ts +41 -0
  295. package/dist/types/tools/toolSessions.d.ts.map +1 -1
  296. package/package.json +3 -3
@@ -0,0 +1,213 @@
1
+ /**
2
+ * work — an admitted operation's charge, and the work under it until that work
3
+ * has settled.
4
+ *
5
+ * Pattern: one object per unit of work that is at once the request guard
6
+ * (`mcp/requestGuard.ts`) and the count of what is still running.
7
+ * Role: The pool's third budget (`docs/design/2026-10-mcp-client-pool.md`
8
+ * § 5.3). The accounting unit is the admitted LOGICAL operation: a
9
+ * `tools/call`, a listing (one `list(ctx)` for one conversation), an
10
+ * `acquire` and an ending each take one slot at admission. Setup an
11
+ * operation performs for itself — the open of its key's client, the
12
+ * revalidating `tools/list` of a replacement — runs inside that slot.
13
+ *
14
+ * AN ABORT IS NOT SETTLEMENT. A slot is released only when the operation's
15
+ * logical request has its answer AND every piece of work under it has settled:
16
+ * the `send()` it made, a credential vend, an attempt's `fetch`, an AWS `send`,
17
+ * the open it owns. A caller's timeout answers the caller; it frees nothing.
18
+ * Work that never settles keeps its charge — `stats().unsettled` reports it,
19
+ * and the budget refuses new work rather than admit it against room that does
20
+ * not exist. There is no release that returns capacity sooner.
21
+ */
22
+ import { abortErrorOf, linkedController } from '../../lib/abort.js';
23
+ /**
24
+ * One unit of pooled work: its requests are checked against it at every
25
+ * boundary (`check`), and every promise started for it is counted until it
26
+ * settles (`track`). It is SETTLED once its logical part has finished and the
27
+ * count is zero.
28
+ */
29
+ export class Work {
30
+ phase;
31
+ link;
32
+ pending = 0;
33
+ finished = false;
34
+ done = false;
35
+ /** Still running underneath when the caller was answered: it lingered. */
36
+ lingered = false;
37
+ listeners = [];
38
+ /** What `check` asks besides the abort — a call's binding. */
39
+ extra;
40
+ /** What the latest answered request proved, if anything. */
41
+ evidence;
42
+ constructor(
43
+ /** What it is doing now. */
44
+ phase, sources) {
45
+ this.phase = phase;
46
+ this.link = linkedController(...sources);
47
+ }
48
+ /** What aborts its own work: its sources, its deadline, its caller detaching. */
49
+ get signal() {
50
+ return this.link.controller.signal;
51
+ }
52
+ /** True once its logical part finished and everything under it settled. */
53
+ get settled() {
54
+ return this.done;
55
+ }
56
+ /** Its caller was answered while work under it still ran. */
57
+ get unsettledPastAnswer() {
58
+ return this.finished && !this.done;
59
+ }
60
+ /** Abort its work with `reason` (a deadline, a detached caller). Idempotent. */
61
+ abort(reason) {
62
+ if (!this.link.controller.signal.aborted)
63
+ this.link.controller.abort(reason);
64
+ }
65
+ /** Ask `gate` too, each time a request is checked. */
66
+ guardWith(gate) {
67
+ this.extra = gate;
68
+ }
69
+ /**
70
+ * The send gate: refused once its caller was answered (nothing more is sent
71
+ * for an answered operation), once aborted, or when the extra gate refuses.
72
+ */
73
+ check() {
74
+ if (this.finished)
75
+ throw workFinished();
76
+ const { signal } = this;
77
+ if (signal.aborted)
78
+ throw abortErrorOf(signal);
79
+ this.extra?.();
80
+ }
81
+ /** Count `work` until it settles. Never throws; never surfaces its rejection. */
82
+ track(work) {
83
+ if (this.done)
84
+ return;
85
+ this.pending += 1;
86
+ const release = () => {
87
+ this.pending -= 1;
88
+ this.settleIfDone();
89
+ };
90
+ Promise.resolve(work).then(release, release);
91
+ }
92
+ /** Record what a request's answer proved. */
93
+ note(evidence) {
94
+ this.evidence = evidence;
95
+ }
96
+ /**
97
+ * Its logical part is over: the caller has its answer. It settles now, or —
98
+ * when work under it still runs — when that work does.
99
+ */
100
+ finish() {
101
+ if (this.finished)
102
+ return;
103
+ this.finished = true;
104
+ if (this.pending > 0)
105
+ this.lingered = true;
106
+ // Nothing more may be sent for it: `check` refuses from here on. Its signal
107
+ // is left alone — a stream the SDK is still reading is not this work's to cut.
108
+ this.settleIfDone();
109
+ }
110
+ /** Did it linger — answered while work under it ran? Read once it settled. */
111
+ get settledLate() {
112
+ return this.lingered;
113
+ }
114
+ /** Call `listener` once it has settled (at once when it already has). */
115
+ onSettled(listener) {
116
+ if (this.done)
117
+ listener();
118
+ else
119
+ this.listeners.push(listener);
120
+ }
121
+ /** A promise that resolves once it has settled. */
122
+ whenSettled() {
123
+ return new Promise((resolve) => this.onSettled(resolve));
124
+ }
125
+ settleIfDone() {
126
+ if (this.done || !this.finished || this.pending > 0)
127
+ return;
128
+ this.done = true;
129
+ this.link.unlink();
130
+ for (const listener of this.listeners.splice(0)) {
131
+ try {
132
+ listener();
133
+ }
134
+ catch {
135
+ // A listener is the pool's own bookkeeping; one failing must not stop the rest.
136
+ }
137
+ }
138
+ }
139
+ }
140
+ /** The reason a finished operation refuses any later send — a fixed sentence. */
141
+ function workFinished() {
142
+ return Object.assign(new Error('This pooled operation has already been answered.'), {
143
+ name: 'AbortError',
144
+ });
145
+ }
146
+ /** An admitted operation: a unit of work that holds one slot of the budget. */
147
+ export class Operation extends Work {
148
+ kind;
149
+ admittedAt;
150
+ constructor(kind, phase, sources,
151
+ /** When it was admitted, on the pool's clock. */
152
+ admittedAt) {
153
+ super(phase, sources);
154
+ this.kind = kind;
155
+ this.admittedAt = admittedAt;
156
+ }
157
+ }
158
+ /**
159
+ * The pool-wide operation budget: at most `max` operations charged, each from
160
+ * its admission until it has SETTLED (see the module note). Admission is the
161
+ * only way in; settlement the only way out.
162
+ */
163
+ export class OperationBudget {
164
+ max;
165
+ charged = new Set();
166
+ onRelease = [];
167
+ constructor(max) {
168
+ this.max = max;
169
+ }
170
+ /** Operations charged now. */
171
+ get count() {
172
+ return this.charged.size;
173
+ }
174
+ /** Is there a free slot? */
175
+ get hasRoom() {
176
+ return this.charged.size < this.max;
177
+ }
178
+ /**
179
+ * Admit one operation, or `undefined` when the budget is full. The slot is
180
+ * charged from here until the operation settles.
181
+ */
182
+ admit(kind, phase, sources, now) {
183
+ if (!this.hasRoom)
184
+ return undefined;
185
+ const operation = new Operation(kind, phase, sources, now);
186
+ this.charged.add(operation);
187
+ operation.onSettled(() => {
188
+ this.charged.delete(operation);
189
+ for (const listener of [...this.onRelease])
190
+ listener(operation);
191
+ });
192
+ return operation;
193
+ }
194
+ /** Call `listener` each time an operation settles and frees its slot. Returns the unsubscribe. */
195
+ whenReleased(listener) {
196
+ this.onRelease.push(listener);
197
+ return () => {
198
+ const at = this.onRelease.indexOf(listener);
199
+ if (at >= 0)
200
+ this.onRelease.splice(at, 1);
201
+ };
202
+ }
203
+ /** Charged work whose caller was already answered and which has not settled, by what it is doing. */
204
+ unsettled() {
205
+ const counts = { open: 0, listing: 0, call: 0, ending: 0 };
206
+ for (const operation of this.charged) {
207
+ if (operation.unsettledPastAnswer)
208
+ counts[operation.phase] += 1;
209
+ }
210
+ return counts;
211
+ }
212
+ }
213
+ //# sourceMappingURL=work.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work.js","sourceRoot":"","sources":["../../../../src/mcp/pool/work.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * requestGuard — the last check a pooled operation's request meets at every
3
+ * boundary the library controls, and the count of the work under it until
4
+ * that work has settled.
5
+ *
6
+ * Pattern: one object per operation, carried on the request's `init` under a
7
+ * library-private symbol.
8
+ * Role: The send gate of the MCP client pool (`mcp/pool/`). The MCP SDK and
9
+ * the transport wait between a tool call and the network — `send()`
10
+ * awaits its own headers, a gateway vends a credential inside `fetch`,
11
+ * the AWS SDK signs inside `send` — so the last check cannot live in
12
+ * the tool. Each place that waits asks the request's guard after its
13
+ * wait and before it hands the request on:
14
+ *
15
+ * - the watched `send` (`requestLedger.ts`) — the last boundary on stdio,
16
+ * whose `send` writes without waiting;
17
+ * - the pool's own fetch, before the request enters the transport;
18
+ * - the retry series, before every attempt (`throttleRetry.ts`);
19
+ * - the vending fetch, once the credential has arrived (`gatewayTransport.ts`);
20
+ * - the AWS SDK command, after it is signed and before its request handler
21
+ * sends it (`adapters/mcp/agentcoreRuntime/invoke.ts`).
22
+ *
23
+ * A refusal there sends nothing. What no guard can recall is a request already
24
+ * handed on: to the network, or to an app's own `fetch`.
25
+ *
26
+ * The same object COUNTS the work under the request: every promise a boundary
27
+ * starts for it — an attempt's `fetch`, a credential vend, an AWS `send` — is
28
+ * tracked until it settles, whatever the caller was told. An abort is a
29
+ * signal, not proof that the work stopped, so the pool keeps an operation
30
+ * charged until this count says it has.
31
+ *
32
+ * The guard rides `init` under a symbol, so a layer that rebuilds `init` with
33
+ * a spread (`{ ...init, headers }`) carries it on, and nothing reaches the
34
+ * wire: `fetch` reads only the fields it knows. A request with no guard — every
35
+ * request of a client the pool did not build — is exactly what it always was.
36
+ */
37
+ /** @internal The `init` key a pooled request's guard rides under. */
38
+ export declare const REQUEST_GUARD: unique symbol;
39
+ /** What one pooled operation's requests are checked against, and counted by. */
40
+ export interface RequestGuard {
41
+ /**
42
+ * Throw the refusal when the request may not be handed on now — its binding
43
+ * went stale, or its operation was aborted or ran out of time. Synchronous:
44
+ * nothing waits between this check and the hand-off it guards.
45
+ */
46
+ check(): void;
47
+ /** Count `work` as work under this request until it settles. Never throws. */
48
+ track(work: PromiseLike<unknown>): void;
49
+ /** What aborts the operation's own work — linked into each request it makes. */
50
+ readonly signal?: AbortSignal;
51
+ }
52
+ /** The guard `init` carries, or `undefined` — a request no pool sent. Never throws. */
53
+ export declare function guardOf(init: RequestInit | undefined): RequestGuard | undefined;
54
+ /** `init` carrying `guard`: a new object, every other field in place. */
55
+ export declare function withGuard(init: RequestInit | undefined, guard: RequestGuard): RequestInit;
56
+ /**
57
+ * Track `work` under `init`'s guard, when it has one, and hand `work` back
58
+ * unchanged. The one line every boundary uses.
59
+ */
60
+ export declare function trackedBy<T extends PromiseLike<unknown>>(init: RequestInit | undefined, work: T): T;
61
+ /**
62
+ * @internal The call-context key a pooled tool hands its guard down under, to
63
+ * the MCP tool it runs (`callLifetime.ts` · `openCall` issues the call with
64
+ * it). Enumerable, so a wrapper that copies the context keeps it.
65
+ */
66
+ export declare const CALL_GUARD: unique symbol;
67
+ /** `ctx` carrying `guard` for the MCP tool it is handed to. */
68
+ export declare function withCallGuard<C extends object>(ctx: C, guard: RequestGuard): C;
69
+ /** The guard a call's context carries, or `undefined`. Never throws, whatever `ctx` is. */
70
+ export declare function callGuardOf(ctx: unknown): RequestGuard | undefined;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * requestGuard — the last check a pooled operation's request meets at every
3
+ * boundary the library controls, and the count of the work under it until
4
+ * that work has settled.
5
+ *
6
+ * Pattern: one object per operation, carried on the request's `init` under a
7
+ * library-private symbol.
8
+ * Role: The send gate of the MCP client pool (`mcp/pool/`). The MCP SDK and
9
+ * the transport wait between a tool call and the network — `send()`
10
+ * awaits its own headers, a gateway vends a credential inside `fetch`,
11
+ * the AWS SDK signs inside `send` — so the last check cannot live in
12
+ * the tool. Each place that waits asks the request's guard after its
13
+ * wait and before it hands the request on:
14
+ *
15
+ * - the watched `send` (`requestLedger.ts`) — the last boundary on stdio,
16
+ * whose `send` writes without waiting;
17
+ * - the pool's own fetch, before the request enters the transport;
18
+ * - the retry series, before every attempt (`throttleRetry.ts`);
19
+ * - the vending fetch, once the credential has arrived (`gatewayTransport.ts`);
20
+ * - the AWS SDK command, after it is signed and before its request handler
21
+ * sends it (`adapters/mcp/agentcoreRuntime/invoke.ts`).
22
+ *
23
+ * A refusal there sends nothing. What no guard can recall is a request already
24
+ * handed on: to the network, or to an app's own `fetch`.
25
+ *
26
+ * The same object COUNTS the work under the request: every promise a boundary
27
+ * starts for it — an attempt's `fetch`, a credential vend, an AWS `send` — is
28
+ * tracked until it settles, whatever the caller was told. An abort is a
29
+ * signal, not proof that the work stopped, so the pool keeps an operation
30
+ * charged until this count says it has.
31
+ *
32
+ * The guard rides `init` under a symbol, so a layer that rebuilds `init` with
33
+ * a spread (`{ ...init, headers }`) carries it on, and nothing reaches the
34
+ * wire: `fetch` reads only the fields it knows. A request with no guard — every
35
+ * request of a client the pool did not build — is exactly what it always was.
36
+ */
37
+ /** @internal The `init` key a pooled request's guard rides under. */
38
+ export const REQUEST_GUARD = Symbol('agentfootprint.mcp.request-guard');
39
+ /** The guard `init` carries, or `undefined` — a request no pool sent. Never throws. */
40
+ export function guardOf(init) {
41
+ if (init === undefined || init === null || typeof init !== 'object')
42
+ return undefined;
43
+ let guard;
44
+ try {
45
+ guard = init[REQUEST_GUARD];
46
+ }
47
+ catch {
48
+ return undefined;
49
+ }
50
+ if (guard === null || typeof guard !== 'object')
51
+ return undefined;
52
+ return typeof guard.check === 'function' &&
53
+ typeof guard.track === 'function'
54
+ ? guard
55
+ : undefined;
56
+ }
57
+ /** `init` carrying `guard`: a new object, every other field in place. */
58
+ export function withGuard(init, guard) {
59
+ return { ...init, [REQUEST_GUARD]: guard };
60
+ }
61
+ /**
62
+ * Track `work` under `init`'s guard, when it has one, and hand `work` back
63
+ * unchanged. The one line every boundary uses.
64
+ */
65
+ export function trackedBy(init, work) {
66
+ guardOf(init)?.track(work);
67
+ return work;
68
+ }
69
+ // ─── The call's port ─────────────────────────────────────────────────
70
+ /**
71
+ * @internal The call-context key a pooled tool hands its guard down under, to
72
+ * the MCP tool it runs (`callLifetime.ts` · `openCall` issues the call with
73
+ * it). Enumerable, so a wrapper that copies the context keeps it.
74
+ */
75
+ export const CALL_GUARD = Symbol('agentfootprint.mcp.call-guard');
76
+ /** `ctx` carrying `guard` for the MCP tool it is handed to. */
77
+ export function withCallGuard(ctx, guard) {
78
+ return { ...ctx, [CALL_GUARD]: guard };
79
+ }
80
+ /** The guard a call's context carries, or `undefined`. Never throws, whatever `ctx` is. */
81
+ export function callGuardOf(ctx) {
82
+ if (ctx === null || typeof ctx !== 'object')
83
+ return undefined;
84
+ let guard;
85
+ try {
86
+ guard = ctx[CALL_GUARD];
87
+ }
88
+ catch {
89
+ return undefined;
90
+ }
91
+ if (guard === null || typeof guard !== 'object')
92
+ return undefined;
93
+ return typeof guard.check === 'function' &&
94
+ typeof guard.track === 'function'
95
+ ? guard
96
+ : undefined;
97
+ }
98
+ //# sourceMappingURL=requestGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestGuard.js","sourceRoot":"","sources":["../../../src/mcp/requestGuard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,qEAAqE;AACrE,MAAM,CAAC,MAAM,aAAa,GAAkB,MAAM,CAAC,kCAAkC,CAAC,CAAC;AAmBvF,uFAAuF;AACvF,MAAM,UAAU,OAAO,CAAC,IAA6B;IACnD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACtF,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAI,IAAgB,CAAC,aAAa,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAClE,OAAO,OAAQ,KAAsB,CAAC,KAAK,KAAK,UAAU;QACxD,OAAQ,KAAsB,CAAC,KAAK,KAAK,UAAU;QACnD,CAAC,CAAE,KAAsB;QACzB,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,SAAS,CAAC,IAA6B,EAAE,KAAmB;IAC1E,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC,aAAa,CAAC,EAAE,KAAK,EAAiB,CAAC;AAC5D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CACvB,IAA6B,EAC7B,IAAO;IAEP,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,wEAAwE;AAExE;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAkB,MAAM,CAAC,+BAA+B,CAAC,CAAC;AAEjF,+DAA+D;AAC/D,MAAM,UAAU,aAAa,CAAmB,GAAM,EAAE,KAAmB;IACzE,OAAO,EAAE,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,CAAC;AACzC,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC9D,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAI,GAA2C,CAAC,UAAU,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAClE,OAAO,OAAQ,KAAsB,CAAC,KAAK,KAAK,UAAU;QACxD,OAAQ,KAAsB,CAAC,KAAK,KAAK,UAAU;QACnD,CAAC,CAAE,KAAsB;QACzB,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
@@ -67,6 +67,7 @@
67
67
  * has stopped waiting for them.
68
68
  */
69
69
  import type { RequestEvidenceSink } from '../tools/requestEvidence.js';
70
+ import type { RequestGuard } from './requestGuard.js';
70
71
  import { RequestReplay } from './replay.js';
71
72
  /** One call's claim on the request it sends. */
72
73
  export interface RequestTicket {
@@ -83,6 +84,11 @@ export interface ClaimedRequest {
83
84
  readonly cancelled: AbortSignal;
84
85
  /** What this request's series and its call share (`replay.ts` · `RequestReplay`). */
85
86
  readonly replay: RequestReplay;
87
+ /**
88
+ * The send gate of the pooled call this request belongs to (`requestGuard.ts`)
89
+ * — absent for a call no pool made.
90
+ */
91
+ readonly guard?: RequestGuard;
86
92
  }
87
93
  /** How a connection's ledger is set up. */
88
94
  export interface RequestLedgerOptions {
@@ -90,6 +96,16 @@ export interface RequestLedgerOptions {
90
96
  readonly server: string;
91
97
  /** The `'app-authorized'` policy is in force: see the module note. */
92
98
  readonly strict: boolean;
99
+ /**
100
+ * A pooled client's guard for every message that does not carry its own — its
101
+ * open, its listing, its ending (`mcp/pool/`) — and for a `tools/call` that
102
+ * reached `send` without a call carrying a guard: the pool answers that one
103
+ * with a guard that refuses it, so a pooled request is never sent unchecked,
104
+ * under either replay policy. A request it guards is checked before it is
105
+ * sent, and every message's send is counted under it. Absent for a client no
106
+ * pool built.
107
+ */
108
+ readonly guardFor?: (message: unknown) => RequestGuard | undefined;
93
109
  }
94
110
  type JsonRpcId = string | number;
95
111
  /** The request a call's marker was matched to. */
@@ -105,6 +121,8 @@ export interface MatchedRequest {
105
121
  export interface IssuedCall {
106
122
  readonly marker: string;
107
123
  readonly ticket?: RequestTicket;
124
+ /** The pooled call's send gate, when a pool made the call (`requestGuard.ts`). */
125
+ readonly guard?: RequestGuard;
108
126
  /** Set once, by the watched `send`: a marker matches ONE request. */
109
127
  readonly request?: MatchedRequest;
110
128
  }
@@ -117,6 +135,12 @@ export declare class RequestLedger {
117
135
  private readonly sending;
118
136
  /** Matched requests whose JSON-RPC answer is awaited (`strict` only) — by id. */
119
137
  private readonly awaiting;
138
+ /**
139
+ * A pooled call's guard by its request's id, until the call settles: the SDK's
140
+ * `notifications/cancelled` for that request is work started for the call, and
141
+ * is counted under it (`requestGuard.ts`).
142
+ */
143
+ private readonly guards;
120
144
  private initializeId;
121
145
  private initializeAnswered;
122
146
  private markers;
@@ -129,7 +153,7 @@ export declare class RequestLedger {
129
153
  * is where its attempts are filed, when the call carries the run's evidence
130
154
  * port.
131
155
  */
132
- issue(ticket?: RequestTicket): IssuedCall;
156
+ issue(ticket?: RequestTicket, guard?: RequestGuard): IssuedCall;
133
157
  /** `call` settled: forget it, and stop awaiting its request's answer. */
134
158
  settle(call: IssuedCall): void;
135
159
  /** The connection closed: nothing it sent is awaited, and no call is pending, any more. */
@@ -172,6 +196,16 @@ export declare class RequestLedger {
172
196
  claim(body: unknown): ClaimedRequest | undefined;
173
197
  /** One message through the watched transport. */
174
198
  private send;
199
+ /**
200
+ * Any message but a `tools/call`: sent as it always was — unless a pool
201
+ * guards it (`guardFor`), and then a REQUEST is checked first and refused
202
+ * unsent, and every message's send is counted under the guard.
203
+ * Notifications are never refused: a `notifications/cancelled` is the
204
+ * courtesy that tells the server to stop.
205
+ */
206
+ private sendOther;
207
+ /** The guard of the pooled call a `notifications/cancelled` names, if any. */
208
+ private cancelledGuard;
175
209
  /** The `initialize` request: its answer is the one that proves the observer is in place. */
176
210
  private noteInitialize;
177
211
  /** The SDK gave a request up: stop the series still sending it, and stop awaiting its answer. */
@@ -85,6 +85,12 @@ export class RequestLedger {
85
85
  sending = new Map();
86
86
  /** Matched requests whose JSON-RPC answer is awaited (`strict` only) — by id. */
87
87
  awaiting = new Map();
88
+ /**
89
+ * A pooled call's guard by its request's id, until the call settles: the SDK's
90
+ * `notifications/cancelled` for that request is work started for the call, and
91
+ * is counted under it (`requestGuard.ts`).
92
+ */
93
+ guards = new Map();
88
94
  initializeId;
89
95
  initializeAnswered = false;
90
96
  markers = 0;
@@ -99,11 +105,12 @@ export class RequestLedger {
99
105
  * is where its attempts are filed, when the call carries the run's evidence
100
106
  * port.
101
107
  */
102
- issue(ticket) {
108
+ issue(ticket, guard) {
103
109
  this.markers += 1;
104
110
  const call = {
105
111
  marker: `call-${this.markers}`,
106
112
  ...(ticket !== undefined && { ticket }),
113
+ ...(guard !== undefined && { guard }),
107
114
  };
108
115
  this.issued.set(call.marker, call);
109
116
  return call;
@@ -113,6 +120,9 @@ export class RequestLedger {
113
120
  if (this.issued.get(call.marker) === call)
114
121
  this.issued.delete(call.marker);
115
122
  const request = call.request;
123
+ if (request !== undefined && this.guards.get(request.id) === call.guard) {
124
+ this.guards.delete(request.id);
125
+ }
116
126
  if (request !== undefined && this.awaiting.get(request.id) === request.replay) {
117
127
  this.awaiting.delete(request.id);
118
128
  }
@@ -122,6 +132,7 @@ export class RequestLedger {
122
132
  this.issued.clear();
123
133
  this.sending.clear();
124
134
  this.awaiting.clear();
135
+ this.guards.clear();
125
136
  }
126
137
  /**
127
138
  * Whether this connection's answers pass the observer: the `initialize`
@@ -234,6 +245,7 @@ export class RequestLedger {
234
245
  ...(entry.ticket !== undefined && { ticket: entry.ticket }),
235
246
  cancelled: entry.cut.signal,
236
247
  replay: entry.replay,
248
+ ...(entry.guard !== undefined && { guard: entry.guard }),
237
249
  };
238
250
  }
239
251
  /** One message through the watched transport. */
@@ -242,12 +254,24 @@ export class RequestLedger {
242
254
  this.noteInitialize(message);
243
255
  const id = requestIdOf(message);
244
256
  if (id === undefined)
245
- return original(message, options);
257
+ return this.sendOther(original, message, options);
246
258
  // The marker comes out before anything else: the server never sees it.
247
259
  const taken = takeMarker(message);
248
260
  const call = taken.marker === undefined ? undefined : this.issued.get(taken.marker);
249
261
  // One request per marker: a second message carrying it is not this call's.
250
262
  const matched = call !== undefined && call.request === undefined ? call : undefined;
263
+ // A pooled call's send gate, asked here — the last boundary on a transport
264
+ // whose `send` writes without waiting (stdio). Refused, nothing is sent and
265
+ // the marker stays unused. On a pooled connection a tool call without a
266
+ // guarded call of its own is the pool's to answer (`guardFor`), whatever
267
+ // the replay policy.
268
+ const guard = matched?.guard ?? this.options.guardFor?.(message);
269
+ try {
270
+ guard?.check();
271
+ }
272
+ catch (refusal) {
273
+ return Promise.reject(refusal);
274
+ }
251
275
  if (matched === undefined && this.options.strict) {
252
276
  return Promise.reject(new McpReplayUnavailableError('request-unmatched', this.options.server, toolNameOf(message)));
253
277
  }
@@ -262,20 +286,62 @@ export class RequestLedger {
262
286
  ...(matched?.ticket !== undefined && { ticket: matched.ticket }),
263
287
  cut: new AbortController(),
264
288
  replay,
289
+ ...(guard !== undefined && { guard }),
265
290
  };
291
+ if (guard !== undefined)
292
+ this.guards.set(id, guard);
266
293
  this.sending.set(id, entry);
267
294
  const release = () => {
268
295
  if (this.sending.get(id) === entry)
269
296
  this.sending.delete(id);
270
297
  };
271
298
  try {
272
- return Promise.resolve(original(taken.message, options)).finally(release);
299
+ const sent = Promise.resolve(original(taken.message, options)).finally(release);
300
+ guard?.track(sent);
301
+ return sent;
273
302
  }
274
303
  catch (err) {
275
304
  release();
276
305
  throw err;
277
306
  }
278
307
  }
308
+ /**
309
+ * Any message but a `tools/call`: sent as it always was — unless a pool
310
+ * guards it (`guardFor`), and then a REQUEST is checked first and refused
311
+ * unsent, and every message's send is counted under the guard.
312
+ * Notifications are never refused: a `notifications/cancelled` is the
313
+ * courtesy that tells the server to stop.
314
+ */
315
+ sendOther(original, message, options) {
316
+ // A cancellation is counted under the call it cancels; anything else under
317
+ // the pool's guard for the client's current work.
318
+ const guard = this.cancelledGuard(message) ?? this.options.guardFor?.(message);
319
+ if (guard === undefined)
320
+ return original(message, options);
321
+ if (isRequest(message)) {
322
+ try {
323
+ guard.check();
324
+ }
325
+ catch (refusal) {
326
+ return Promise.reject(refusal);
327
+ }
328
+ }
329
+ const sent = Promise.resolve(original(message, options));
330
+ guard.track(sent);
331
+ return sent;
332
+ }
333
+ /** The guard of the pooled call a `notifications/cancelled` names, if any. */
334
+ cancelledGuard(message) {
335
+ if (message === null || typeof message !== 'object' || Array.isArray(message))
336
+ return undefined;
337
+ const { method, params } = message;
338
+ if (method !== CANCELLED || params === null || typeof params !== 'object')
339
+ return undefined;
340
+ const requestId = params.requestId;
341
+ if (typeof requestId !== 'string' && typeof requestId !== 'number')
342
+ return undefined;
343
+ return this.guards.get(requestId);
344
+ }
279
345
  /** The `initialize` request: its answer is the one that proves the observer is in place. */
280
346
  noteInitialize(message) {
281
347
  if (message === null || typeof message !== 'object' || Array.isArray(message))
@@ -321,6 +387,13 @@ function responseIdOf(message) {
321
387
  return undefined;
322
388
  return typeof fields.id === 'string' || typeof fields.id === 'number' ? fields.id : undefined;
323
389
  }
390
+ /** A JSON-RPC request — a method and an id. A notification or a response is not one. */
391
+ function isRequest(message) {
392
+ if (message === null || typeof message !== 'object' || Array.isArray(message))
393
+ return false;
394
+ const { method, id } = message;
395
+ return typeof method === 'string' && (typeof id === 'string' || typeof id === 'number');
396
+ }
324
397
  /** The id of a `tools/call` REQUEST — a response or a notification has none to claim. */
325
398
  function requestIdOf(message) {
326
399
  if (message === null || typeof message !== 'object' || Array.isArray(message))