alibabacloud-agentcore-sdk 0.1.5 → 0.1.7

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.
package/README.md CHANGED
@@ -79,7 +79,7 @@ Header 作用于该客户端的握手、工具发现和工具调用,不是请
79
79
  SDK 会复制配置,不同 Header 配置使用不同客户端和 Session;不传时保持原有行为。
80
80
  合并顺序为平台 Header → 凭证 Header → 自定义 Header;名称大小写不敏感,冲突直接报错,不做覆盖。
81
81
  `Authorization`、平台已配置的 Header,以及 `Host`、`Mcp-Session-Id`、`Mcp-Protocol-Version` 等协议维护字段不可覆盖。
82
- 凭证在首次建连和重连时查询与获取,复用 Session 期间不重复获取,也不后台刷新。
82
+ HTTP Header provider 在每次发送请求时求值,Session 复用不影响 Header 更新。
83
83
  托管 MCP 使用 Streamable HTTP;直连 MCP 保留 `headersProvider`,stdio 不承载 HTTP Header。
84
84
  不要修改共享客户端来切换用户身份。Header 的内容由业务明确选择,不自动透传入站请求头。
85
85
  固定 Header 不等于可信身份。完整示例见 [MCP 凭证与 Header](examples/mcp-credentials.ts)。
@@ -206,6 +206,16 @@ npx tsx examples/server.ts
206
206
 
207
207
  ## 使用提示
208
208
 
209
+ `AgentCoreServer` 在当前执行上下文中保留入站网关设置的 `X-AgentCore-Accessor-WAT`。
210
+ `core.mcp()` 调用平台 MCP 时自动携带该原始值,同一次入站请求内复用 MCP Session,
211
+ 不同入站请求隔离,请求结束(包括失败或取消)后关闭 Session。已有网关 `Authorization` 继续保留;
212
+ 直连 MCP 不会自动收到平台 WAT。不要把 Accessor JWT、WAT 或 Matrix ID 配成固定 MCP
213
+ Header。自行启动的后台任务或跨进程执行需要显式传播可信请求上下文。
214
+ `listTools({ headers: { "X-Trace-ID": "..." } })`、`callTool(name, args, { headers: {...} })`
215
+ 支持逐次传入业务 Header,不改变 MCP Session;身份、Authorization 和协议 Header 不可覆盖。
216
+ 手动管理 `RequestContext` 时,在请求结束后调用 `await context.close()` 释放 Session。
217
+ WAT 兑换、到期恢复和工具权限校验由网关负责,SDK 不会自动重试工具调用。
218
+
209
219
  - 在应用生命周期内复用 Core,退出时调用 `await core.close()`。
210
220
  - 模型的工具调用、Responses 和 Embedding 支持情况取决于所选模型。
211
221
  - 仅加载可信 Skill;不需要执行命令时设置 `ALLOW_EXECUTE_COMMAND=false`。
@@ -1,22 +1,32 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); var _class;
2
2
 
3
3
  var _chunkV4A6MJ42cjs = require('./chunk-V4A6MJ42.cjs');
4
4
 
5
5
  // src/runtime/context.ts
6
6
  var _async_hooks = require('async_hooks');
7
7
  var _util = require('util');
8
- var RequestContext = class {
9
-
10
- constructor(headers) {
8
+ var RequestContext = (_class = class {
9
+ constructor(headers, signal) {;_class.prototype.__init.call(this);
10
+ this.signal = signal;
11
11
  this.headers = Object.freeze(Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v])));
12
12
  }
13
+
14
+ __init() {this.cleanups = []}
15
+ onClose(cleanup) {
16
+ this.cleanups.push(cleanup);
17
+ }
18
+ async close() {
19
+ const results = await Promise.allSettled(this.cleanups.splice(0).map((cleanup) => Promise.resolve().then(cleanup)));
20
+ for (const result of results) if (result.status === "rejected") throw result.reason;
21
+ }
22
+
13
23
  [_util.inspect.custom]() {
14
24
  return "RequestContext(headers=<redacted>)";
15
25
  }
16
26
  toJSON() {
17
27
  return this[_util.inspect.custom]();
18
28
  }
19
- };
29
+ }, _class);
20
30
  var context = new (0, _async_hooks.AsyncLocalStorage)();
