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,78 @@
1
+ /**
2
+ * clientPort — what the MCP client pool asks of a client it opens, and what the
3
+ * client hands back beside the ordinary `McpClient`.
4
+ *
5
+ * Pattern: an internal seam, never on a door.
6
+ * Role: The pool's clients are ordinary `mcpClient`s (`mcpClient.ts`): their
7
+ * discovery, their per-tool checks (`callChecks.ts`, counted per
8
+ * client object by `listingGenerations.ts`), their retry series and
9
+ * their request ledger are the library's, unchanged. A pool needs
10
+ * four things more, and only a pool does, so they cross here:
11
+ *
12
+ * - a CONNECT signal apart from the client's lifetime signal: one open runs
13
+ * under the pool's own signal and open deadline, never a caller's;
14
+ * - the request guard of whatever the pool is doing on the client
15
+ * (`requestGuard.ts`), so every request is checked and counted;
16
+ * - the server's tools-changed notice, heard through the SDK's
17
+ * `listChanged.tools` with its automatic refresh OFF — a notice can only
18
+ * make the pool list again, through `listing`, which `mcpClient` counts;
19
+ * - a port back: a listing under the pool's own signal, a callable tool for a
20
+ * definition this client did not list (a shared one), and an ending whose
21
+ * settlement is reported apart from its answer.
22
+ */
23
+ import type { Tool } from '../tools/tools.js';
24
+ import type { RequestGuard } from './requestGuard.js';
25
+ import type { Ending } from './terminate.js';
26
+ import type { McpListedTool } from './types.js';
27
+ import type { ThrottleFetch } from './throttleRetry.js';
28
+ /** @internal What a pool passes when it opens a client. */
29
+ export interface PooledClientSeam {
30
+ /** Bounds the connect (`initialize`) only: the pool's signal and its open deadline. */
31
+ readonly connectSignal: AbortSignal;
32
+ /**
33
+ * Wrap the transport's outermost `fetch`. `claim` finds the guard of the
34
+ * pooled tool call a request body belongs to (its ledger), `undefined` for
35
+ * any other request.
36
+ */
37
+ readonly wrapFetch: (inner: ThrottleFetch, claim: (body: unknown) => RequestGuard | undefined) => ThrottleFetch;
38
+ /** The guard for a message that is not a pooled tool call — asked at the watched `send`. */
39
+ readonly guardFor: (message: unknown) => RequestGuard | undefined;
40
+ /** The server said its tool list changed. Called by the SDK; must not throw. */
41
+ readonly onToolsChanged: () => void;
42
+ }
43
+ /** @internal One listing, through the client's own discovery. */
44
+ export interface ClientListing {
45
+ /** The tools as `mcpClient` wraps them: each carries the checks of this listing. */
46
+ readonly tools: readonly Tool[];
47
+ /** The definitions as the server listed them, in the same order. */
48
+ readonly listed: readonly McpListedTool[];
49
+ }
50
+ /** @internal What a pooled client hands the pool. */
51
+ export interface ClientPort {
52
+ /** List every page through the client's own discovery, under `signal`. Replaces its cache. */
53
+ listing(signal: AbortSignal | undefined): Promise<ClientListing>;
54
+ /**
55
+ * A callable tool for `listed` on this connection that carries its OWN
56
+ * checks — for a definition this client did not list itself, so the SDK on
57
+ * this connection holds none for it.
58
+ */
59
+ bind(listed: McpListedTool): Tool;
60
+ /** End the remote session and close: the answer by its deadline, the settlement apart. */
61
+ end(): Ending;
62
+ }
63
+ /**
64
+ * @internal What a pooled open that FAILED leaves behind: the server may have
65
+ * opened a session all the same (its `initialize` answer can land after the
66
+ * open was aborted), so the pool can still end it — once the open's work has
67
+ * settled, it asks `hasSession` and, if so, ends it under the ending rule.
68
+ */
69
+ export interface HalfOpen {
70
+ /** Does the transport hold a session id the server issued — read when asked. */
71
+ hasSession(): boolean;
72
+ /** End that session, then close locally. */
73
+ end(): Ending;
74
+ }
75
+ /** @internal Remember what `error` (a failed pooled open's) left behind. */
76
+ export declare function rememberHalfOpen(error: unknown, halfOpen: HalfOpen): void;
77
+ /** @internal What a failed pooled open left behind, if anything. */
78
+ export declare function halfOpenOf(error: unknown): HalfOpen | undefined;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * clientPort — what the MCP client pool asks of a client it opens, and what the
3
+ * client hands back beside the ordinary `McpClient`.
4
+ *
5
+ * Pattern: an internal seam, never on a door.
6
+ * Role: The pool's clients are ordinary `mcpClient`s (`mcpClient.ts`): their
7
+ * discovery, their per-tool checks (`callChecks.ts`, counted per
8
+ * client object by `listingGenerations.ts`), their retry series and
9
+ * their request ledger are the library's, unchanged. A pool needs
10
+ * four things more, and only a pool does, so they cross here:
11
+ *
12
+ * - a CONNECT signal apart from the client's lifetime signal: one open runs
13
+ * under the pool's own signal and open deadline, never a caller's;
14
+ * - the request guard of whatever the pool is doing on the client
15
+ * (`requestGuard.ts`), so every request is checked and counted;
16
+ * - the server's tools-changed notice, heard through the SDK's
17
+ * `listChanged.tools` with its automatic refresh OFF — a notice can only
18
+ * make the pool list again, through `listing`, which `mcpClient` counts;
19
+ * - a port back: a listing under the pool's own signal, a callable tool for a
20
+ * definition this client did not list (a shared one), and an ending whose
21
+ * settlement is reported apart from its answer.
22
+ */
23
+ const HALF_OPENS = new WeakMap();
24
+ /** @internal Remember what `error` (a failed pooled open's) left behind. */
25
+ export function rememberHalfOpen(error, halfOpen) {
26
+ if (error !== null && typeof error === 'object')
27
+ HALF_OPENS.set(error, halfOpen);
28
+ }
29
+ /** @internal What a failed pooled open left behind, if anything. */
30
+ export function halfOpenOf(error) {
31
+ return error !== null && typeof error === 'object' ? HALF_OPENS.get(error) : undefined;
32
+ }
33
+ //# sourceMappingURL=clientPort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clientPort.js","sourceRoot":"","sources":["../../../src/mcp/clientPort.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA8DH,MAAM,UAAU,GAAG,IAAI,OAAO,EAAoB,CAAC;AAEnD,4EAA4E;AAC5E,MAAM,UAAU,gBAAgB,CAAC,KAAc,EAAE,QAAkB;IACjE,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AACnF,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACzF,CAAC"}
@@ -35,6 +35,7 @@
35
35
  */
