agentfootprint 12.1.0 → 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 (275) hide show
  1. package/AGENTS.md +1 -1
  2. package/CAPABILITIES.md +3 -1
  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/callLifetime.js +5 -2
  44. package/dist/esm/mcp/callLifetime.js.map +1 -1
  45. package/dist/esm/mcp/clientPort.d.ts +78 -0
  46. package/dist/esm/mcp/clientPort.js +33 -0
  47. package/dist/esm/mcp/clientPort.js.map +1 -0
  48. package/dist/esm/mcp/gatewayTransport.js +13 -3
  49. package/dist/esm/mcp/gatewayTransport.js.map +1 -1
  50. package/dist/esm/mcp/index.d.ts +9 -1
  51. package/dist/esm/mcp/index.js +8 -0
  52. package/dist/esm/mcp/index.js.map +1 -1
  53. package/dist/esm/mcp/mcpClient.d.ts +18 -1
  54. package/dist/esm/mcp/mcpClient.js +203 -43
  55. package/dist/esm/mcp/mcpClient.js.map +1 -1
  56. package/dist/esm/mcp/mockMcpClient.js +13 -0
  57. package/dist/esm/mcp/mockMcpClient.js.map +1 -1
  58. package/dist/esm/mcp/pool/boundTool.d.ts +58 -0
  59. package/dist/esm/mcp/pool/boundTool.js +218 -0
  60. package/dist/esm/mcp/pool/boundTool.js.map +1 -0
  61. package/dist/esm/mcp/pool/clientSet.d.ts +73 -0
  62. package/dist/esm/mcp/pool/clientSet.js +152 -0
  63. package/dist/esm/mcp/pool/clientSet.js.map +1 -0
  64. package/dist/esm/mcp/pool/definitions.d.ts +103 -0
  65. package/dist/esm/mcp/pool/definitions.js +168 -0
  66. package/dist/esm/mcp/pool/definitions.js.map +1 -0
  67. package/dist/esm/mcp/pool/errors.d.ts +76 -0
  68. package/dist/esm/mcp/pool/errors.js +91 -0
  69. package/dist/esm/mcp/pool/errors.js.map +1 -0
  70. package/dist/esm/mcp/pool/identityKey.d.ts +57 -0
  71. package/dist/esm/mcp/pool/identityKey.js +94 -0
  72. package/dist/esm/mcp/pool/identityKey.js.map +1 -0
  73. package/dist/esm/mcp/pool/lease.d.ts +36 -0
  74. package/dist/esm/mcp/pool/lease.js +51 -0
  75. package/dist/esm/mcp/pool/lease.js.map +1 -0
  76. package/dist/esm/mcp/pool/listTools.d.ts +33 -0
  77. package/dist/esm/mcp/pool/listTools.js +146 -0
  78. package/dist/esm/mcp/pool/listTools.js.map +1 -0
  79. package/dist/esm/mcp/pool/mcpClientPool.d.ts +121 -0
  80. package/dist/esm/mcp/pool/mcpClientPool.js +151 -0
  81. package/dist/esm/mcp/pool/mcpClientPool.js.map +1 -0
  82. package/dist/esm/mcp/pool/options.d.ts +183 -0
  83. package/dist/esm/mcp/pool/options.js +119 -0
  84. package/dist/esm/mcp/pool/options.js.map +1 -0
  85. package/dist/esm/mcp/pool/plan.d.ts +77 -0
  86. package/dist/esm/mcp/pool/plan.js +134 -0
  87. package/dist/esm/mcp/pool/plan.js.map +1 -0
  88. package/dist/esm/mcp/pool/pool.d.ts +307 -0
  89. package/dist/esm/mcp/pool/pool.js +1071 -0
  90. package/dist/esm/mcp/pool/pool.js.map +1 -0
  91. package/dist/esm/mcp/pool/pooledClient.d.ts +93 -0
  92. package/dist/esm/mcp/pool/pooledClient.js +90 -0
  93. package/dist/esm/mcp/pool/pooledClient.js.map +1 -0
  94. package/dist/esm/mcp/pool/record.d.ts +80 -0
  95. package/dist/esm/mcp/pool/record.js +75 -0
  96. package/dist/esm/mcp/pool/record.js.map +1 -0
  97. package/dist/esm/mcp/pool/sessionEnded.d.ts +24 -0
  98. package/dist/esm/mcp/pool/sessionEnded.js +31 -0
  99. package/dist/esm/mcp/pool/sessionEnded.js.map +1 -0
  100. package/dist/esm/mcp/pool/work.d.ts +116 -0
  101. package/dist/esm/mcp/pool/work.js +213 -0
  102. package/dist/esm/mcp/pool/work.js.map +1 -0
  103. package/dist/esm/mcp/requestGuard.d.ts +70 -0
  104. package/dist/esm/mcp/requestGuard.js +98 -0
  105. package/dist/esm/mcp/requestGuard.js.map +1 -0
  106. package/dist/esm/mcp/requestLedger.d.ts +35 -1
  107. package/dist/esm/mcp/requestLedger.js +76 -3
  108. package/dist/esm/mcp/requestLedger.js.map +1 -1
  109. package/dist/esm/mcp/seriesEvidence.d.ts +10 -6
  110. package/dist/esm/mcp/seriesEvidence.js +16 -7
  111. package/dist/esm/mcp/seriesEvidence.js.map +1 -1
  112. package/dist/esm/mcp/terminate.d.ts +94 -0
  113. package/dist/esm/mcp/terminate.js +184 -0
  114. package/dist/esm/mcp/terminate.js.map +1 -0
  115. package/dist/esm/mcp/throttleRetry.js +71 -7
  116. package/dist/esm/mcp/throttleRetry.js.map +1 -1
  117. package/dist/esm/mcp/types.d.ts +85 -7
  118. package/dist/esm/redaction/knownStrings.js +34 -0
  119. package/dist/esm/redaction/knownStrings.js.map +1 -1
  120. package/dist/esm/tool-providers/index.d.ts +2 -2
  121. package/dist/esm/tool-providers/index.js +7 -1
  122. package/dist/esm/tool-providers/index.js.map +1 -1
  123. package/dist/esm/tools/mcpClientUse.d.ts +59 -0
  124. package/dist/esm/tools/mcpClientUse.js +216 -0
  125. package/dist/esm/tools/mcpClientUse.js.map +1 -0
  126. package/dist/esm/tools/toolProvider.d.ts +16 -0
  127. package/dist/esm/tools/toolSessions.d.ts +41 -0
  128. package/dist/esm/tools/toolSessions.js +41 -1
  129. package/dist/esm/tools/toolSessions.js.map +1 -1
  130. package/dist/events/registry.js +2 -0
  131. package/dist/events/registry.js.map +1 -1
  132. package/dist/identity/types.js.map +1 -1
  133. package/dist/index.js +6 -3
  134. package/dist/index.js.map +1 -1
  135. package/dist/lib/boundedJson.js +14 -7
  136. package/dist/lib/boundedJson.js.map +1 -1
  137. package/dist/mcp/callLifetime.js +5 -2
  138. package/dist/mcp/callLifetime.js.map +1 -1
  139. package/dist/mcp/clientPort.js +38 -0
  140. package/dist/mcp/clientPort.js.map +1 -0
  141. package/dist/mcp/gatewayTransport.js +13 -3
  142. package/dist/mcp/gatewayTransport.js.map +1 -1
  143. package/dist/mcp/index.js +16 -1
  144. package/dist/mcp/index.js.map +1 -1
  145. package/dist/mcp/mcpClient.js +207 -45
  146. package/dist/mcp/mcpClient.js.map +1 -1
  147. package/dist/mcp/mockMcpClient.js +13 -0
  148. package/dist/mcp/mockMcpClient.js.map +1 -1
  149. package/dist/mcp/pool/boundTool.js +223 -0
  150. package/dist/mcp/pool/boundTool.js.map +1 -0
  151. package/dist/mcp/pool/clientSet.js +157 -0
  152. package/dist/mcp/pool/clientSet.js.map +1 -0
  153. package/dist/mcp/pool/definitions.js +174 -0
  154. package/dist/mcp/pool/definitions.js.map +1 -0
  155. package/dist/mcp/pool/errors.js +96 -0
  156. package/dist/mcp/pool/errors.js.map +1 -0
  157. package/dist/mcp/pool/identityKey.js +99 -0
  158. package/dist/mcp/pool/identityKey.js.map +1 -0
  159. package/dist/mcp/pool/lease.js +55 -0
  160. package/dist/mcp/pool/lease.js.map +1 -0
  161. package/dist/mcp/pool/listTools.js +150 -0
  162. package/dist/mcp/pool/listTools.js.map +1 -0
  163. package/dist/mcp/pool/mcpClientPool.js +155 -0
  164. package/dist/mcp/pool/mcpClientPool.js.map +1 -0
  165. package/dist/mcp/pool/options.js +123 -0
  166. package/dist/mcp/pool/options.js.map +1 -0
  167. package/dist/mcp/pool/plan.js +140 -0
  168. package/dist/mcp/pool/plan.js.map +1 -0
  169. package/dist/mcp/pool/pool.js +1075 -0
  170. package/dist/mcp/pool/pool.js.map +1 -0
  171. package/dist/mcp/pool/pooledClient.js +94 -0
  172. package/dist/mcp/pool/pooledClient.js.map +1 -0
  173. package/dist/mcp/pool/record.js +80 -0
  174. package/dist/mcp/pool/record.js.map +1 -0
  175. package/dist/mcp/pool/sessionEnded.js +35 -0
  176. package/dist/mcp/pool/sessionEnded.js.map +1 -0
  177. package/dist/mcp/pool/work.js +219 -0
  178. package/dist/mcp/pool/work.js.map +1 -0
  179. package/dist/mcp/requestGuard.js +106 -0
  180. package/dist/mcp/requestGuard.js.map +1 -0
  181. package/dist/mcp/requestLedger.js +76 -3
  182. package/dist/mcp/requestLedger.js.map +1 -1
  183. package/dist/mcp/seriesEvidence.js +16 -7
  184. package/dist/mcp/seriesEvidence.js.map +1 -1
  185. package/dist/mcp/terminate.js +193 -0
  186. package/dist/mcp/terminate.js.map +1 -0
  187. package/dist/mcp/throttleRetry.js +71 -7
  188. package/dist/mcp/throttleRetry.js.map +1 -1
  189. package/dist/redaction/knownStrings.js +34 -0
  190. package/dist/redaction/knownStrings.js.map +1 -1
  191. package/dist/tool-providers/index.js +12 -1
  192. package/dist/tool-providers/index.js.map +1 -1
  193. package/dist/tools/mcpClientUse.js +225 -0
  194. package/dist/tools/mcpClientUse.js.map +1 -0
  195. package/dist/tools/toolSessions.js +44 -1
  196. package/dist/tools/toolSessions.js.map +1 -1
  197. package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts +23 -4
  198. package/dist/types/adapters/mcp/agentcoreRuntime/invoke.d.ts.map +1 -1
  199. package/dist/types/adapters/mcp/agentcoreRuntime/session.d.ts +20 -6
  200. package/dist/types/adapters/mcp/agentcoreRuntime/session.d.ts.map +1 -1
  201. package/dist/types/adapters/mcp/agentcoreRuntime/transport.d.ts.map +1 -1
  202. package/dist/types/core/Agent.d.ts.map +1 -1
  203. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  204. package/dist/types/core/slots/buildToolsSlot.d.ts +12 -0
  205. package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
  206. package/dist/types/events/payloads.d.ts +41 -0
  207. package/dist/types/events/payloads.d.ts.map +1 -1
  208. package/dist/types/events/registry.d.ts +3 -1
  209. package/dist/types/events/registry.d.ts.map +1 -1
  210. package/dist/types/identity/types.d.ts +7 -3
  211. package/dist/types/identity/types.d.ts.map +1 -1
  212. package/dist/types/index.d.ts +1 -1
  213. package/dist/types/index.d.ts.map +1 -1
  214. package/dist/types/lib/boundedJson.d.ts +14 -7
  215. package/dist/types/lib/boundedJson.d.ts.map +1 -1
  216. package/dist/types/mcp/callLifetime.d.ts.map +1 -1
  217. package/dist/types/mcp/clientPort.d.ts +79 -0
  218. package/dist/types/mcp/clientPort.d.ts.map +1 -0
  219. package/dist/types/mcp/gatewayTransport.d.ts.map +1 -1
  220. package/dist/types/mcp/index.d.ts +9 -1
  221. package/dist/types/mcp/index.d.ts.map +1 -1
  222. package/dist/types/mcp/mcpClient.d.ts +18 -1
  223. package/dist/types/mcp/mcpClient.d.ts.map +1 -1
  224. package/dist/types/mcp/mockMcpClient.d.ts.map +1 -1
  225. package/dist/types/mcp/pool/boundTool.d.ts +59 -0
  226. package/dist/types/mcp/pool/boundTool.d.ts.map +1 -0
  227. package/dist/types/mcp/pool/clientSet.d.ts +74 -0
  228. package/dist/types/mcp/pool/clientSet.d.ts.map +1 -0
  229. package/dist/types/mcp/pool/definitions.d.ts +104 -0
  230. package/dist/types/mcp/pool/definitions.d.ts.map +1 -0
  231. package/dist/types/mcp/pool/errors.d.ts +77 -0
  232. package/dist/types/mcp/pool/errors.d.ts.map +1 -0
  233. package/dist/types/mcp/pool/identityKey.d.ts +58 -0
  234. package/dist/types/mcp/pool/identityKey.d.ts.map +1 -0
  235. package/dist/types/mcp/pool/lease.d.ts +37 -0
  236. package/dist/types/mcp/pool/lease.d.ts.map +1 -0
  237. package/dist/types/mcp/pool/listTools.d.ts +34 -0
  238. package/dist/types/mcp/pool/listTools.d.ts.map +1 -0
  239. package/dist/types/mcp/pool/mcpClientPool.d.ts +122 -0
  240. package/dist/types/mcp/pool/mcpClientPool.d.ts.map +1 -0
  241. package/dist/types/mcp/pool/options.d.ts +184 -0
  242. package/dist/types/mcp/pool/options.d.ts.map +1 -0
  243. package/dist/types/mcp/pool/plan.d.ts +78 -0
  244. package/dist/types/mcp/pool/plan.d.ts.map +1 -0
  245. package/dist/types/mcp/pool/pool.d.ts +308 -0
  246. package/dist/types/mcp/pool/pool.d.ts.map +1 -0
  247. package/dist/types/mcp/pool/pooledClient.d.ts +94 -0
  248. package/dist/types/mcp/pool/pooledClient.d.ts.map +1 -0
  249. package/dist/types/mcp/pool/record.d.ts +81 -0
  250. package/dist/types/mcp/pool/record.d.ts.map +1 -0
  251. package/dist/types/mcp/pool/sessionEnded.d.ts +25 -0
  252. package/dist/types/mcp/pool/sessionEnded.d.ts.map +1 -0
  253. package/dist/types/mcp/pool/work.d.ts +117 -0
  254. package/dist/types/mcp/pool/work.d.ts.map +1 -0
  255. package/dist/types/mcp/requestGuard.d.ts +71 -0
  256. package/dist/types/mcp/requestGuard.d.ts.map +1 -0
  257. package/dist/types/mcp/requestLedger.d.ts +35 -1
  258. package/dist/types/mcp/requestLedger.d.ts.map +1 -1
  259. package/dist/types/mcp/seriesEvidence.d.ts +10 -6
  260. package/dist/types/mcp/seriesEvidence.d.ts.map +1 -1
  261. package/dist/types/mcp/terminate.d.ts +95 -0
  262. package/dist/types/mcp/terminate.d.ts.map +1 -0
  263. package/dist/types/mcp/throttleRetry.d.ts.map +1 -1
  264. package/dist/types/mcp/types.d.ts +85 -7
  265. package/dist/types/mcp/types.d.ts.map +1 -1
  266. package/dist/types/redaction/knownStrings.d.ts.map +1 -1
  267. package/dist/types/tool-providers/index.d.ts +2 -2
  268. package/dist/types/tool-providers/index.d.ts.map +1 -1
  269. package/dist/types/tools/mcpClientUse.d.ts +60 -0
  270. package/dist/types/tools/mcpClientUse.d.ts.map +1 -0
  271. package/dist/types/tools/toolProvider.d.ts +16 -0
  272. package/dist/types/tools/toolProvider.d.ts.map +1 -1
  273. package/dist/types/tools/toolSessions.d.ts +41 -0
  274. package/dist/types/tools/toolSessions.d.ts.map +1 -1
  275. package/package.json +3 -3