21
31
  function currentContext(required = true) {
22
32
  const value = context.getStore();
@@ -32,4 +42,4 @@ function useContext(value, callback) {
32
42
 
33
43
 
34
44
  exports.RequestContext = RequestContext; exports.currentContext = currentContext; exports.useContext = useContext;
35
- //# sourceMappingURL=chunk-R7NVBTPK.cjs.map
45
+ //# sourceMappingURL=chunk-4GKZ6VKD.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/agentcore-sdk-nodejs/agentcore-sdk-nodejs/dist/chunk-4GKZ6VKD.cjs","../src/runtime/context.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACJA,0CAAkC;AAClC,4BAAwB;AAGjB,IAAM,eAAA,YAAN,MAAqB;AAAA,EAQ1B,WAAA,CAAY,OAAA,EAAoD,MAAA,EAAsB;AAAtB,IAAA,IAAA,CAAA,OAAA,EAAA,MAAA;AAC9D,IAAA,IAAA,CAAK,QAAA,EAAU,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,WAAA,CAAY,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,CAAE,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,EAAA,GAAM,CAAC,CAAA,CAAE,WAAA,CAAY,CAAA,EAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AAAA,EAChH;AAAA,EAFgE;AAAA,iBAP/C,SAAA,EAAuC,CAAC,EAAA;AAAA,EACzD,OAAA,CAAQ,OAAA,EAAoC;AAAE,IAAA,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,OAAO,CAAA;AAAA,EAAG;AAAA,EAC3E,MAAM,KAAA,CAAA,EAAuB;AAC3B,IAAA,MAAM,QAAA,EAAU,MAAM,OAAA,CAAQ,UAAA,CAAW,IAAA,CAAK,QAAA,CAAS,MAAA,CAAO,CAAC,CAAA,CAAE,GAAA,CAAI,CAAA,OAAA,EAAA,GAAW,OAAA,CAAQ,OAAA,CAAQ,CAAA,CAAE,IAAA,CAAK,OAAO,CAAC,CAAC,CAAA;AAChH,IAAA,IAAA,CAAA,MAAW,OAAA,GAAU,OAAA,EAAS,GAAA,CAAI,MAAA,CAAO,OAAA,IAAW,UAAA,EAAY,MAAM,MAAA,CAAO,MAAA;AAAA,EAC/E;AAAA,EACS;AAAA,EAIT,CAAC,aAAA,CAAQ,MAAM,CAAA,CAAA,EAAY;AAAE,IAAA,OAAO,oCAAA;AAAA,EAAsC;AAAA,EAC1E,MAAA,CAAA,EAAiB;AAAE,IAAA,OAAO,IAAA,CAAK,aAAA,CAAQ,MAAM,CAAA,CAAE,CAAA;AAAA,EAAG;AACpD,UAAA;AACA,IAAM,QAAA,EAAU,IAAI,mCAAA,CAAkC,CAAA;AAC/C,SAAS,cAAA,CAAe,SAAA,EAAW,IAAA,EAAkC;AAC1E,EAAA,MAAM,MAAA,EAAQ,OAAA,CAAQ,QAAA,CAAS,CAAA;AAC/B,EAAA,GAAA,CAAI,CAAC,MAAA,GAAS,QAAA,EAAU,MAAM,IAAI,mCAAA,CAAa,wCAAwC,CAAA;AACvF,EAAA,OAAO,KAAA;AACT;AACO,SAAS,UAAA,CAAc,KAAA,EAAuB,QAAA,EAAsB;AACzE,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,KAAA,EAAO,QAAQ,CAAA;AACpC;ADYA;AACA;AACE;AACA;AACA;AACF,kHAAC","file":"/home/runner/work/agentcore-sdk-nodejs/agentcore-sdk-nodejs/dist/chunk-4GKZ6VKD.cjs","sourcesContent":[null,"import { AsyncLocalStorage } from 'node:async_hooks';\nimport { inspect } from 'node:util';\nimport { ContextError } from '../errors';\n\nexport class RequestContext {\n private readonly cleanups: Array<() => Promise<void>> = [];\n onClose(cleanup: () => Promise<void>): void { this.cleanups.push(cleanup); }\n async close(): Promise<void> {\n const results = await Promise.allSettled(this.cleanups.splice(0).map(cleanup => Promise.resolve().then(cleanup)));\n for (const result of results) if (result.status === 'rejected') throw result.reason;\n }\n readonly headers: Readonly<Record<string, string>>;\n constructor(headers: Readonly<Record<string, string>>, readonly signal?: AbortSignal) {\n this.headers = Object.freeze(Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v])));\n }\n [inspect.custom](): string { return 'RequestContext(headers=<redacted>)'; }\n toJSON(): string { return this[inspect.custom](); }\n}\nconst context = new AsyncLocalStorage<RequestContext>();\nexport function currentContext(required = true): RequestContext | undefined {\n const value = context.getStore();\n if (!value && required) throw new ContextError('no AgentCore request context is active');\n return value;\n}\nexport function useContext<T>(value: RequestContext, callback: () => T): T {\n return context.run(value, callback);\n}\n"]}
@@ -1,5 +1,8 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
2
2
 
3
+ var _chunk4GKZ6VKDcjs = require('./chunk-4GKZ6VKD.cjs');
4
+
5
+
3
6
  var _chunkEGUKJE3Zcjs = require('./chunk-EGUKJE3Z.cjs');
4
7
 
5
8
 
@@ -12,6 +15,7 @@ var _streamableHttpjs = require('@modelcontextprotocol/sdk/client/streamableHttp
12
15
  var _ssejs = require('@modelcontextprotocol/sdk/client/sse.js');
13
16
  var _stdiojs = require('@modelcontextprotocol/sdk/client/stdio.js');
14
17
  var _typesjs = require('@modelcontextprotocol/sdk/types.js');
18
+ var _async_hooks = require('async_hooks');
15
19
  var _util = require('util');
16
20
 
17
21
  // src/mcp/headers.ts
@@ -25,7 +29,10 @@ var transportHeaders = /* @__PURE__ */ new Set([
25
29
  "mcp-protocol-version",
26
30
  "last-event-id",
27
31
  "content-type",
28
- "accept"
32
+ "accept",
33
+ "x-agentcore-accessor-wat",
34
+ "x-agentcore-accessor-jwt",
35
+ "x-agentcore-accessor-matrixid"
29
36
  ]);
30
37
  function mergeMCPHeaders(base, custom) {
31
38
  const result = { ...base };
@@ -46,12 +53,13 @@ function mergeMCPHeaders(base, custom) {
46
53
  }
47
54
 
48
55
  // src/mcp/client.ts
56
+ var callHeaders = new (0, _async_hooks.AsyncLocalStorage)();
49
57
  function mcpServerURL(gatewayURL, id) {
50
58
  const gateway = gatewayURL.replace(/\/+$/, "").replace(/\/(?:mcp|mcp-servers)$/, "");
51
59
  return `${gateway}/mcp-servers/${encodeURIComponent(id)}`;
52
60
  }
53
61
  var MCPClient = (_class = class _MCPClient {
54
- constructor(options, descriptor) {;_class.prototype.__init.call(this);
62
+ constructor(options, descriptor) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);
55
63
  this.options = options;
56
64
  this.descriptor = descriptor;
57
65
  this.logger = _nullishCoalesce(options.logger, () => ( _chunkV4A6MJ42cjs.nullLogger));
@@ -73,12 +81,16 @@ var MCPClient = (_class = class _MCPClient {
73
81
 
74
82
 
75
83
 
84
+ __init() {this.requestClients = /* @__PURE__ */ new WeakMap()}
85
+ __init2() {this.scopedClients = /* @__PURE__ */ new Set()}
76
86
 
77
- __init() {this.closed = false}
87
+ __init3() {this.closed = false}
78
88
 
79
89
 
80
90
  static platform(config, descriptor, options = {}) {
81
91
  const { headers: custom, credentialHeadersProvider, ...connectionOptions } = options;
92
+ if (Object.keys(config.gatewayHeaders).some((name) => name.toLowerCase().startsWith("x-agentcore-accessor-")))
93
+ throw new (0, _chunkV4A6MJ42cjs.ConfigError)("MCP accessor headers must come from the request context");
82
94
  const customHeaders = mergeMCPHeaders({}, custom);
83
95
  const headers = mergeMCPHeaders(config.gatewayHeaders, custom);
84
96
  return new _MCPClient({ ...connectionOptions, url: mcpServerURL(config.mcpGatewayUrl, descriptor.mcpServerId), transport: "streamable-http", headersProvider: async () => {
@@ -95,7 +107,8 @@ var MCPClient = (_class = class _MCPClient {
95
107
  const definitions = [];
96
108
  let cursor;
97
109
  const deadline = AbortSignal.timeout(this.timeouts.metadataMs);
98
- const signal = AbortSignal.any([deadline, ...options.signal ? [options.signal] : []]);
110
+ const requestSignal = _optionalChain([_chunk4GKZ6VKDcjs.currentContext.call(void 0, false), 'optionalAccess', _ => _.signal]);
111
+ const signal = AbortSignal.any([deadline, ...options.signal ? [options.signal] : [], ...requestSignal ? [requestSignal] : []]);
99
112
  do {
100
113
  const result = await session.client.listTools(cursor ? { cursor } : void 0, { signal, timeout: this.timeouts.metadataMs });
101
114
  definitions.push(...result.tools);
@@ -107,24 +120,27 @@ var MCPClient = (_class = class _MCPClient {
107
120
  parameters: definition.inputSchema,
108
121
  invoke: (arguments_) => this.callTool(definition.name, arguments_)
109
122
  }));
110
- });
123
+ }, options.headers);
111
124
  }
112
125
  async tools(options = {}) {
113
126
  return this.listTools(options);
114
127
  }
115
128
  async callTool(name, arguments_, options = {}) {
129
+ const requestSignal = _optionalChain([_chunk4GKZ6VKDcjs.currentContext.call(void 0, false), 'optionalAccess', _2 => _2.signal]);
130
+ const signal = options.signal && requestSignal ? AbortSignal.any([options.signal, requestSignal]) : _nullishCoalesce(options.signal, () => ( requestSignal));
116
131
  return this.withSession("call_tool", (session) => session.client.callTool(
117
132
  { name, arguments: arguments_ },
118
133
  _typesjs.CallToolResultSchema,
119
- { timeout: this.timeouts.toolMs, signal: options.signal }
120
- ));
134
+ { timeout: this.timeouts.toolMs, signal }
135
+ ), options.headers);
121
136
  }
122
137
  close() {
123
138
  if (this.closing) return this.closing;
124
139
  this.closed = true;
125
140
  this.closing = (async () => {
141
+ await Promise.all([...this.scopedClients].map((client) => client.close()));
126
142
  if (this.opening && this.state) this.state.abort.abort();
127
- await _optionalChain([this, 'access', _ => _.opening, 'optionalAccess', _2 => _2.catch, 'call', _3 => _3(() => void 0)]);
143
+ await _optionalChain([this, 'access', _3 => _3.opening, 'optionalAccess', _4 => _4.catch, 'call', _5 => _5(() => void 0)]);
128
144
  if (this.state) await this.retire(this.state);
129
145
  })();
130
146
  return this.closing;
@@ -144,7 +160,7 @@ var MCPClient = (_class = class _MCPClient {
144
160
  for (; ; ) {
145
161
  if (this.closed) throw new Error("MCP client is closed");
146
162
  if (this.opening) return this.opening;
147
- if (_optionalChain([this, 'access', _4 => _4.state, 'optionalAccess', _5 => _5.invalid])) {
163
+ if (_optionalChain([this, 'access', _6 => _6.state, 'optionalAccess', _7 => _7.invalid])) {
148
164
  await this.retire(this.state);
149
165
  continue;
150
166
  }
@@ -164,9 +180,14 @@ var MCPClient = (_class = class _MCPClient {
164
180
  let transport;
165
181
  if (this.options.transport === "stdio") transport = new (0, _stdiojs.StdioClientTransport)(this.options.server);
166
182
  else {
167
- const headers = await _asyncNullishCoalesce(await _optionalChain([this, 'access', _6 => _6.options, 'access', _7 => _7.headersProvider, 'optionalCall', _8 => _8()]), async () => ( {}));
168
- const fetch = (input, init) => globalThis.fetch(input, { ...init, redirect: "manual", signal: AbortSignal.any([abort.signal, ..._optionalChain([init, 'optionalAccess', _9 => _9.signal]) ? [init.signal] : []]) });
169
- transport = this.options.transport === "sse" ? new (0, _ssejs.SSEClientTransport)(new URL(this.options.url), { requestInit: { headers }, fetch }) : new (0, _streamableHttpjs.StreamableHTTPClientTransport)(new URL(this.options.url), { requestInit: { headers }, fetch });
183
+ const options = this.options;
184
+ const fetch = async (input, init) => {
185
+ const resolved = mergeMCPHeaders(await _asyncNullishCoalesce(await _optionalChain([options, 'access', _8 => _8.headersProvider, 'optionalCall', _9 => _9()]), async () => ( {})), callHeaders.getStore());
186
+ const headers = new Headers(_optionalChain([init, 'optionalAccess', _10 => _10.headers]));
187
+ for (const [name, value] of Object.entries(resolved)) headers.set(name, value);
188
+ return globalThis.fetch(input, { ...init, headers, redirect: "manual", signal: AbortSignal.any([abort.signal, ..._optionalChain([init, 'optionalAccess', _11 => _11.signal]) ? [init.signal] : [], ..._optionalChain([init, 'optionalAccess', _12 => _12.method]) === "DELETE" ? [AbortSignal.timeout(this.timeouts.sessionMs)] : []]) });
189
+ };
190
+ transport = this.options.transport === "sse" ? new (0, _ssejs.SSEClientTransport)(new URL(this.options.url), { fetch }) : new (0, _streamableHttpjs.StreamableHTTPClientTransport)(new URL(this.options.url), { fetch });
170
191
  }
171
192
  const session = { client, transport, abort, invalid: false, active: /* @__PURE__ */ new Set() };
172
193
  this.state = session;
@@ -193,7 +214,32 @@ var MCPClient = (_class = class _MCPClient {
193
214
  clearTimeout(timer);
194
215
  }
195
216
  }
196
- async withSession(operation, call) {
217
+ async withSession(operation, call, headers) {
218
+ if (headers !== void 0) return callHeaders.run(mergeMCPHeaders({}, headers), () => this.withSession(operation, call));
219
+ if (this.closed) throw new Error("MCP client is closed");
220
+ const context = _chunk4GKZ6VKDcjs.currentContext.call(void 0, false);
221
+ const wat = _optionalChain([context, 'optionalAccess', _13 => _13.headers, 'access', _14 => _14["x-agentcore-accessor-wat"]]);
222
+ if (this.descriptor && wat && context && this.options.transport !== "stdio") {
223
+ let scoped = this.requestClients.get(context);
224
+ if (!scoped) {
225
+ const options = this.options;
226
+ scoped = _MCPClient.direct({ ...options, headersProvider: async () => ({
227
+ ...await _optionalChain([options, 'access', _15 => _15.headersProvider, 'optionalCall', _16 => _16()]),
228
+ "x-agentcore-accessor-wat": wat
229
+ }) });
230
+ this.requestClients.set(context, scoped);
231
+ this.scopedClients.add(scoped);
232
+ const child = scoped;
233
+ context.onClose(async () => {
234
+ try {
235
+ await child.close();
236
+ } finally {
237
+ this.scopedClients.delete(child);
238
+ }
239
+ });
240
+ }
241
+ return scoped.withSession(operation, call);
242
+ }
197
243
  const session = await this.acquire();
198
244
  if (this.closed) throw new Error("MCP client is closed");
199
245
  const request = call(session);
@@ -216,6 +262,11 @@ var MCPClient = (_class = class _MCPClient {
216
262
  session.invalid = true;
217
263
  session.closing = (async () => {
218
264
  await Promise.allSettled([...session.active]);
265
+ try {
266
+ if (session.transport instanceof _streamableHttpjs.StreamableHTTPClientTransport && session.transport.sessionId)
267
+ await session.transport.terminateSession();
268
+ } catch (e2) {
269
+ }
219
270
  session.abort.abort();
220
271
  try {
221
272
  await session.client.close();
@@ -233,4 +284,4 @@ var MCPClient = (_class = class _MCPClient {
233
284
 
234
285
 
235
286
  exports.mergeMCPHeaders = mergeMCPHeaders; exports.mcpServerURL = mcpServerURL; exports.MCPClient = MCPClient;
236
- //# sourceMappingURL=chunk-ZXPTDLXJ.cjs.map
287
+ //# sourceMappingURL=chunk-EIA6MHAR.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/agentcore-sdk-nodejs/agentcore-sdk-nodejs/dist/chunk-EIA6MHAR.cjs","../src/mcp/client.ts","../src/mcp/headers.ts"],"names":[],"mappings":"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","file":"/home/runner/work/agentcore-sdk-nodejs/agentcore-sdk-nodejs/dist/chunk-EIA6MHAR.cjs","sourcesContent":[null,"import { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';\nimport { StdioClientTransport, type StdioServerParameters } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { CallToolResultSchema, McpError, ErrorCode, type CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { inspect } from 'node:util';\nimport { ConfigError } from '../errors';\nimport { nullLogger, type Logger } from '../logging';\nimport type { MCPDescriptor } from '../controlplane/client';\nimport type { AgentConfig } from '../runtime/config';\nimport type { HeadersProvider } from '../model/client';\nimport { Tool, type ToolArguments } from '../integrations/common';\nimport { mergeMCPHeaders } from './headers';\nimport { currentContext } from '../runtime/context';\n\nexport interface MCPTimeouts { sessionMs?: number; metadataMs?: number; toolMs?: number; }\nexport interface ManagedMCPOptions extends MCPTimeouts { headers?: Readonly<Record<string, string>>; credentialName?: string; }\nexport type DirectMCPOptions = ({ url: string; transport?: 'streamable-http' | 'sse'; headersProvider?: HeadersProvider } |\n { transport: 'stdio'; server: StdioServerParameters }) & MCPTimeouts & { logger?: Logger };\nexport interface MCPCallOptions { signal?: AbortSignal; headers?: Readonly<Record<string, string>>; }\nconst callHeaders = new AsyncLocalStorage<Readonly<Record<string, string>>>();\ninterface Session {\n client: Client;\n transport: Transport;\n abort: AbortController;\n invalid: boolean;\n active: Set<Promise<unknown>>;\n closing?: Promise<void>;\n}\n\nexport function mcpServerURL(gatewayURL: string, id: string): string {\n const gateway = gatewayURL.replace(/\\/+$/, '').replace(/\\/(?:mcp|mcp-servers)$/, '');\n return `${gateway}/mcp-servers/${encodeURIComponent(id)}`;\n}\n\nexport class MCPClient {\n readonly descriptor?: MCPDescriptor;\n private readonly logger: Logger;\n private state?: Session;\n private readonly requestClients = new WeakMap<NonNullable<ReturnType<typeof currentContext>>, MCPClient>();\n private readonly scopedClients = new Set<MCPClient>();\n private opening?: Promise<Session>;\n private closed = false;\n private closing?: Promise<void>;\n private readonly timeouts: Required<MCPTimeouts>;\n\n private constructor(private readonly options: DirectMCPOptions, descriptor?: MCPDescriptor) {\n this.descriptor = descriptor;\n this.logger = options.logger ?? nullLogger;\n this.timeouts = { sessionMs: options.sessionMs ?? 30_000, metadataMs: options.metadataMs ?? 60_000, toolMs: options.toolMs ?? 600_000 };\n if (Object.values(this.timeouts).some((timeout) => !Number.isFinite(timeout) || timeout <= 0)) throw new ConfigError('MCP operation timeouts must be positive');\n if (options.transport !== 'stdio') {\n let url: URL;\n try { url = new URL(options.url); } catch { throw new ConfigError('Direct MCP URL must be HTTP(S)'); }\n if (!['http:', 'https:'].includes(url.protocol)) throw new ConfigError('Direct MCP URL must be HTTP(S)');\n if (options.transport !== undefined && !['streamable-http', 'sse'].includes(options.transport)) throw new ConfigError('MCP transport must be streamable-http, sse, or stdio');\n }\n this.logger.info('agentcore.mcp.client.created', { mode: descriptor ? 'managed' : 'direct', transport: options.transport ?? 'streamable-http', url: this.safeURL });\n }\n static platform(config: AgentConfig, descriptor: MCPDescriptor, options: Omit<ManagedMCPOptions, 'credentialName'> & {\n logger?: Logger; credentialHeadersProvider?: () => Promise<Record<string, string>>;\n } = {}): MCPClient {\n const { headers: custom, credentialHeadersProvider, ...connectionOptions } = options;\n if (Object.keys(config.gatewayHeaders).some((name) => name.toLowerCase().startsWith('x-agentcore-accessor-')))\n throw new ConfigError('MCP accessor headers must come from the request context');\n const customHeaders = mergeMCPHeaders({}, custom);\n const headers = mergeMCPHeaders(config.gatewayHeaders, custom);\n return new MCPClient({ ...connectionOptions, url: mcpServerURL(config.mcpGatewayUrl, descriptor.mcpServerId), transport: 'streamable-http', headersProvider: async () => {\n if (!credentialHeadersProvider) return headers;\n const bound = mergeMCPHeaders(config.gatewayHeaders, await credentialHeadersProvider());\n return mergeMCPHeaders(bound, customHeaders);\n } }, descriptor);\n }\n static direct(options: DirectMCPOptions): MCPClient { return new MCPClient(options); }\n\n async listTools(options: MCPCallOptions = {}): Promise<Tool[]> {\n return this.withSession('list_tools', async (session) => {\n const definitions: Awaited<ReturnType<Client['listTools']>>['tools'] = [];\n let cursor: string | undefined;\n const deadline = AbortSignal.timeout(this.timeouts.metadataMs);\n const requestSignal = currentContext(false)?.signal;\n const signal = AbortSignal.any([deadline, ...(options.signal ? [options.signal] : []), ...(requestSignal ? [requestSignal] : [])]);\n do {\n const result = await session.client.listTools(cursor ? { cursor } : undefined, { signal, timeout: this.timeouts.metadataMs });\n definitions.push(...result.tools); cursor = result.nextCursor;\n } while (cursor);\n return definitions.map((definition) => new Tool({ name: definition.name, description: definition.description ?? '', parameters: definition.inputSchema,\n invoke: (arguments_) => this.callTool(definition.name, arguments_),\n }));\n }, options.headers);\n }\n async tools(options: MCPCallOptions = {}): Promise<Tool[]> { return this.listTools(options); }\n async callTool(name: string, arguments_: ToolArguments, options: MCPCallOptions = {}): Promise<CallToolResult> {\n const requestSignal = currentContext(false)?.signal;\n const signal = options.signal && requestSignal ? AbortSignal.any([options.signal, requestSignal]) : options.signal ?? requestSignal;\n return this.withSession('call_tool', (session) => session.client.callTool({ name, arguments: arguments_ }, CallToolResultSchema,\n { timeout: this.timeouts.toolMs, signal }) as Promise<CallToolResult>, options.headers);\n }\n\n close(): Promise<void> {\n if (this.closing) return this.closing;\n this.closed = true;\n this.closing = (async () => {\n await Promise.all([...this.scopedClients].map((client) => client.close()));\n if (this.opening && this.state) this.state.abort.abort();\n await this.opening?.catch(() => undefined);\n if (this.state) await this.retire(this.state);\n })();\n return this.closing;\n }\n [inspect.custom](): string { return `MCPClient(url=${this.safeURL}, <redacted>)`; }\n toJSON(): string { return this[inspect.custom](); }\n\n private get safeURL(): string {\n if (this.options.transport === 'stdio') return 'stdio';\n const url = new URL(this.options.url); return `${url.origin}${url.pathname}`;\n }\n private async acquire(): Promise<Session> {\n for (;;) {\n if (this.closed) throw new Error('MCP client is closed');\n if (this.opening) return this.opening;\n if (this.state?.invalid) { await this.retire(this.state); continue; }\n if (this.state) return this.state;\n const request = this.open(); this.opening = request;\n try { return await request; } finally { this.opening = undefined; }\n }\n }\n private async open(): Promise<Session> {\n const client = new Client({ name: 'agentcore-sdk', version: '0.1.0' });\n const abort = new AbortController();\n let transport: Transport;\n if (this.options.transport === 'stdio') transport = new StdioClientTransport(this.options.server);\n else {\n const options = this.options;\n const fetch: typeof globalThis.fetch = async (input, init) => {\n const resolved = mergeMCPHeaders(await options.headersProvider?.() ?? {}, callHeaders.getStore());\n const headers = new Headers(init?.headers);\n for (const [name, value] of Object.entries(resolved)) headers.set(name, value);\n return globalThis.fetch(input, { ...init, headers, redirect: 'manual', signal: AbortSignal.any([abort.signal, ...(init?.signal ? [init.signal] : []), ...(init?.method === 'DELETE' ? [AbortSignal.timeout(this.timeouts.sessionMs)] : [])]) });\n };\n transport = this.options.transport === 'sse'\n ? new SSEClientTransport(new URL(this.options.url), { fetch })\n : new StreamableHTTPClientTransport(new URL(this.options.url), { fetch });\n }\n const session: Session = { client, transport, abort, invalid: false, active: new Set() };\n this.state = session;\n const timer = setTimeout(() => abort.abort(new Error('MCP session open timed out')), this.timeouts.sessionMs);\n client.onclose = () => { session.invalid = true; };\n client.onerror = (error) => {\n if (this.closed || session.closing) return;\n session.invalid = true;\n this.logger.warn('agentcore.mcp.session.error', { url: this.safeURL, errorType: error.name });\n };\n this.logger.info('agentcore.mcp.session.open.started', { url: this.safeURL, timeoutMs: this.timeouts.sessionMs });\n try {\n await client.connect(transport, { signal: abort.signal, timeout: this.timeouts.sessionMs });\n abort.signal.throwIfAborted();\n if (this.closed) throw new Error('MCP client is closed');\n this.logger.info('agentcore.mcp.session.open.succeeded', { url: this.safeURL });\n return session;\n } catch (error) { await this.retire(session); throw error; }\n finally { clearTimeout(timer); }\n }\n\n private async withSession<T>(operation: string, call: (session: Session) => Promise<T>, headers?: Readonly<Record<string, string>>): Promise<T> {\n if (headers !== undefined) return callHeaders.run(mergeMCPHeaders({}, headers), () => this.withSession(operation, call));\n if (this.closed) throw new Error('MCP client is closed');\n const context = currentContext(false);\n const wat = context?.headers['x-agentcore-accessor-wat'];\n if (this.descriptor && wat && context && this.options.transport !== 'stdio') {\n let scoped = this.requestClients.get(context);\n if (!scoped) {\n const options = this.options;\n scoped = MCPClient.direct({ ...options, headersProvider: async () => ({\n ...await options.headersProvider?.(), 'x-agentcore-accessor-wat': wat,\n }) });\n this.requestClients.set(context, scoped);\n this.scopedClients.add(scoped);\n const child = scoped;\n context.onClose(async () => {\n try { await child.close(); } finally { this.scopedClients.delete(child); }\n });\n }\n return scoped.withSession(operation, call);\n }\n const session = await this.acquire();\n if (this.closed) throw new Error('MCP client is closed');\n const request = call(session); session.active.add(request);\n this.logger.info('agentcore.mcp.request.started', { operation, url: this.safeURL });\n try { const result = await request; this.logger.info('agentcore.mcp.request.succeeded', { operation, url: this.safeURL }); return result; }\n catch (error) {\n if (!(error instanceof McpError) || [ErrorCode.ConnectionClosed, ErrorCode.RequestTimeout].includes(error.code)) session.invalid = true;\n this.logger.warn('agentcore.mcp.request.failed', { operation, url: this.safeURL, errorType: error instanceof Error ? error.name : 'unknown' });\n throw error;\n } finally { session.active.delete(request); }\n }\n private retire(session: Session): Promise<void> {\n if (session.closing) return session.closing;\n session.invalid = true;\n session.closing = (async () => {\n await Promise.allSettled([...session.active]);\n try {\n if (session.transport instanceof StreamableHTTPClientTransport && session.transport.sessionId)\n await session.transport.terminateSession();\n } catch { /* Closing the transport below still revokes the local Session. */ }\n session.abort.abort();\n try { await session.client.close(); }\n catch (error) { this.logger.warn('agentcore.mcp.session.close.failed', { errorType: error instanceof Error ? error.name : 'unknown', url: this.safeURL }); }\n if (this.state === session) this.state = undefined;\n })();\n return session.closing;\n }\n}\n","import { ConfigError } from '../errors';\n\nconst transportHeaders = new Set([\n 'host', 'content-length', 'connection', 'transfer-encoding', 'upgrade',\n 'mcp-session-id', 'mcp-protocol-version', 'last-event-id', 'content-type', 'accept',\n 'x-agentcore-accessor-wat', 'x-agentcore-accessor-jwt', 'x-agentcore-accessor-matrixid',\n]);\n\n/** Copy fixed headers, rejecting case-insensitive credential/transport conflicts. */\nexport function mergeMCPHeaders(base: Readonly<Record<string, string>>, custom?: Readonly<Record<string, string>>): Record<string, string> {\n const result = { ...base };\n if (custom === undefined) return result;\n if (!custom || typeof custom !== 'object' || Array.isArray(custom)) throw new ConfigError('MCP headers must be an object');\n const protectedNames = new Set([...Object.keys(base).map((name) => name.toLowerCase()), ...transportHeaders, 'authorization']);\n const seen = new Set<string>();\n for (const [name, value] of Object.entries(custom)) {\n if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) throw new ConfigError('Invalid MCP header name');\n if (typeof value !== 'string' || /[\\x00-\\x1f\\x7f]/.test(value)) throw new ConfigError('MCP header values must be strings without control characters');\n const folded = name.toLowerCase();\n if (protectedNames.has(folded)) throw new ConfigError(`MCP header is protected: ${folded}`);\n if (seen.has(folded)) throw new ConfigError(`Duplicate MCP header: ${folded}`);\n seen.add(folded);\n Object.defineProperty(result, folded, { value, enumerable: true, configurable: true, writable: true });\n }\n return result;\n}\n"]}
@@ -1,3 +1,6 @@
1
+ import {
2
+ currentContext
3
+ } from "./chunk-YBBMBSFN.js";
1
4
  import {
2
5
  Tool
3
6
  } from "./chunk-EKXZKKJJ.js";
@@ -12,6 +15,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
12
15
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
13
16
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
14
17
  import { CallToolResultSchema, McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
18
+ import { AsyncLocalStorage } from "async_hooks";
15
19
  import { inspect } from "util";
16
20
 
17
21
  // src/mcp/headers.ts
@@ -25,7 +29,10 @@ var transportHeaders = /* @__PURE__ */ new Set([
25
29
  "mcp-protocol-version",
26
30
  "last-event-id",
27
31
  "content-type",
28
- "accept"
32
+ "accept",
33
+ "x-agentcore-accessor-wat",
34
+ "x-agentcore-accessor-jwt",
35
+ "x-agentcore-accessor-matrixid"
29
36
  ]);
30
37
  function mergeMCPHeaders(base, custom) {
31
38
  const result = { ...base };
@@ -46,6 +53,7 @@ function mergeMCPHeaders(base, custom) {
46
53
  }
47
54
 
48
55
  // src/mcp/client.ts
56
+ var callHeaders = new AsyncLocalStorage();
49
57
  function mcpServerURL(gatewayURL, id) {
50
58
  const gateway = gatewayURL.replace(/\/+$/, "").replace(/\/(?:mcp|mcp-servers)$/, "");
51
59
  return `${gateway}/mcp-servers/${encodeURIComponent(id)}`;
@@ -73,12 +81,16 @@ var MCPClient = class _MCPClient {
73
81
  descriptor;
74
82
  logger;
75
83
  state;
84
+ requestClients = /* @__PURE__ */ new WeakMap();
85
+ scopedClients = /* @__PURE__ */ new Set();
76
86
  opening;
77
87
  closed = false;
78
88
  closing;
79
89
  timeouts;
80
90
  static platform(config, descriptor, options = {}) {
81
91
  const { headers: custom, credentialHeadersProvider, ...connectionOptions } = options;
92
+ if (Object.keys(config.gatewayHeaders).some((name) => name.toLowerCase().startsWith("x-agentcore-accessor-")))
93
+ throw new ConfigError("MCP accessor headers must come from the request context");
82
94
  const customHeaders = mergeMCPHeaders({}, custom);
83
95
  const headers = mergeMCPHeaders(config.gatewayHeaders, custom);
84
96
  return new _MCPClient({ ...connectionOptions, url: mcpServerURL(config.mcpGatewayUrl, descriptor.mcpServerId), transport: "streamable-http", headersProvider: async () => {
@@ -95,7 +107,8 @@ var MCPClient = class _MCPClient {
95
107
  const definitions = [];
96
108
  let cursor;
97
109
  const deadline = AbortSignal.timeout(this.timeouts.metadataMs);
98
- const signal = AbortSignal.any([deadline, ...options.signal ? [options.signal] : []]);
110
+ const requestSignal = currentContext(false)?.signal;
111
+ const signal = AbortSignal.any([deadline, ...options.signal ? [options.signal] : [], ...requestSignal ? [requestSignal] : []]);
99
112
  do {
100
113
  const result = await session.client.listTools(cursor ? { cursor } : void 0, { signal, timeout: this.timeouts.metadataMs });
101
114
  definitions.push(...result.tools);
@@ -107,22 +120,25 @@ var MCPClient = class _MCPClient {
107
120
  parameters: definition.inputSchema,
108
121
  invoke: (arguments_) => this.callTool(definition.name, arguments_)
109
122
  }));
110
- });
123
+ }, options.headers);
111
124
  }
112
125
  async tools(options = {}) {
113
126
  return this.listTools(options);
114
127
  }
115
128
  async callTool(name, arguments_, options = {}) {
129
+ const requestSignal = currentContext(false)?.signal;
130
+ const signal = options.signal && requestSignal ? AbortSignal.any([options.signal, requestSignal]) : options.signal ?? requestSignal;
116
131
  return this.withSession("call_tool", (session) => session.client.callTool(
117
132
  { name, arguments: arguments_ },
118
133
  CallToolResultSchema,
119
- { timeout: this.timeouts.toolMs, signal: options.signal }
120
- ));
134
+ { timeout: this.timeouts.toolMs, signal }
135
+ ), options.headers);
121
136
  }
122
137
  close() {
123
138
  if (this.closing) return this.closing;
124
139
  this.closed = true;
125
140
  this.closing = (async () => {
141
+ await Promise.all([...this.scopedClients].map((client) => client.close()));
126
142
  if (this.opening && this.state) this.state.abort.abort();
127
143
  await this.opening?.catch(() => void 0);
128
144
  if (this.state) await this.retire(this.state);
@@ -164,9 +180,14 @@ var MCPClient = class _MCPClient {
164
180
  let transport;
165
181
  if (this.options.transport === "stdio") transport = new StdioClientTransport(this.options.server);
166
182
  else {
167
- const headers = await this.options.headersProvider?.() ?? {};
168
- const fetch = (input, init) => globalThis.fetch(input, { ...init, redirect: "manual", signal: AbortSignal.any([abort.signal, ...init?.signal ? [init.signal] : []]) });
169
- transport = this.options.transport === "sse" ? new SSEClientTransport(new URL(this.options.url), { requestInit: { headers }, fetch }) : new StreamableHTTPClientTransport(new URL(this.options.url), { requestInit: { headers }, fetch });
183
+ const options = this.options;
184
+ const fetch = async (input, init) => {
185
+ const resolved = mergeMCPHeaders(await options.headersProvider?.() ?? {}, callHeaders.getStore());
186
+ const headers = new Headers(init?.headers);
187
+ for (const [name, value] of Object.entries(resolved)) headers.set(name, value);
188
+ return globalThis.fetch(input, { ...init, headers, redirect: "manual", signal: AbortSignal.any([abort.signal, ...init?.signal ? [init.signal] : [], ...init?.method === "DELETE" ? [AbortSignal.timeout(this.timeouts.sessionMs)] : []]) });
189
+ };
190
+ transport = this.options.transport === "sse" ? new SSEClientTransport(new URL(this.options.url), { fetch }) : new StreamableHTTPClientTransport(new URL(this.options.url), { fetch });
170
191
  }
171
192
  const session = { client, transport, abort, invalid: false, active: /* @__PURE__ */ new Set() };
172
193
  this.state = session;
@@ -193,7 +214,32 @@ var MCPClient = class _MCPClient {
193
214
  clearTimeout(timer);
194
215
  }
195
216
  }
196
- async withSession(operation, call) {
217
+ async withSession(operation, call, headers) {
218
+ if (headers !== void 0) return callHeaders.run(mergeMCPHeaders({}, headers), () => this.withSession(operation, call));
219
+ if (this.closed) throw new Error("MCP client is closed");
220
+ const context = currentContext(false);
221
+ const wat = context?.headers["x-agentcore-accessor-wat"];
222
+ if (this.descriptor && wat && context && this.options.transport !== "stdio") {
223
+ let scoped = this.requestClients.get(context);
224
+ if (!scoped) {
225
+ const options = this.options;
226
+ scoped = _MCPClient.direct({ ...options, headersProvider: async () => ({
227
+ ...await options.headersProvider?.(),
228
+ "x-agentcore-accessor-wat": wat
229
+ }) });
230
+ this.requestClients.set(context, scoped);
231
+ this.scopedClients.add(scoped);
232
+ const child = scoped;
233
+ context.onClose(async () => {
234
+ try {
235
+ await child.close();
236
+ } finally {
237
+ this.scopedClients.delete(child);
238
+ }
239
+ });
240
+ }
241
+ return scoped.withSession(operation, call);
242
+ }
197
243
  const session = await this.acquire();
198
244
  if (this.closed) throw new Error("MCP client is closed");
199
245
  const request = call(session);
@@ -216,6 +262,11 @@ var MCPClient = class _MCPClient {
216
262
  session.invalid = true;
217
263
  session.closing = (async () => {
218
264
  await Promise.allSettled([...session.active]);
265
+ try {
266
+ if (session.transport instanceof StreamableHTTPClientTransport && session.transport.sessionId)
267
+ await session.transport.terminateSession();
268
+ } catch {
269
+ }
219
270
  session.abort.abort();
220
271
  try {
221
272
  await session.client.close();
@@ -233,4 +284,4 @@ export {
233
284
  mcpServerURL,
234
285
  MCPClient
235
286
  };
236
- //# sourceMappingURL=chunk-5UQIUMSF.js.map
287
+ //# sourceMappingURL=chunk-NSYCD5U4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp/client.ts","../src/mcp/headers.ts"],"sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';\nimport { StdioClientTransport, type StdioServerParameters } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { CallToolResultSchema, McpError, ErrorCode, type CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { inspect } from 'node:util';\nimport { ConfigError } from '../errors';\nimport { nullLogger, type Logger } from '../logging';\nimport type { MCPDescriptor } from '../controlplane/client';\nimport type { AgentConfig } from '../runtime/config';\nimport type { HeadersProvider } from '../model/client';\nimport { Tool, type ToolArguments } from '../integrations/common';\nimport { mergeMCPHeaders } from './headers';\nimport { currentContext } from '../runtime/context';\n\nexport interface MCPTimeouts { sessionMs?: number; metadataMs?: number; toolMs?: number; }\nexport interface ManagedMCPOptions extends MCPTimeouts { headers?: Readonly<Record<string, string>>; credentialName?: string; }\nexport type DirectMCPOptions = ({ url: string; transport?: 'streamable-http' | 'sse'; headersProvider?: HeadersProvider } |\n { transport: 'stdio'; server: StdioServerParameters }) & MCPTimeouts & { logger?: Logger };\nexport interface MCPCallOptions { signal?: AbortSignal; headers?: Readonly<Record<string, string>>; }\nconst callHeaders = new AsyncLocalStorage<Readonly<Record<string, string>>>();\ninterface Session {\n client: Client;\n transport: Transport;\n abort: AbortController;\n invalid: boolean;\n active: Set<Promise<unknown>>;\n closing?: Promise<void>;\n}\n\nexport function mcpServerURL(gatewayURL: string, id: string): string {\n const gateway = gatewayURL.replace(/\\/+$/, '').replace(/\\/(?:mcp|mcp-servers)$/, '');\n return `${gateway}/mcp-servers/${encodeURIComponent(id)}`;\n}\n\nexport class MCPClient {\n readonly descriptor?: MCPDescriptor;\n private readonly logger: Logger;\n private state?: Session;\n private readonly requestClients = new WeakMap<NonNullable<ReturnType<typeof currentContext>>, MCPClient>();\n private readonly scopedClients = new Set<MCPClient>();\n private opening?: Promise<Session>;\n private closed = false;\n private closing?: Promise<void>;\n private readonly timeouts: Required<MCPTimeouts>;\n\n private constructor(private readonly options: DirectMCPOptions, descriptor?: MCPDescriptor) {\n this.descriptor = descriptor;\n this.logger = options.logger ?? nullLogger;\n this.timeouts = { sessionMs: options.sessionMs ?? 30_000, metadataMs: options.metadataMs ?? 60_000, toolMs: options.toolMs ?? 600_000 };\n if (Object.values(this.timeouts).some((timeout) => !Number.isFinite(timeout) || timeout <= 0)) throw new ConfigError('MCP operation timeouts must be positive');\n if (options.transport !== 'stdio') {\n let url: URL;\n try { url = new URL(options.url); } catch { throw new ConfigError('Direct MCP URL must be HTTP(S)'); }\n if (!['http:', 'https:'].includes(url.protocol)) throw new ConfigError('Direct MCP URL must be HTTP(S)');\n if (options.transport !== undefined && !['streamable-http', 'sse'].includes(options.transport)) throw new ConfigError('MCP transport must be streamable-http, sse, or stdio');\n }\n this.logger.info('agentcore.mcp.client.created', { mode: descriptor ? 'managed' : 'direct', transport: options.transport ?? 'streamable-http', url: this.safeURL });\n }\n static platform(config: AgentConfig, descriptor: MCPDescriptor, options: Omit<ManagedMCPOptions, 'credentialName'> & {\n logger?: Logger; credentialHeadersProvider?: () => Promise<Record<string, string>>;\n } = {}): MCPClient {\n const { headers: custom, credentialHeadersProvider, ...connectionOptions } = options;\n if (Object.keys(config.gatewayHeaders).some((name) => name.toLowerCase().startsWith('x-agentcore-accessor-')))\n throw new ConfigError('MCP accessor headers must come from the request context');\n const customHeaders = mergeMCPHeaders({}, custom);\n const headers = mergeMCPHeaders(config.gatewayHeaders, custom);\n return new MCPClient({ ...connectionOptions, url: mcpServerURL(config.mcpGatewayUrl, descriptor.mcpServerId), transport: 'streamable-http', headersProvider: async () => {\n if (!credentialHeadersProvider) return headers;\n const bound = mergeMCPHeaders(config.gatewayHeaders, await credentialHeadersProvider());\n return mergeMCPHeaders(bound, customHeaders);\n } }, descriptor);\n }\n static direct(options: DirectMCPOptions): MCPClient { return new MCPClient(options); }\n\n async listTools(options: MCPCallOptions = {}): Promise<Tool[]> {\n return this.withSession('list_tools', async (session) => {\n const definitions: Awaited<ReturnType<Client['listTools']>>['tools'] = [];\n let cursor: string | undefined;\n const deadline = AbortSignal.timeout(this.timeouts.metadataMs);\n const requestSignal = currentContext(false)?.signal;\n const signal = AbortSignal.any([deadline, ...(options.signal ? [options.signal] : []), ...(requestSignal ? [requestSignal] : [])]);\n do {\n const result = await session.client.listTools(cursor ? { cursor } : undefined, { signal, timeout: this.timeouts.metadataMs });\n definitions.push(...result.tools); cursor = result.nextCursor;\n } while (cursor);\n return definitions.map((definition) => new Tool({ name: definition.name, description: definition.description ?? '', parameters: definition.inputSchema,\n invoke: (arguments_) => this.callTool(definition.name, arguments_),\n }));\n }, options.headers);\n }\n async tools(options: MCPCallOptions = {}): Promise<Tool[]> { return this.listTools(options); }\n async callTool(name: string, arguments_: ToolArguments, options: MCPCallOptions = {}): Promise<CallToolResult> {\n const requestSignal = currentContext(false)?.signal;\n const signal = options.signal && requestSignal ? AbortSignal.any([options.signal, requestSignal]) : options.signal ?? requestSignal;\n return this.withSession('call_tool', (session) => session.client.callTool({ name, arguments: arguments_ }, CallToolResultSchema,\n { timeout: this.timeouts.toolMs, signal }) as Promise<CallToolResult>, options.headers);\n }\n\n close(): Promise<void> {\n if (this.closing) return this.closing;\n this.closed = true;\n this.closing = (async () => {\n await Promise.all([...this.scopedClients].map((client) => client.close()));\n if (this.opening && this.state) this.state.abort.abort();\n await this.opening?.catch(() => undefined);\n if (this.state) await this.retire(this.state);\n })();\n return this.closing;\n }\n [inspect.custom](): string { return `MCPClient(url=${this.safeURL}, <redacted>)`; }\n toJSON(): string { return this[inspect.custom](); }\n\n private get safeURL(): string {\n if (this.options.transport === 'stdio') return 'stdio';\n const url = new URL(this.options.url); return `${url.origin}${url.pathname}`;\n }\n private async acquire(): Promise<Session> {\n for (;;) {\n if (this.closed) throw new Error('MCP client is closed');\n if (this.opening) return this.opening;\n if (this.state?.invalid) { await this.retire(this.state); continue; }\n if (this.state) return this.state;\n const request = this.open(); this.opening = request;\n try { return await request; } finally { this.opening = undefined; }\n }\n }\n private async open(): Promise<Session> {\n const client = new Client({ name: 'agentcore-sdk', version: '0.1.0' });\n const abort = new AbortController();\n let transport: Transport;\n if (this.options.transport === 'stdio') transport = new StdioClientTransport(this.options.server);\n else {\n const options = this.options;\n const fetch: typeof globalThis.fetch = async (input, init) => {\n const resolved = mergeMCPHeaders(await options.headersProvider?.() ?? {}, callHeaders.getStore());\n const headers = new Headers(init?.headers);\n for (const [name, value] of Object.entries(resolved)) headers.set(name, value);\n return globalThis.fetch(input, { ...init, headers, redirect: 'manual', signal: AbortSignal.any([abort.signal, ...(init?.signal ? [init.signal] : []), ...(init?.method === 'DELETE' ? [AbortSignal.timeout(this.timeouts.sessionMs)] : [])]) });\n };\n transport = this.options.transport === 'sse'\n ? new SSEClientTransport(new URL(this.options.url), { fetch })\n : new StreamableHTTPClientTransport(new URL(this.options.url), { fetch });\n }\n const session: Session = { client, transport, abort, invalid: false, active: new Set() };\n this.state = session;\n const timer = setTimeout(() => abort.abort(new Error('MCP session open timed out')), this.timeouts.sessionMs);\n client.onclose = () => { session.invalid = true; };\n client.onerror = (error) => {\n if (this.closed || session.closing) return;\n session.invalid = true;\n this.logger.warn('agentcore.mcp.session.error', { url: this.safeURL, errorType: error.name });\n };\n this.logger.info('agentcore.mcp.session.open.started', { url: this.safeURL, timeoutMs: this.timeouts.sessionMs });\n try {\n await client.connect(transport, { signal: abort.signal, timeout: this.timeouts.sessionMs });\n abort.signal.throwIfAborted();\n if (this.closed) throw new Error('MCP client is closed');\n this.logger.info('agentcore.mcp.session.open.succeeded', { url: this.safeURL });\n return session;\n } catch (error) { await this.retire(session); throw error; }\n finally { clearTimeout(timer); }\n }\n\n private async withSession<T>(operation: string, call: (session: Session) => Promise<T>, headers?: Readonly<Record<string, string>>): Promise<T> {\n if (headers !== undefined) return callHeaders.run(mergeMCPHeaders({}, headers), () => this.withSession(operation, call));\n if (this.closed) throw new Error('MCP client is closed');\n const context = currentContext(false);\n const wat = context?.headers['x-agentcore-accessor-wat'];\n if (this.descriptor && wat && context && this.options.transport !== 'stdio') {\n let scoped = this.requestClients.get(context);\n if (!scoped) {\n const options = this.options;\n scoped = MCPClient.direct({ ...options, headersProvider: async () => ({\n ...await options.headersProvider?.(), 'x-agentcore-accessor-wat': wat,\n }) });\n this.requestClients.set(context, scoped);\n this.scopedClients.add(scoped);\n const child = scoped;\n context.onClose(async () => {\n try { await child.close(); } finally { this.scopedClients.delete(child); }\n });\n }\n return scoped.withSession(operation, call);\n }\n const session = await this.acquire();\n if (this.closed) throw new Error('MCP client is closed');\n const request = call(session); session.active.add(request);\n this.logger.info('agentcore.mcp.request.started', { operation, url: this.safeURL });\n try { const result = await request; this.logger.info('agentcore.mcp.request.succeeded', { operation, url: this.safeURL }); return result; }\n catch (error) {\n if (!(error instanceof McpError) || [ErrorCode.ConnectionClosed, ErrorCode.RequestTimeout].includes(error.code)) session.invalid = true;\n this.logger.warn('agentcore.mcp.request.failed', { operation, url: this.safeURL, errorType: error instanceof Error ? error.name : 'unknown' });\n throw error;\n } finally { session.active.delete(request); }\n }\n private retire(session: Session): Promise<void> {\n if (session.closing) return session.closing;\n session.invalid = true;\n session.closing = (async () => {\n await Promise.allSettled([...session.active]);\n try {\n if (session.transport instanceof StreamableHTTPClientTransport && session.transport.sessionId)\n await session.transport.terminateSession();\n } catch { /* Closing the transport below still revokes the local Session. */ }\n session.abort.abort();\n try { await session.client.close(); }\n catch (error) { this.logger.warn('agentcore.mcp.session.close.failed', { errorType: error instanceof Error ? error.name : 'unknown', url: this.safeURL }); }\n if (this.state === session) this.state = undefined;\n })();\n return session.closing;\n }\n}\n","import { ConfigError } from '../errors';\n\nconst transportHeaders = new Set([\n 'host', 'content-length', 'connection', 'transfer-encoding', 'upgrade',\n 'mcp-session-id', 'mcp-protocol-version', 'last-event-id', 'content-type', 'accept',\n 'x-agentcore-accessor-wat', 'x-agentcore-accessor-jwt', 'x-agentcore-accessor-matrixid',\n]);\n\n/** Copy fixed headers, rejecting case-insensitive credential/transport conflicts. */\nexport function mergeMCPHeaders(base: Readonly<Record<string, string>>, custom?: Readonly<Record<string, string>>): Record<string, string> {\n const result = { ...base };\n if (custom === undefined) return result;\n if (!custom || typeof custom !== 'object' || Array.isArray(custom)) throw new ConfigError('MCP headers must be an object');\n const protectedNames = new Set([...Object.keys(base).map((name) => name.toLowerCase()), ...transportHeaders, 'authorization']);\n const seen = new Set<string>();\n for (const [name, value] of Object.entries(custom)) {\n if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) throw new ConfigError('Invalid MCP header name');\n if (typeof value !== 'string' || /[\\x00-\\x1f\\x7f]/.test(value)) throw new ConfigError('MCP header values must be strings without control characters');\n const folded = name.toLowerCase();\n if (protectedNames.has(folded)) throw new ConfigError(`MCP header is protected: ${folded}`);\n if (seen.has(folded)) throw new ConfigError(`Duplicate MCP header: ${folded}`);\n seen.add(folded);\n Object.defineProperty(result, folded, { value, enumerable: true, configurable: true, writable: true });\n }\n return result;\n}\n"],"mappings":"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","names":[]}
@@ -6,10 +6,20 @@ import {
6
6
  import { AsyncLocalStorage } from "async_hooks";
7
7
  import { inspect } from "util";
8
8
  var RequestContext = class {
9
- headers;
10
- constructor(headers) {
9
+ constructor(headers, signal) {
10
+ this.signal = signal;
11
11
  this.headers = Object.freeze(Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v])));
12
12
  }
13
+ signal;
14
+ cleanups = [];
15
+ onClose(cleanup) {
16
+ this.cleanups.push(cleanup);
17
+ }
18
+ async close() {
19
+ const results = await Promise.allSettled(this.cleanups.splice(0).map((cleanup) => Promise.resolve().then(cleanup)));
20
+ for (const result of results) if (result.status === "rejected") throw result.reason;
21
+ }
22
+ headers;
13
23
  [inspect.custom]() {
14
24
  return "RequestContext(headers=<redacted>)";
15
25
  }
@@ -32,4 +42,4 @@ export {
32
42
  currentContext,
33
43
  useContext
34
44
  };
35
- //# sourceMappingURL=chunk-6NNPSP3O.js.map
45
+ //# sourceMappingURL=chunk-YBBMBSFN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/context.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport { inspect } from 'node:util';\nimport { ContextError } from '../errors';\n\nexport class RequestContext {\n private readonly cleanups: Array<() => Promise<void>> = [];\n onClose(cleanup: () => Promise<void>): void { this.cleanups.push(cleanup); }\n async close(): Promise<void> {\n const results = await Promise.allSettled(this.cleanups.splice(0).map(cleanup => Promise.resolve().then(cleanup)));\n for (const result of results) if (result.status === 'rejected') throw result.reason;\n }\n readonly headers: Readonly<Record<string, string>>;\n constructor(headers: Readonly<Record<string, string>>, readonly signal?: AbortSignal) {\n this.headers = Object.freeze(Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v])));\n }\n [inspect.custom](): string { return 'RequestContext(headers=<redacted>)'; }\n toJSON(): string { return this[inspect.custom](); }\n}\nconst context = new AsyncLocalStorage<RequestContext>();\nexport function currentContext(required = true): RequestContext | undefined {\n const value = context.getStore();\n if (!value && required) throw new ContextError('no AgentCore request context is active');\n return value;\n}\nexport function useContext<T>(value: RequestContext, callback: () => T): T {\n return context.run(value, callback);\n}\n"],"mappings":";;;;;AAAA,SAAS,yBAAyB;AAClC,SAAS,eAAe;AAGjB,IAAM,iBAAN,MAAqB;AAAA,EAQ1B,YAAY,SAAoD,QAAsB;AAAtB;AAC9D,SAAK,UAAU,OAAO,OAAO,OAAO,YAAY,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC;AAAA,EAChH;AAAA,EAFgE;AAAA,EAP/C,WAAuC,CAAC;AAAA,EACzD,QAAQ,SAAoC;AAAE,SAAK,SAAS,KAAK,OAAO;AAAA,EAAG;AAAA,EAC3E,MAAM,QAAuB;AAC3B,UAAM,UAAU,MAAM,QAAQ,WAAW,KAAK,SAAS,OAAO,CAAC,EAAE,IAAI,aAAW,QAAQ,QAAQ,EAAE,KAAK,OAAO,CAAC,CAAC;AAChH,eAAW,UAAU,QAAS,KAAI,OAAO,WAAW,WAAY,OAAM,OAAO;AAAA,EAC/E;AAAA,EACS;AAAA,EAIT,CAAC,QAAQ,MAAM,IAAY;AAAE,WAAO;AAAA,EAAsC;AAAA,EAC1E,SAAiB;AAAE,WAAO,KAAK,QAAQ,MAAM,EAAE;AAAA,EAAG;AACpD;AACA,IAAM,UAAU,IAAI,kBAAkC;AAC/C,SAAS,eAAe,WAAW,MAAkC;AAC1E,QAAM,QAAQ,QAAQ,SAAS;AAC/B,MAAI,CAAC,SAAS,SAAU,OAAM,IAAI,aAAa,wCAAwC;AACvF,SAAO;AACT;AACO,SAAS,WAAc,OAAuB,UAAsB;AACzE,SAAO,QAAQ,IAAI,OAAO,QAAQ;AACpC;","names":[]}
@@ -1,8 +1,12 @@
1
1
  import { inspect } from 'node:util';
2
2
 
3
3
  declare class RequestContext {
4
+ readonly signal?: AbortSignal | undefined;
5
+ private readonly cleanups;
6
+ onClose(cleanup: () => Promise<void>): void;
7
+ close(): Promise<void>;
4
8
  readonly headers: Readonly<Record<string, string>>;
5
- constructor(headers: Readonly<Record<string, string>>);
9
+ constructor(headers: Readonly<Record<string, string>>, signal?: AbortSignal | undefined);
6
10
  [inspect.custom](): string;
7
11
  toJSON(): string;
8
12
  }
@@ -1,8 +1,12 @@
1
1
  import { inspect } from 'node:util';
2
2
 
3
3
  declare class RequestContext {
4
+ readonly signal?: AbortSignal | undefined;
5
+ private readonly cleanups;
6
+ onClose(cleanup: () => Promise<void>): void;
7
+ close(): Promise<void>;
4
8
  readonly headers: Readonly<Record<string, string>>;
5
- constructor(headers: Readonly<Record<string, string>>);
9
+ constructor(headers: Readonly<Record<string, string>>, signal?: AbortSignal | undefined);
6
10
  [inspect.custom](): string;
7
11
  toJSON(): string;
8
12
  }