36
36
  import { isCredentialIssued } from '../identity/types.js';
37
37
  import { throwIfAborted, untilAborted } from '../lib/abort.js';
38
+ import { guardOf, trackedBy } from './requestGuard.js';
38
39
  const DEFAULT_SERVICE = 'gateway';
39
40
  /**
40
41
  * Describe an MCP connection whose auth headers are vended per request.
@@ -128,16 +129,25 @@ export function createVendingFetch(transport, baseFetch = globalThis.fetch.bind(
128
129
  throwIfAborted(signal);
129
130
  // The provider gets the signal, to stop its own work. The wait is raced
130
131
  // against it, so a provider that ignores it cannot hold the request open.
131
- const result = await untilAborted(transport.credentials.getCredential({
132
+ const vend = Promise.resolve(transport.credentials.getCredential({
132
133
  service: transport.service,
133
134
  ...(transport.scopes !== undefined && { scopes: transport.scopes }),
134
135
  ...(transport.mode !== undefined && { mode: transport.mode }),
135
136
  ...(signal !== undefined && { signal }),
136
- }), signal);
137
+ }));
138
+ // A pooled request counts the vend itself, not the raced wait: a provider
139
+ // that ignores the abort is still work under the operation until it
140
+ // settles (`requestGuard.ts`).
141
+ const guard = guardOf(init);
142
+ guard?.track(vend);
143
+ const result = await untilAborted(vend, signal);
137
144
  // Checked again once the credential is here, before the request is handed
138
145
  // on: a credential that lands after the abort is never used, so an aborted
139
146
  // or timed-out request is never sent, however late the provider answers.
147
+ // A pooled request's send gate is asked here too: a binding that went
148
+ // stale while the credential was vended sends nothing.
140
149
  throwIfAborted(signal);
150
+ guard?.check();
141
151
  if (!isCredentialIssued(result)) {
142
152
  throw new GatewayAuthorizationRequiredError(transport.service, result.authorizationUrl);
143
153
  }
@@ -151,7 +161,7 @@ export function createVendingFetch(transport, baseFetch = globalThis.fetch.bind(
151
161
  }
152
162
  // The vended value exists only inside this call, on this Headers object,
153
163
  // for the duration of this request. Nothing above closes over it.
154
- return baseFetch(input, { ...init, headers });
164
+ return trackedBy(init, baseFetch(input, { ...init, headers }));
155
165
  };
156
166
  }
157
167
  //# sourceMappingURL=gatewayTransport.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gatewayTransport.js","sourceRoot":"","sources":["../../../src/mcp/gatewayTransport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gatewayTransport.js","sourceRoot":"","sources":["../../../src/mcp/gatewayTransport.ts"],"names":[],"mappings":"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"}
@@ -15,4 +15,12 @@ export type { McpReplayDeduplication, McpReplayKind, McpReplayOptions, McpReplay
15
15
  export { McpReplayUnavailableError, type McpReplayUnavailableReason } from './replayUnavailable.js';
16
16
  export { McpToolListIncompleteError, type McpToolListIncompleteReason } from './toolList.js';
17
17
  export { McpOutputUncheckedError } from './callChecks.js';
18
- export type { McpCallToolResult, McpClient, McpClientOptions, McpConnection, McpConnectionOptions, McpJsonSchemaValidator, McpSdk, McpGatewayTransport, McpHttpTransport, McpStdioTransport, McpTransport, McpSdkClient, McpServeOptions, McpServeHandle, McpServeTransport, McpHttpServeTransport, McpStdioServeTransport, McpSdkServer, } from './types.js';
18
+ export { canTerminate, type TerminableMcpClient } from './terminate.js';
19
+ export { mcpClientPool, type McpClientPool, type McpPoolScope } from './pool/mcpClientPool.js';
20
+ export { MCP_POOL_DEFAULTS, MAX_DEFINITION_ENTRIES, MAX_SHARED_TOOLS, type McpClientPoolOptions, type McpPoolDefinitionSharing, type McpPoolIdentity, type McpPoolShareContext, } from './pool/options.js';
21
+ export { McpPoolRefusedError, PoolFullError, type McpPoolLimit, type McpPoolRefusal, } from './pool/errors.js';
22
+ export type { McpPoolLease } from './pool/lease.js';
23
+ export type { McpPoolReport } from './pool/record.js';
24
+ export type { McpPoolStats } from './pool/pool.js';
25
+ export type { EndReason as McpPoolEndReason } from './pool/pooledClient.js';
26
+ export type { McpCallToolResult, McpClient, McpClientOptions, McpConnection, McpConnectionOptions, McpJsonSchemaValidator, McpSdk, McpGatewayTransport, McpHttpTransport, McpStdioTransport, McpTerminateOutcome, McpTransport, McpTransportSettings, McpSdkClient, McpServeOptions, McpServeHandle, McpServeTransport, McpHttpServeTransport, McpStdioServeTransport, McpSdkServer, } from './types.js';
@@ -25,4 +25,12 @@ export { McpToolListIncompleteError } from './toolList.js';
25
25
  // cannot check — no JSON Schema validator reachable — is refused, never answered
26
26
  // unchecked.
27
27
  export { McpOutputUncheckedError } from './callChecks.js';
28
+ // Ending a client's remote session: a feature-detected capability, never a
29
+ // required member of `McpClient` (`close()` stays local).
30
+ export { canTerminate } from './terminate.js';
31
+ // A client per conversation, for one server: bounded, ended remotely, and its
32
+ // tool definitions per client incarnation unless the app declares sharing.
33
+ export { mcpClientPool } from './pool/mcpClientPool.js';
34
+ export { MCP_POOL_DEFAULTS, MAX_DEFINITION_ENTRIES, MAX_SHARED_TOOLS, } from './pool/options.js';
35
+ export { McpPoolRefusedError, PoolFullError, } from './pool/errors.js';
28
36
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,8EAA8E;AAC9E,0EAA0E;AAC1E,OAAO,EAAE,mBAAmB,EAAsB,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAA+C,MAAM,oBAAoB,CAAC;AAChG,OAAO,EACL,gBAAgB,EAChB,iCAAiC,GAElC,MAAM,uBAAuB,CAAC;AAC/B,6EAA6E;AAC7E,4EAA4E;AAC5E,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAgBnD,2EAA2E;AAC3E,0CAA0C;AAC1C,OAAO,EAAE,yBAAyB,EAAmC,MAAM,wBAAwB,CAAC;AACpG,gFAAgF;AAChF,8EAA8E;AAC9E,OAAO,EAAE,0BAA0B,EAAoC,MAAM,eAAe,CAAC;AAC7F,8EAA8E;AAC9E,iFAAiF;AACjF,aAAa;AACb,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,8EAA8E;AAC9E,0EAA0E;AAC1E,OAAO,EAAE,mBAAmB,EAAsB,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAA+C,MAAM,oBAAoB,CAAC;AAChG,OAAO,EACL,gBAAgB,EAChB,iCAAiC,GAElC,MAAM,uBAAuB,CAAC;AAC/B,6EAA6E;AAC7E,4EAA4E;AAC5E,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAgBnD,2EAA2E;AAC3E,0CAA0C;AAC1C,OAAO,EAAE,yBAAyB,EAAmC,MAAM,wBAAwB,CAAC;AACpG,gFAAgF;AAChF,8EAA8E;AAC9E,OAAO,EAAE,0BAA0B,EAAoC,MAAM,eAAe,CAAC;AAC7F,8EAA8E;AAC9E,iFAAiF;AACjF,aAAa;AACb,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,2EAA2E;AAC3E,0DAA0D;AAC1D,OAAO,EAAE,YAAY,EAA4B,MAAM,gBAAgB,CAAC;AACxE,8EAA8E;AAC9E,2EAA2E;AAC3E,OAAO,EAAE,aAAa,EAAyC,MAAM,yBAAyB,CAAC;AAC/F,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,GAKjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,mBAAmB,EACnB,aAAa,GAGd,MAAM,kBAAkB,CAAC"}
@@ -42,7 +42,9 @@
42
42
  * loads only when a consumer actually constructs a client. Tests
43
43
  * inject `_client` and skip the import path entirely.
44
44
  */