@@ -0,0 +1,51 @@
1
+ /**
2
+ * lease — the app's explicit, bounded hold on one conversation's client.
3
+ *
4
+ * Pattern: a handle that releases at most once, by hand or by `await using`.
5
+ * Role: § 3.3 and § 5.2 of the design. `acquire` is for code that drives a
6
+ * sequence itself, or a host that must keep a conversation's client
7
+ * from eviction for a whole turn (it holds one around `agent.run()`, and
8
+ * the provider then finds the same client). It is the only way to
9
+ * protect a client between calls: there is no hidden grace period.
10
+ *
11
+ * The `acquire` that opened it was the operation; the held lease is not one. It
12
+ * is bounded by `maxLeaseMs`: past it, it is revoked at the next pool
13
+ * interaction and reported, and its next use throws `lease-expired`. Calls
14
+ * already in flight keep their own leases until they settle.
15
+ */
16
+ import { ASYNC_DISPOSE } from '../../strategies/lifecycle.js';
17
+ import { noteRefusal } from './boundTool.js';
18
+ import { listTools } from './listTools.js';
19
+ import { UseFacts } from './record.js';
20
+ /** The lease handle for `held`, on `key`'s client. */
21
+ export function leaseHandle(pool, key, held) {
22
+ const release = () => {
23
+ if (held.released)
24
+ return;
25
+ held.released = true;
26
+ pool.endLease(held);
27
+ pool.interact();
28
+ };
29
+ const handle = {
30
+ async tools(options) {
31
+ pool.interact();
32
+ if (held.revoked || held.released)
33
+ throw pool.refused('lease-expired');
34
+ const facts = new UseFacts(pool.settings.name, 'list');
35
+ try {
36
+ return await listTools(pool, key, options?.signal, facts);
37
+ }
38
+ catch (error) {
39
+ noteRefusal(facts, error);
40
+ throw error;
41
+ }
42
+ },
43
+ release,
44
+ [ASYNC_DISPOSE]: async () => release(),
45
+ };
46
+ // The computed key gives TypeScript a symbol index rather than the one
47
+ // well-known member `AsyncDisposable` names; at runtime it IS
48
+ // `Symbol.asyncDispose` on every engine this package supports.
49
+ return handle;
50
+ }
51
+ //# sourceMappingURL=lease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lease.js","sourceRoot":"","sources":["../../../../src/mcp/pool/lease.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAiBvC,sDAAsD;AACtD,MAAM,UAAU,WAAW,CAAC,IAAU,EAAE,GAAY,EAAE,IAAe;IACnE,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpB,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC,CAAC;IACF,MAAM,MAAM,GAAG;QACb,KAAK,CAAC,KAAK,CAAC,OAA2C;YACrD,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ;gBAAE,MAAM,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YACvE,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACvD,IAAI,CAAC;gBACH,OAAO,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;YAC5D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;gBAC1B,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO;QACP,CAAC,aAAa,CAAC,EAAE,KAAK,IAAmB,EAAE,CAAC,OAAO,EAAE;KACtD,CAAC;IACF,uEAAuE;IACvE,8DAA8D;IAC9D,+DAA+D;IAC/D,OAAO,MAAiC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * listTools — one conversation's tools, bound to it: one listing operation.
3
+ *
4
+ * Pattern: one admitted operation per call, the provider's and the lease's alike.
5
+ * Role: § 3.2 and § 6 of the design. A listing is one `list(ctx)` for one
6
+ * conversation, admitted against `maxOperations`. Its setup — the open
7
+ * of the key's client, the `tools/list` — runs inside its slot.
8
+ *
9
+ * - PER CLIENT: the conversation's own client is opened (or reused) and its own
10
+ * list served, listed again when it cannot be served as it stands.
11
+ * - SHARED, under the app's contract: the plan is resolved and the context
12
+ * computed WITHOUT opening anything; a current entry is served touching no
13
+ * client (`client: 'not-needed'`); a listing already under way for it is
14
+ * joined; otherwise the listing runs on THIS conversation's own client —
15
+ * never in another conversation's session.
16
+ * - THE CALLER'S CONTEXT AFTER EVERY WAIT: the app's names are asked again
17
+ * after a joined listing and after this one's own (`Pool.ensureListed`); a
18
+ * list is served only under the context the caller has at that moment, and
19
+ * a moved context is resolved again — at most three rounds, then refused
20
+ * (`binding-changed`).
21
+ *
22
+ * A refused listing never reaches the model as a tool result: it fails that
23
+ * iteration's tool discovery (`agentfootprint.tools.discovery_failed`), as any
24
+ * failing `ToolProvider` does. A list that cannot be read to its end
25
+ * (`McpToolListIncompleteError`, from the client's own discovery) is never
26
+ * served cut short.
27
+ */
28
+ import type { Tool } from '../../tools/tools.js';
29
+ import type { PoolKey } from './identityKey.js';
30
+ import type { Pool } from './pool.js';
31
+ import type { UseFacts } from './record.js';
32
+ /** List `key`'s conversation's tools, bound to it, under one admitted operation. */
33
+ export declare function listTools(pool: Pool, key: PoolKey, signal: AbortSignal | undefined, facts: UseFacts): Promise<readonly Tool[]>;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * listTools — one conversation's tools, bound to it: one listing operation.
3
+ *
4
+ * Pattern: one admitted operation per call, the provider's and the lease's alike.
5
+ * Role: § 3.2 and § 6 of the design. A listing is one `list(ctx)` for one
6
+ * conversation, admitted against `maxOperations`. Its setup — the open
7
+ * of the key's client, the `tools/list` — runs inside its slot.
8
+ *
9
+ * - PER CLIENT: the conversation's own client is opened (or reused) and its own
10
+ * list served, listed again when it cannot be served as it stands.
11
+ * - SHARED, under the app's contract: the plan is resolved and the context
12
+ * computed WITHOUT opening anything; a current entry is served touching no
13
+ * client (`client: 'not-needed'`); a listing already under way for it is
14
+ * joined; otherwise the listing runs on THIS conversation's own client —
15
+ * never in another conversation's session.
16
+ * - THE CALLER'S CONTEXT AFTER EVERY WAIT: the app's names are asked again
17
+ * after a joined listing and after this one's own (`Pool.ensureListed`); a
18
+ * list is served only under the context the caller has at that moment, and
19
+ * a moved context is resolved again — at most three rounds, then refused
20
+ * (`binding-changed`).
21
+ *
22
+ * A refused listing never reaches the model as a tool result: it fails that
23
+ * iteration's tool discovery (`agentfootprint.tools.discovery_failed`), as any
24
+ * failing `ToolProvider` does. A list that cannot be read to its end
25
+ * (`McpToolListIncompleteError`, from the client's own discovery) is never
26
+ * served cut short.
27
+ */
28
+ import { untilAborted } from '../../lib/abort.js';
29
+ import { isLocalSessionHandleLost, LocalSessionHandleLostError } from '../../tools/toolSessions.js';
30
+ import { ServerEndedSessionError } from './sessionEnded.js';
31
+ import { boundTool } from './boundTool.js';
32
+ import { contextFor, resolvePlan } from './plan.js';
33
+ /** List `key`'s conversation's tools, bound to it, under one admitted operation. */
34
+ export async function listTools(pool, key, signal, facts) {
35
+ facts.keyHash = key.keyHash;
36
+ pool.interact();
37
+ const operation = pool.admit('listing', 'listing', signal);
38
+ try {
39
+ for (let round = 0; round < 3; round += 1) {
40
+ const current = pool.clients.currentFor(key.key);
41
+ let plan;
42
+ if (current === undefined) {
43
+ try {
44
+ plan = resolvePlan(pool.settings, key);
45
+ }
46
+ catch (cause) {
47
+ throw pool.refused('init-failed', cause);
48
+ }
49
+ }
50
+ // A key with a client: the app's names asked again, so a changed
51
+ // authorization reaches this conversation as it would a new one.
52
+ const definitions = current === undefined ? plan : pool.refreshContext(current);
53
+ if (definitions.shared) {
54
+ const served = await sharedWithoutClient(pool, key, definitions.context, operation.signal);
55
+ if (served !== undefined) {
56
+ // After that wait, the caller's names once more: served only under
57
+ // the context they give now — otherwise resolved again.
58
+ if (!callerStillIn(pool, key, plan, served.context))
59
+ continue;
60
+ facts.client = 'not-needed';
61
+ facts.definitions = 'shared';
62
+ facts.definitionsAgeMs = Math.max(0, pool.now() - served.list.listedAt);
63
+ return bindAll(pool, key, served, undefined);
64
+ }
65
+ }
66
+ const client = await pool.leaseClient(key, operation, facts, plan);
67
+ let served;
68
+ try {
69
+ // Under the client's context of now, checked with no wait since.
70
+ served = await pool.ensureListed(client, operation);
71
+ }
72
+ catch (error) {
73
+ throw classifiedListing(pool, client, facts, error);
74
+ }
75
+ facts.definitions = served.shared ? 'shared' : 'listed';
76
+ if (served.shared)
77
+ facts.definitionsAgeMs = Math.max(0, pool.now() - served.list.listedAt);
78
+ return bindAll(pool, key, served, client);
79
+ }
80
+ // The app's names kept moving under this listing.
81
+ throw pool.refused('binding-changed');
82
+ }
83
+ finally {
84
+ operation.finish();
85
+ }
86
+ }
87
+ /**
88
+ * Do the caller's names still give `context` — asked now? Through the key's
89
+ * client when it has one by now, else over the plan's configuration. False
90
+ * when `share` cannot answer: the next round says why.
91
+ */
92
+ function callerStillIn(pool, key, plan, context) {
93
+ const current = pool.clients.currentFor(key.key);
94
+ if (current !== undefined)
95
+ return pool.contextHolds(current, context);
96
+ if (plan === undefined)
97
+ return false;
98
+ try {
99
+ return contextFor(pool.settings, key, plan.configHash).context === context;
100
+ }
101
+ catch {
102
+ return false;
103
+ }
104
+ }
105
+ /**
106
+ * The shared entry for `plan`, when it can be served without touching a client
107
+ * — at once, or once a listing already under way for it is done.
108
+ */
109
+ async function sharedWithoutClient(pool, key, context, signal) {
110
+ const entry = pool.shared.entryFor(context, key.tenant);
111
+ const servable = () => entry.list.servable(pool.now(), pool.settings.definitionTtlMs);
112
+ if (!servable() && entry.list.listing !== undefined) {
113
+ // Joined inside this operation's own slot; if it fails, this one lists itself.
114
+ await untilAborted(entry.list.listing, signal).catch(() => undefined);
115
+ }
116
+ return servable() ? { list: entry.list, shared: true, context } : undefined;
117
+ }
118
+ /** Every definition of `served`, bound to `key` and to the list it came from. */
119
+ function bindAll(pool, key, served, client) {
120
+ const base = {
121
+ key,
122
+ context: served.context,
123
+ shared: served.shared,
124
+ listId: served.list.id,
125
+ generation: served.list.generation,
126
+ ...(!served.shared && client !== undefined && { clientId: client.clientId }),
127
+ };
128
+ return served.list.all().map((definition) => boundTool(pool, { ...base, definition }));
129
+ }
130
+ /** A listing that failed because the client's session expired or its handle was lost. */
131
+ function classifiedListing(pool, client, facts, error) {
132
+ if (client.lost === 'expired') {
133
+ facts.client = 'expired';
134
+ facts.evidence = 'server-404';
135
+ return new ServerEndedSessionError(pool.settings.name, client.clientId, error);
136
+ }
137
+ if (client.lost === 'handle-lost') {
138
+ facts.client = 'handle-lost';
139
+ facts.evidence = 'transport-forgot-handle';
140
+ return isLocalSessionHandleLost(error)
141
+ ? error
142
+ : new LocalSessionHandleLostError({ cause: error });
143
+ }
144
+ return error;
145
+ }
146
+ //# sourceMappingURL=listTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listTools.js","sourceRoot":"","sources":["../../../../src/mcp/pool/listTools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * mcpClientPool — a client per conversation, for one MCP server.
3
+ *
4
+ * const orders = mcpClientPool({
5
+ * name: 'orders',
6
+ * transport: () =>
7
+ * agentCoreRuntimeMcpTransport({ agentRuntimeArn, region, session: 'reuse', client: aws }),
8
+ * });
9
+ * const agent = Agent.create({ provider, model }).toolProvider(orders.toolProvider()).build();
10
+ * await agent.run({ message, identity: { tenant, principal, conversationId } });
11
+ *
12
+ * Pattern: Facade over one state machine (`pool.ts`), its accounting (`work.ts`),
13
+ * its definitions (`definitions.ts`) and its bound tools (`boundTool.ts`).
14
+ * Role: `docs/design/2026-10-mcp-client-pool.md`. A `'reuse'` session costs a
15
+ * cold start once per conversation instead of once per call — but only
16
+ * if each conversation has a client of its own, opened once, bounded,
17
+ * never closed while in use, ended remotely and said to be. This is
18
+ * the one place that does all of it:
19
+ *
20
+ * - one client per authenticated conversation, keyed by the library's
21
+ * identity encoding of the identity the CALLER named;
22
+ * - one open per key, under the pool's own signal and `initTimeoutMs`;
23
+ * - three budgets — open clients, endings, outstanding operations — each
24
+ * charged until the work under it has SETTLED, and `PoolFullError` at once
25
+ * when one is full: there is no queue;
26
+ * - tool definitions per client incarnation by default, shared only under the
27
+ * app's declared contract, every tool re-checked after each wait and at the
28
+ * send gate;
29
+ * - every ending through `terminate()`, with the remote outcome reported, and
30
+ * `close()` still only local.
31
+ *
32
+ * What it does not do: repeat a call, share anything across processes, bound a
33
+ * remote session it could not end, or make a server enforce per-user
34
+ * permissions.
35
+ */
36
+ import type { ToolProvider } from '../../tools/toolProvider.js';
37
+ import type { McpTerminateOutcome } from '../types.js';
38
+ import { type McpPoolLease } from './lease.js';
39
+ import { type McpClientPoolOptions, type McpPoolIdentity } from './options.js';
40
+ import { type McpPoolStats } from './pool.js';
41
+ /** The scope `terminate` and `permissionsChanged` name. */
42
+ export interface McpPoolScope {
43
+ /** Matched exactly, always: an absent tenant is "no tenant", never "every tenant". */
44
+ readonly tenant?: string;
45
+ /** Absent: every principal of the tenant (`permissionsChanged` only). */
46
+ readonly principal?: string;
47
+ /** Absent: every conversation of the principal. */
48
+ readonly conversationId?: string;
49
+ }
50
+ /** One pool per MCP server: a client per conversation. */
51
+ export interface McpClientPool {
52
+ /** The pool's name: the server. */
53
+ readonly name: string;
54
+ /**
55
+ * The agent path: a `ToolProvider` (id `mcp-pool:<name>`) whose `list(ctx)`
56
+ * takes the run's caller identity (`ToolDispatchContext.callerIdentity`) and
57
+ * returns that conversation's tools, bound to it. A bound tool's `execute`
58
+ * takes a lease on the key's client for one call, opening the client first
59
+ * when the key has none; nothing is held between calls. A run with no caller
60
+ * identity is refused (`no-identity`) — a derived one counts as none.
61
+ */
62
+ toolProvider(): ToolProvider;
63
+ /**
64
+ * Hold `identity`'s conversation client — opened or reused — until
65
+ * `release()`, at most `maxLeaseMs`. The only way to protect a client between
66
+ * calls (a host holds one around `agent.run()`).
67
+ *
68
+ * @throws `McpPoolRefusedError` (`no-identity`, `incomplete-identity`,
69
+ * `init-timeout`, `init-failed`, `closed`) or `PoolFullError`
70
+ */
71
+ acquire(identity: McpPoolIdentity, options?: {
72
+ readonly signal?: AbortSignal;
73
+ }): Promise<McpPoolLease>;
74
+ /**
75
+ * End the conversations `scope` names — one (`{ tenant, principal,
76
+ * conversationId }`) or every one of a principal (`{ tenant, principal }`).
77
+ * New leases stop at once; the next acquire for a key opens a new client.
78
+ * In-flight work is waited for up to 5 s; then each client is ended through
79
+ * `terminate()` once an ending slot and an operation slot are reserved. A
80
+ * client still leased, or whose slots cannot be reserved yet, is reported
81
+ * deferred (`onEvent`), never as ended, and is ended when it can be.
82
+ *
83
+ * @returns one `McpTerminateOutcome` per client it ended
84
+ */
85
+ terminate(scope: McpPoolScope): Promise<readonly McpTerminateOutcome[]>;
86
+ /**
87
+ * Shut down: refuse new work, revoke app leases, abort in-flight calls
88
+ * through the pool's own signal, then end every client as slots can be
89
+ * reserved, at most `maxEnding` at a time. Resolves after a bounded wait;
90
+ * the clients it could not end yet are reported deferred and the work still
91
+ * unsettled stays charged until it settles (`stats()`). Idempotent.
92
+ *
93
+ * @returns one `McpTerminateOutcome` per client it ended
94
+ */
95
+ close(): Promise<readonly McpTerminateOutcome[]>;
96
+ /** Resolves once every charged operation and every ending has settled. */
97
+ settled(): Promise<void>;
98
+ /**
99
+ * The app's permission signal: every tool list in the scope stops being
100
+ * served, and every tool bound to one is refused (`stale-tools`) before it is
101
+ * sent. Per process: a fleet sends it to every process, or versions its
102
+ * `authorization` names.
103
+ */
104
+ permissionsChanged(scope: {
105
+ readonly tenant?: string;
106
+ readonly principal?: string;
107
+ }): void;
108
+ /** The server changed (a deploy hook): every tool list stops being served. */
109
+ toolsChanged(): void;
110
+ /** Run the lazy sweep now: revoked leases, idle ends, deferred ends. No timer is ever installed. */
111
+ sweep(): void;
112
+ /** Counts only — never keys. They report; the budgets enforce. */
113
+ stats(): McpPoolStats;
114
+ }
115
+ /**
116
+ * Build a pool for one MCP server.
117
+ *
118
+ * @throws TypeError when an option is missing, mistyped or out of range, or when
119
+ * `connection` is given — here, when the pool is built
120
+ */
121
+ export declare function mcpClientPool(options: McpClientPoolOptions): McpClientPool;
@@ -0,0 +1,151 @@
1
+ /**
2
+ * mcpClientPool — a client per conversation, for one MCP server.
3
+ *
4
+ * const orders = mcpClientPool({
5
+ * name: 'orders',
6
+ * transport: () =>
7
+ * agentCoreRuntimeMcpTransport({ agentRuntimeArn, region, session: 'reuse', client: aws }),
8
+ * });
9
+ * const agent = Agent.create({ provider, model }).toolProvider(orders.toolProvider()).build();
10
+ * await agent.run({ message, identity: { tenant, principal, conversationId } });
11
+ *
12
+ * Pattern: Facade over one state machine (`pool.ts`), its accounting (`work.ts`),
13
+ * its definitions (`definitions.ts`) and its bound tools (`boundTool.ts`).
14
+ * Role: `docs/design/2026-10-mcp-client-pool.md`. A `'reuse'` session costs a
15
+ * cold start once per conversation instead of once per call — but only
16
+ * if each conversation has a client of its own, opened once, bounded,
17
+ * never closed while in use, ended remotely and said to be. This is
18
+ * the one place that does all of it:
19
+ *
20
+ * - one client per authenticated conversation, keyed by the library's
21
+ * identity encoding of the identity the CALLER named;
22
+ * - one open per key, under the pool's own signal and `initTimeoutMs`;
23
+ * - three budgets — open clients, endings, outstanding operations — each
24
+ * charged until the work under it has SETTLED, and `PoolFullError` at once
25
+ * when one is full: there is no queue;
26
+ * - tool definitions per client incarnation by default, shared only under the
27
+ * app's declared contract, every tool re-checked after each wait and at the
28
+ * send gate;
29
+ * - every ending through `terminate()`, with the remote outcome reported, and
30
+ * `close()` still only local.
31
+ *
32
+ * What it does not do: repeat a call, share anything across processes, bound a
33
+ * remote session it could not end, or make a server enforce per-user
34
+ * permissions.
35
+ */
36
+ import { fileListUse } from '../../tools/mcpClientUse.js';
37
+ import { TOOL_TEARDOWN_TIMEOUT_MS } from '../../tools/toolSessions.js';
38
+ import { noteRefusal } from './boundTool.js';
39
+ import { poolKeyOf, scopeMatcher } from './identityKey.js';
40
+ import { leaseHandle } from './lease.js';
41
+ import { listTools } from './listTools.js';
42
+ import { readPoolOptions } from './options.js';
43
+ import { Pool } from './pool.js';
44
+ import { UseFacts } from './record.js';
45
+ /**
46
+ * Build a pool for one MCP server.
47
+ *
48
+ * @throws TypeError when an option is missing, mistyped or out of range, or when
49
+ * `connection` is given — here, when the pool is built
50
+ */
51
+ export function mcpClientPool(options) {
52
+ const settings = readPoolOptions(options);
53
+ const pool = new Pool(settings);
54
+ const rules = {
55
+ requirePrincipal: settings.requirePrincipal,
56
+ requireTenant: settings.requireTenant,
57
+ };
58
+ let closing;
59
+ const provider = {
60
+ id: `mcp-pool:${settings.name}`,
61
+ list: async (ctx) => {
62
+ const facts = new UseFacts(settings.name, 'list');
63
+ try {
64
+ const key = poolKeyOf(ctx.callerIdentity, rules);
65
+ if (typeof key === 'string')
66
+ throw pool.refused(key);
67
+ return await listTools(pool, key, ctx.signal, facts);
68
+ }
69
+ catch (error) {
70
+ noteRefusal(facts, error);
71
+ throw error;
72
+ }
73
+ finally {
74
+ // Filed against the context the tools slot passed; the slot emits it.
75
+ fileListUse(ctx, facts.toUse());
76
+ }
77
+ },
78
+ };
79
+ return {
80
+ name: settings.name,
81
+ toolProvider: () => provider,
82
+ async acquire(identity, acquireOptions) {
83
+ const key = poolKeyOf(identity, rules);
84
+ if (typeof key === 'string')
85
+ throw pool.refused(key);
86
+ pool.interact();
87
+ const operation = pool.admit('acquire', 'open', acquireOptions?.signal);
88
+ try {
89
+ const facts = new UseFacts(settings.name, 'list');
90
+ for (let round = 0; round < 3; round += 1) {
91
+ const client = await pool.leaseClient(key, operation, facts);
92
+ // After that wait, before anything is held: the pool may have closed,
93
+ // the caller may have gone, the client may have been named by a
94
+ // terminate. Refused, the operation's own lease ends with it below.
95
+ if (pool.closed)
96
+ throw pool.refused('closed');
97
+ operation.check();
98
+ if (client.serving)
99
+ return leaseHandle(pool, key, pool.holdLease(client));
100
+ }
101
+ throw pool.refused('init-failed');
102
+ }
103
+ finally {
104
+ operation.finish();
105
+ }
106
+ },
107
+ async terminate(scope) {
108
+ assertScope(scope, settings.name);
109
+ pool.interact();
110
+ return pool.terminate(scopeMatcher(scope), 'terminated', TOOL_TEARDOWN_TIMEOUT_MS);
111
+ },
112
+ close() {
113
+ closing ??= pool.closeAll(TOOL_TEARDOWN_TIMEOUT_MS);
114
+ return closing;
115
+ },
116
+ settled: () => pool.settled(),
117
+ permissionsChanged(scope) {
118
+ assertFields(scope, settings.name, 'permissionsChanged');
119
+ pool.permissionsChanged(scope);
120
+ },
121
+ toolsChanged: () => pool.toolsChanged(),
122
+ sweep: () => pool.interact(),
123
+ stats: () => pool.stats(),
124
+ };
125
+ }
126
+ /**
127
+ * A `terminate` scope names a principal or a conversation: ending every
128
+ * conversation of a whole tenant is `close()`'s job, and a mistyped scope must
129
+ * not do it.
130
+ */
131
+ function assertScope(scope, name) {
132
+ assertFields(scope, name, 'terminate');
133
+ const named = (value) => value !== undefined && value !== '';
134
+ if (!named(scope.principal) && !named(scope.conversationId)) {
135
+ throw new TypeError(`mcpClientPool[${name}].terminate: the scope must name a principal or a conversation ` +
136
+ '({ tenant, principal } or { tenant, principal, conversationId }).');
137
+ }
138
+ }
139
+ /** Every field present is a string. Never quoted: a scope is an identity. */
140
+ function assertFields(scope, name, door) {
141
+ if (scope === null || typeof scope !== 'object') {
142
+ throw new TypeError(`mcpClientPool[${name}].${door}: the scope must be an object.`);
143
+ }
144
+ for (const field of ['tenant', 'principal', 'conversationId']) {
145
+ const value = scope[field];
146
+ if (value !== undefined && typeof value !== 'string') {
147
+ throw new TypeError(`mcpClientPool[${name}].${door}: \`${field}\` must be a string when present.`);
148
+ }
149
+ }
150
+ }
151
+ //# sourceMappingURL=mcpClientPool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpClientPool.js","sourceRoot":"","sources":["../../../../src/mcp/pool/mcpClientPool.ts"],"names":[],"mappings":"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"}