45
- import type { McpClient, McpClientOptions, McpConnectionOptions } from './types.js';
45
+ import type { Tool } from '../tools/tools.js';
46
+ import type { McpClient, McpClientOptions, McpConnectionOptions, McpListedTool } from './types.js';
47
+ import { type ClientPort, type PooledClientSeam } from './clientPort.js';
46
48
  /**
47
49
  * Connect to an MCP server. Returns an `McpClient` that exposes the
48
50
  * server's tools as agentfootprint `Tool[]` and a `close()` to tear
@@ -66,3 +68,18 @@ import type { McpClient, McpClientOptions, McpConnectionOptions } from './types.
66
68
  * transport fails to connect.
67
69
  */
68
70
  export declare function mcpClient(opts: McpClientOptions | McpConnectionOptions): Promise<McpClient>;
71
+ /**
72
+ * @internal The MCP client pool's way to open a client (`mcp/pool/`): an
73
+ * ordinary `mcpClient` — the same discovery, checks, ledger and retry series —
74
+ * plus what only a pool asks for (`clientPort.ts`), and the port back.
75
+ */
76
+ export declare function openPooledClient(opts: McpClientOptions, seam: PooledClientSeam): Promise<{
77
+ readonly client: McpClient;
78
+ readonly port: ClientPort;
79
+ }>;
80
+ /**
81
+ * @internal What a listed MCP tool looks like as a `Tool`, less its `execute`:
82
+ * its schema, its provenance and the declarations its `_meta` carries. The ONE
83
+ * shape both an `mcpClient` tool and an MCP client pool's bound tool take.
84
+ */
85
+ export declare function mcpToolShape(serverName: string, mcp: McpListedTool): Omit<Tool, 'execute'>;
@@ -58,6 +58,8 @@ import { refuseConflictingOptions } from './connectionRefusals.js';
58
58
  import { sdkLoadFailure } from './sdkLoadFailure.js';
59
59
  import { transportUrl } from './transportUrl.js';
60
60
  import { lazyRequire } from '../lib/lazyRequire.js';
61
+ import { rememberHalfOpen, } from './clientPort.js';
62
+ import { DeleteWatch, endLocally, endSession, } from './terminate.js';
61
63
  // Version-less identity. The MCP `clientInfo` field is informational
62
64
  // (server logs it); a hardcoded number drifts every release. Consumers
63
65
  // who care about wire-level identity pass `clientInfo` explicitly.
@@ -88,6 +90,18 @@ const DEFAULT_CLIENT_INFO = {
88
90
  * transport fails to connect.
89
91
  */
90
92
  export async function mcpClient(opts) {
93
+ return (await connectClient(opts)).client;
94
+ }
95
+ /**
96
+ * @internal The MCP client pool's way to open a client (`mcp/pool/`): an
97
+ * ordinary `mcpClient` — the same discovery, checks, ledger and retry series —
98
+ * plus what only a pool asks for (`clientPort.ts`), and the port back.
99
+ */
100
+ export async function openPooledClient(opts, seam) {
101
+ return connectClient(opts, seam);
102
+ }
103
+ /** The one construction path behind `mcpClient` and the pool's opener. */
104
+ async function connectClient(opts, seam) {
91
105
  const name = opts.name ?? 'mcp';
92
106
  const resultMode = resultModeOf(opts.resultMode, name);
93
107
  // Before anything connects: an option that names a behaviour which cannot
@@ -100,24 +114,27 @@ export async function mcpClient(opts) {
100
114
  // The validator a tool's own output checks use (`callChecks.ts`), refused by
101
115
  // name now when it is not one.
102
116
  const validator = validatorOption(opts.jsonSchemaValidator, `mcpClient[${name}]`);
103
- const { connection, ledger, kit } = await openConnection(opts, replay, name, validator);
117
+ const { connection, ledger, kit, ending } = await openConnection(opts, replay, name, validator, seam);
104
118
  const unlisted = new Set();
105
119
  // Tool cache so consumers calling `.tools()` more than once don't
106
120
  // hammer the server. `.refresh()` invalidates it.
107
121
  let cache = null;
108
122
  let closed = false;
123
+ // The one ending this client gets (`terminate.ts`): `terminate()` asks for it
124
+ // once, and every later call answers the same outcome.
125
+ let ended;
109
126
  const ensureOpen = (op) => {
110
127
  if (closed) {
111
128
  throw new Error(`mcpClient[${name}].${op}() called after close(). Construct a new client to reconnect.`);
112
129
  }
113
130
  };
114
- const buildTools = async () => {
131
+ const listWith = async (signal) => {
115
132
  // Every page of the server's list (`toolList.ts`). `signal` rides each
116
133
  // page in the SDK's trailing options argument, never in the params — see
117
134
  // `wrapMcpTool`.
118
135
  // Each page request is counted in this connection's listings, whatever it
119
136
  // settles to (`listingGenerations.ts`).
120
- const listing = await listEveryPage(connection, opts.signal, kit.listings);
137
+ const listing = await listEveryPage(connection, signal, kit.listings);
121
138
  // A list that did not end cannot say which entries name nothing.
122
139
  if (listing.incomplete === undefined)
123
140
  noteUnlisted(replay, listing.tools, name, unlisted);
@@ -129,9 +146,28 @@ export async function mcpClient(opts) {
129
146
  if (listing.incomplete !== undefined) {
130
147
  throw new McpToolListIncompleteError(listing.incomplete, name, listing.pages, tools);
131
148
  }
132
- return tools;
149
+ return { tools, listed: listing.tools };
133
150
  };
134
- return {
151
+ const buildTools = async () => (await listWith(opts.signal)).tools;
152
+ const closeLocally = async () => {
153
+ try {
154
+ await connection.close();
155
+ }
156
+ finally {
157
+ // Nothing this connection sent is awaited any more, however it closed.
158
+ ledger?.close();
159
+ }
160
+ };
161
+ // The remote session ended, then the local side — at most once.
162
+ const end = () => {
163
+ if (ended !== undefined)
164
+ return ended;
165
+ closed = true;
166
+ cache = null;
167
+ ended = ending(closeLocally);
168
+ return ended;
169
+ };
170
+ const client = {
135
171
  name,
136
172
  async tools() {
137
173
  ensureOpen('tools');
@@ -149,15 +185,37 @@ export async function mcpClient(opts) {
149
185
  return;
150
186
  closed = true;
151
187
  cache = null;
152
- try {
153
- await connection.close();
154
- }
155
- finally {
156
- // Nothing this connection sent is awaited any more, however it closed.
157
- ledger?.close();
158
- }
188
+ await closeLocally();
189
+ },
190
+ async terminate() {
191
+ if (ended !== undefined)
192
+ return ended.outcome;
193
+ ensureOpen('terminate');
194
+ return end().outcome;
195
+ },
196
+ };
197
+ const port = {
198
+ async listing(signal) {
199
+ ensureOpen('tools');
200
+ const listing = await listWith(signal);
201
+ cache = listing.tools;
202
+ return listing;
159
203
  },
204
+ bind: (listed) =>
205
+ // No listing generation: this connection's SDK holds no checks for a
206
+ // definition it did not list, so the tool always runs its own.
207
+ wrapMcpTool(name, connection, listed, opts.signal, resultMode, ledger, strict, checksFor(listed, kit, undefined)),
208
+ end,
160
209
  };
210
+ return { client, port };
211
+ }
212
+ /**
213
+ * An ending that sends nothing: the local close, and the outcome that says why
214
+ * — answered by the teardown deadline even when the close hangs, the close
215
+ * itself reported apart (`terminate.ts` · `endLocally`).
216
+ */
217
+ function endingWithout(remote) {
218
+ return (close) => endLocally(remote, close);
161
219
  }
162
220
  /**
163
221
  * The one funnel every arm passes through, in precedence order: a connection
@@ -168,38 +226,48 @@ export async function mcpClient(opts) {
168
226
  * A call with neither `connection` nor `sdk` reaches `resolveClient` with two
169
227
  * `undefined` arguments and takes the identical path it always took.
170
228
  */
171
- async function openConnection(opts, replay, name, validator) {
229
+ async function openConnection(opts, replay, name, validator, seam) {
172
230
  // A client built elsewhere: the library loads nothing, so only the caller's
173
231
  // validator can check a tool's output itself, and errors take the SDK's shape.
174
232
  // Its list requests are counted per client: every mcpClient over it shares one
175
233
  // count, as they share the SDK's one set of checks (`listingGenerations.ts`).
234
+ // The library did not build its transport, so it cannot end the session.
176
235
  if (opts.connection !== undefined) {
177
236
  const kit = callCheckKit(name, listingsOf(opts.connection), validator);
178
- return { connection: opts.connection, kit };
237
+ return { connection: opts.connection, kit, ending: endingWithout('unavailable') };
179
238
  }
180
239
  if (opts._client !== undefined) {
181
240
  return {
182
241
  connection: opts._client,
183
242
  kit: callCheckKit(name, listingsOf(opts._client), validator),
243
+ ending: endingWithout('unavailable'),
184
244
  };
185
245
  }
186
246
  // `refuseConflictingOptions` has already refused the case where this is
187
247
  // absent, so the assertion documents that guarantee rather than assuming it.
188
248
  const transport = opts.transport;
189
- return resolveClient(transport, opts.clientInfo, opts.signal, opts.retryOnThrottle, opts.sdk, replay, name, validator);
249
+ return resolveClient(transport, opts.clientInfo, opts.signal, opts.retryOnThrottle, opts.sdk, replay, name, validator, seam);
190
250
  }
191
251
  // ─── SDK construction (lazy require, unless the caller supplied it) ─
192
- async function resolveClient(transport, clientInfo, signal, retryOnThrottle, sdk, replay, name = 'mcp', validator) {
252
+ async function resolveClient(transport, clientInfo, signal, retryOnThrottle, sdk, replay, name = 'mcp', validator, seam) {
193
253
  if (sdk !== undefined)
194
254
  assertSdk(sdk, transport.transport);
195
255
  const mod = sdk ?? loadClientModule();
196
256
  // A caller's validator is the client's too, so one validator checks every
197
- // page; without one, the options are exactly what they always were.
257
+ // page; without one, the options are exactly what they always were. A pooled
258
+ // client hears the server's tools-changed notice with the SDK's automatic
259
+ // refresh OFF: the notice only makes the pool list again, through this
260
+ // client's own discovery, which counts every listing (`listingGenerations.ts`).
198
261
  const options = validator === undefined
199
262
  ? { capabilities: {} }
200
263
  : { capabilities: {}, jsonSchemaValidator: validator };
264
+ if (seam !== undefined) {
265
+ options.listChanged = {
266
+ tools: { autoRefresh: false, debounceMs: 0, onChanged: () => seam.onToolsChanged() },
267
+ };
268
+ }
201
269
  const client = new mod.Client(clientInfo ?? DEFAULT_CLIENT_INFO, options);
202
- const built = await buildTransport(transport, retryOnThrottle, sdk, replay, name);
270
+ const built = await buildTransport(transport, retryOnThrottle, sdk, replay, name, seam);
203
271
  // With the SDK loaded here, so is what a tool's own checks run with: its
204
272
  // default validator and its own error class. With `sdk`, the loader is never
205
273
  // touched (`callChecks.ts`).
@@ -207,14 +275,39 @@ async function resolveClient(transport, clientInfo, signal, retryOnThrottle, sdk
207
275
  ? callCheckKit(name, listingsOf(client), validator, loadCheckModules(validator))
208
276
  : callCheckKit(name, listingsOf(client), validator);
209
277
  // Same rule as callTool: options ride beside the transport, never inside it.
210
- if (signal)
211
- await client.connect(built.transport, { signal });
212
- else
213
- await client.connect(built.transport);
278
+ // A pooled client connects under the pool's own signal and open deadline;
279
+ // its lifetime signal (`signal`) rides every later request.
280
+ const connectSignal = seam?.connectSignal ?? signal;
281
+ try {
282
+ if (connectSignal)
283
+ await client.connect(built.transport, { signal: connectSignal });
284
+ else
285
+ await client.connect(built.transport);
286
+ }
287
+ catch (error) {
288
+ // A pooled open that failed may still have opened a session on the server:
289
+ // the pool can end it once the open's work has settled (`clientPort.ts`).
290
+ if (seam !== undefined) {
291
+ const transport = built.transport;
292
+ rememberHalfOpen(error, {
293
+ hasSession: () => transport?.sessionId !== undefined,
294
+ end: () => built.ending(async () => {
295
+ try {
296
+ await client.close();
297
+ }
298
+ finally {
299
+ built.ledger?.close();
300
+ }
301
+ }),
302
+ });
303
+ }
304
+ throw error;
305
+ }
214
306
  return {
215
307
  connection: client,
216
308
  kit,
217
309
  ...(built.ledger !== undefined && { ledger: built.ledger }),
310
+ ending: built.ending,
218
311
  };
219
312
  }
220
313
  /**
@@ -284,7 +377,7 @@ function loadClientModule() {
284
377
  }));
285
378
  }
286
379
  }
287
- async function buildTransport(t, retryOnThrottle, sdk, replay, name = 'mcp') {
380
+ async function buildTransport(t, retryOnThrottle, sdk, replay, name = 'mcp', seam) {
288
381
  if (t.transport === 'stdio') {
289
382
  // Deliberately NOT served by `sdk`: stdio spawns a subprocess, so it can
290
383
  // never run in a browser, and `client/stdio.js` is the SDK's one client
@@ -305,14 +398,23 @@ async function buildTransport(t, retryOnThrottle, sdk, replay, name = 'mcp') {
305
398
  }));
306
399
  }
307
400
  // No fetch, so no retry series: a stdio call has no attempts to file.
308
- return {
309
- transport: new stdioMod.StdioClientTransport({
310
- command: t.command,
311
- args: t.args ? [...t.args] : [],
312
- ...(t.env && { env: { ...t.env } }),
313
- ...(t.cwd !== undefined && { cwd: t.cwd }),
314
- }),
315
- };
401
+ // stdio has no session id to end: its ending is the local close.
402
+ const stdio = new stdioMod.StdioClientTransport({
403
+ command: t.command,
404
+ args: t.args ? [...t.args] : [],
405
+ ...(t.env && { env: { ...t.env } }),
406
+ ...(t.cwd !== undefined && { cwd: t.cwd }),
407
+ });
408
+ if (seam === undefined)
409
+ return { transport: stdio, ending: endingWithout('no-session') };
410
+ // A pooled client's sends are watched, so the pool's gate is asked at
411
+ // `send()` — on stdio, which writes without waiting, the last boundary.
412
+ const ledger = new RequestLedger({
413
+ server: name,
414
+ strict: replay?.policy === 'app-authorized',
415
+ guardFor: seam.guardFor,
416
+ });
417
+ return watchedForPool(stdio, ledger, endingWithout('no-session'), name);
316
418
  }
317
419
  // http + gateway transports both ride Streamable HTTP. They differ only in
318
420
  // WHEN the auth headers are decided: `http` fixes them at construction,
@@ -345,7 +447,16 @@ async function buildTransport(t, retryOnThrottle, sdk, replay, name = 'mcp') {
345
447
  const ledger = new RequestLedger({
346
448
  server: name,
347
449
  strict: replay?.policy === 'app-authorized',
450
+ ...(seam !== undefined && { guardFor: seam.guardFor }),
348
451
  });
452
+ // The pass-through fetch every transport the library builds carries: it
453
+ // changes no request, and reads a `DELETE`'s answer for `terminate()`
454
+ // (`terminate.ts`), because the SDK treats a 405 as success. A pooled client's
455
+ // requests also cross the pool's gate here, before anything else of ours.
456
+ const watch = new DeleteWatch();
457
+ const outermost = (inner) => watch.fetch(seam === undefined
458
+ ? inner
459
+ : seam.wrapFetch(inner ?? platformFetch, (body) => guardOfCall(ledger, body)));
349
460
  if (t.transport === 'gateway') {
350
461
  // The consumer's own `fetch` (9.32.0) goes UNDERNEATH the vending, not
351
462
  // beside it: `createVendingFetch` resolves the credential, applies it to
@@ -356,9 +467,11 @@ async function buildTransport(t, retryOnThrottle, sdk, replay, name = 'mcp') {
356
467
  // the seam existed.
357
468
  const gatewayFetch = retried(createVendingFetch(t, t.fetch));
358
469
  const ledgered = gatewayFetch && ledgeredFetch(gatewayFetch, ledger, replay);
359
- return watched(new httpMod.StreamableHTTPClientTransport(transportUrl(t.url, 'mcpClient'), {
360
- fetch: ledgered ?? gatewayFetch,
361
- }), ledgered && ledger);
470
+ const sdkTransport = new httpMod.StreamableHTTPClientTransport(transportUrl(t.url, 'mcpClient'), { fetch: outermost(ledgered ?? gatewayFetch) });
471
+ if (seam !== undefined) {
472
+ return watchedForPool(sdkTransport, ledger, sessionEnding(sdkTransport, watch), name);
473
+ }
474
+ return watched(sdkTransport, ledgered !== undefined ? ledger : undefined, sessionEnding(sdkTransport, watch));
362
475
  }
363
476
  // Both options ride the SAME SDK transport, and both are forwarded when both
364
477
  // are given. The SDK folds `requestInit.headers` into the `init.headers` it
@@ -366,23 +479,60 @@ async function buildTransport(t, retryOnThrottle, sdk, replay, name = 'mcp') {
366
479
  // final word over the bytes — see `McpHttpTransport.fetch`.
367
480
  const httpFetch = retried(t.fetch);
368
481
  const ledgered = httpFetch && ledgeredFetch(httpFetch, ledger, replay);
369
- const fetch = ledgered ?? httpFetch;
370
- return watched(new httpMod.StreamableHTTPClientTransport(transportUrl(t.url, 'mcpClient'), {
482
+ const sdkTransport = new httpMod.StreamableHTTPClientTransport(transportUrl(t.url, 'mcpClient'), {
371
483
  ...(t.headers && { requestInit: { headers: { ...t.headers } } }),
372
- ...(fetch && { fetch }),
373
- }), ledgered && ledger);
484
+ fetch: outermost(ledgered ?? httpFetch),
485
+ });
486
+ if (seam !== undefined) {
487
+ return watchedForPool(sdkTransport, ledger, sessionEnding(sdkTransport, watch), name);
488
+ }
489
+ return watched(sdkTransport, ledgered !== undefined ? ledger : undefined, sessionEnding(sdkTransport, watch));
490
+ }
491
+ /** The guard of the pooled tool call `body` belongs to, by its ledger claim. */
492
+ function guardOfCall(ledger, body) {
493
+ return ledger.claim(body)?.guard;
494
+ }
495
+ /** How an SDK Streamable HTTP transport's session ends: its own `terminateSession()`. */
496
+ function sessionEnding(transport, watch) {
497
+ const terminable = transport;
498
+ if (typeof terminable?.terminateSession !== 'function')
499
+ return endingWithout('unavailable');
500
+ return (close) => endSession(terminable, watch, close);
374
501
  }
375
502
  /**
376
503
  * `transport` with its sends watched by `ledger` — the SAME object, its own
377
504
  * `send` installed in place. As it is, with no ledger, when there is none or it
378
505
  * cannot be watched: then no call expects its attempts filed.
379
506
  */
380
- function watched(transport, ledger) {
507
+ function watched(transport, ledger, ending) {
381
508
  if (ledger === undefined || transport === null || typeof transport !== 'object') {
382
- return { transport };
509
+ return { transport, ending };
510
+ }
511
+ return ledger.watch(transport) === undefined
512
+ ? { transport, ending }
513
+ : { transport, ledger, ending };
514
+ }
515
+ /**
516
+ * A pooled client's transport, its sends watched — REQUIRED. The pool checks
517
+ * every request against its binding at the watched `send` (on stdio the last
518
+ * boundary there is) and ties each tool call to its guard there, so a
519
+ * transport whose `send` cannot be watched is refused before anything is
520
+ * built on it or sent: the pool's guarantee needs the watch, whatever the
521
+ * replay policy. A standalone client never comes here (`watched`).
522
+ *
523
+ * @throws when the transport has no `send` to watch or refuses the property
524
+ */
525
+ function watchedForPool(transport, ledger, ending, name) {
526
+ const built = watched(transport, ledger, ending);
527
+ if (built.ledger === undefined) {
528
+ throw new Error(`mcpClient[${name}]: the MCP client pool checks every request at its transport's \`send\` ` +
529
+ "before it is sent, and this transport's `send` cannot be watched (it is missing, or a " +
530
+ 'property that cannot be redefined), so the pooled client is not opened. Nothing was sent.');
383
531
  }
384
- return ledger.watch(transport) === undefined ? { transport } : { transport, ledger };
532
+ return built;
385
533
  }
534
+ /** The platform's fetch, looked up at call time so a later polyfill still wins. */
535
+ const platformFetch = (input, init) => globalThis.fetch(input, init);
386
536
  /** The Node loader path for Streamable HTTP — reached only with no `sdk`. */
387
537
  function loadHttpModule(transport) {
388
538
  try {
@@ -419,8 +569,13 @@ function noteUnlisted(replay, listed, server, noted) {
419
569
  }
420
570
  }
421
571
  // ─── Tool wrapping ─────────────────────────────────────────────────
422
- function wrapMcpTool(serverName, connection, mcp, signal, resultMode, ledger, strict = false, checks) {
423
- const tool = {
572
+ /**
573
+ * @internal What a listed MCP tool looks like as a `Tool`, less its `execute`:
574
+ * its schema, its provenance and the declarations its `_meta` carries. The ONE
575
+ * shape both an `mcpClient` tool and an MCP client pool's bound tool take.
576
+ */
577
+ export function mcpToolShape(serverName, mcp) {
578
+ return {
424
579
  schema: {
425
580
  name: mcp.name,
426
581
  description: mcp.description ?? `MCP tool: ${mcp.name}`,
@@ -441,6 +596,11 @@ function wrapMcpTool(serverName, connection, mcp, signal, resultMode, ledger, st
441
596
  tool: mcp.name,
442
597
  inputSchema: mcp.inputSchema,
443
598
  }),
599
+ };
600
+ }
601
+ function wrapMcpTool(serverName, connection, mcp, signal, resultMode, ledger, strict = false, checks) {
602
+ const tool = {
603
+ ...mcpToolShape(serverName, mcp),
444
604
  execute: async (args, ctx) => {
445
605
  // The agent passes args as `unknown` per Tool contract. MCP
446
606
  // expects a JSON object — non-object inputs become `{}` rather