tiny-http-mcp-server 0.1.21 → 0.1.23

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.
@@ -152,11 +152,17 @@ var HttpResponseMessages = class {
152
152
  };
153
153
 
154
154
  // ../toolcraft-schema/dist/json.js
155
- function isJsonValue(value) {
155
+ function isJsonValue(value, options = {}) {
156
+ const maxNodes = options.maxNodes ?? 1e4;
157
+ const maxDepth = options.maxDepth ?? 64;
158
+ if (!Number.isSafeInteger(maxNodes) || maxNodes < 1)
159
+ throw new Error("maxNodes must be a positive safe integer");
160
+ if (!Number.isSafeInteger(maxDepth) || maxDepth < 0 || maxDepth > 256)
161
+ throw new Error("maxDepth must be an integer between 0 and 256");
156
162
  const ancestors = /* @__PURE__ */ new Set();
157
163
  let nodes = 0;
158
164
  const visit = (item, depth) => {
159
- if (++nodes > 1e4 || depth > 64)
165
+ if (++nodes > maxNodes || depth > maxDepth)
160
166
  return false;
161
167
  if (item === null || typeof item === "string" || typeof item === "boolean")
162
168
  return true;
@@ -182,7 +188,7 @@ function isJsonValue(value) {
182
188
  ancestors.add(item);
183
189
  let valid = true;
184
190
  if (Array.isArray(item)) {
185
- if (item.length > 1e4)
191
+ if (item.length > maxNodes)
186
192
  valid = false;
187
193
  else
188
194
  for (let index = 0; index < item.length; index++) {
@@ -4102,47 +4108,137 @@ function getOwnEntry4(record2, key2) {
4102
4108
 
4103
4109
  // ../mcp-oauth/dist/client/loopback-authorization.js
4104
4110
  async function createLoopbackAuthorizationSession(options = {}) {
4105
- const callbackPath = options.callbackPath ?? "/callback";
4111
+ options.signal?.throwIfAborted();
4112
+ const timeoutMs = options.timeoutMs ?? 12e4;
4113
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
4114
+ throw new Error("OAuth authorization timeoutMs must be a positive supported timer interval");
4115
+ const target = loopbackTarget(options);
4106
4116
  const server = options.createServer ? options.createServer() : http.createServer();
4107
- const port = await startServer(server);
4108
- const redirectUri = `http://127.0.0.1:${port}${callbackPath}`;
4117
+ const controller = new AbortController();
4118
+ let closed = false;
4119
+ let used = false;
4120
+ const callerAbort = () => controller.abort(options.signal?.reason);
4121
+ const teardown = () => {
4122
+ if (closed)
4123
+ return;
4124
+ closed = true;
4125
+ clearTimeout(timer);
4126
+ options.signal?.removeEventListener("abort", callerAbort);
4127
+ server.closeAllConnections?.();
4128
+ server.close();
4129
+ };
4130
+ const timer = setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
4131
+ timer.unref?.();
4132
+ controller.signal.addEventListener("abort", teardown, { once: true });
4133
+ options.signal?.addEventListener("abort", callerAbort, { once: true });
4134
+ if (options.signal?.aborted)
4135
+ callerAbort();
4136
+ let port;
4137
+ try {
4138
+ port = await startServer(server, target.port, target.host, controller.signal);
4139
+ } catch (error) {
4140
+ controller.abort(error);
4141
+ throw error;
4142
+ }
4143
+ const redirectUri = options.redirectUri ?? `http://127.0.0.1:${port}${target.callbackPath}`;
4109
4144
  return {
4110
4145
  redirectUri,
4111
4146
  async waitForCode(authorizationUrl) {
4112
- return waitForAuthorizationCode(server, authorizationUrl, options, callbackPath);
4147
+ controller.signal.throwIfAborted();
4148
+ if (used)
4149
+ throw new Error("OAuth authorization session has already been used");
4150
+ used = true;
4151
+ try {
4152
+ return await waitForAuthorizationCode(server, authorizationUrl, options, target.callbackPath, controller.signal);
4153
+ } finally {
4154
+ clearTimeout(timer);
4155
+ }
4113
4156
  },
4114
4157
  close() {
4115
- server.closeAllConnections?.();
4116
- server.close();
4158
+ controller.abort(new Error("OAuth authorization session closed"));
4117
4159
  }
4118
4160
  };
4119
4161
  }
4120
- async function startServer(server) {
4162
+ function loopbackTarget(options) {
4163
+ if (options.redirectUri !== void 0) {
4164
+ let url;
4165
+ try {
4166
+ url = new URL(options.redirectUri);
4167
+ } catch (cause) {
4168
+ throw new Error("Invalid OAuth loopback redirect URI", { cause });
4169
+ }
4170
+ const forbiddenQuery = ["code", "state", "error", "error_description", "iss"];
4171
+ if (url.protocol !== "http:" || !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname) || url.username || url.password || url.hash || url.port === "0" || forbiddenQuery.some((name) => url.searchParams.has(name)) || [...options.redirectUri].some((char) => char.codePointAt(0) <= 32) || options.callbackPath !== void 0 && options.callbackPath !== url.pathname)
4172
+ throw new Error("Invalid OAuth loopback redirect URI");
4173
+ return { port: url.port ? Number(url.port) : 80, host: url.hostname === "[::1]" ? "::1" : url.hostname, callbackPath: url.pathname };
4174
+ }
4175
+ const callbackPath = options.callbackPath ?? "/callback";
4176
+ const parsed = new URL(callbackPath, "http://127.0.0.1");
4177
+ if (!callbackPath.startsWith("/") || parsed.origin !== "http://127.0.0.1" || parsed.pathname !== callbackPath || parsed.search || parsed.hash)
4178
+ throw new Error("Invalid OAuth loopback callback path");
4179
+ return { port: 0, host: "127.0.0.1", callbackPath };
4180
+ }
4181
+ async function startServer(server, port, host, signal) {
4182
+ signal.throwIfAborted();
4121
4183
  return new Promise((resolve, reject) => {
4122
- const handleError = (error) => {
4184
+ const cleanup = () => {
4123
4185
  server.off("error", handleError);
4186
+ signal.removeEventListener("abort", aborted);
4187
+ };
4188
+ const handleError = (error) => {
4189
+ cleanup();
4124
4190
  reject(error);
4125
4191
  };
4192
+ const aborted = () => {
4193
+ cleanup();
4194
+ reject(signal.reason);
4195
+ };
4126
4196
  server.once("error", handleError);
4127
- server.listen(0, "127.0.0.1", () => {
4128
- server.off("error", handleError);
4129
- const address = server.address();
4130
- resolve(address.port);
4131
- });
4197
+ signal.addEventListener("abort", aborted, { once: true });
4198
+ try {
4199
+ server.listen(port, host, () => {
4200
+ cleanup();
4201
+ if (signal.aborted) {
4202
+ server.close();
4203
+ reject(signal.reason);
4204
+ return;
4205
+ }
4206
+ const address = server.address();
4207
+ if (address === null || typeof address === "string") {
4208
+ reject(new Error("OAuth listener has no TCP address"));
4209
+ return;
4210
+ }
4211
+ resolve(address.port);
4212
+ });
4213
+ } catch (error) {
4214
+ cleanup();
4215
+ reject(error);
4216
+ }
4132
4217
  });
4133
4218
  }
4134
- function waitForAuthorizationCode(server, authorizationUrl, options, callbackPath) {
4219
+ function waitForAuthorizationCode(server, authorizationUrl, options, callbackPath, signal) {
4220
+ signal.throwIfAborted();
4135
4221
  const expectedAuthorization = readExpectedAuthorizationCallback(authorizationUrl);
4136
4222
  return new Promise((resolve, reject) => {
4137
4223
  let settled = false;
4138
4224
  const settle = (fn) => {
4139
- if (!settled) {
4140
- settled = true;
4141
- fn();
4142
- }
4225
+ if (settled)
4226
+ return;
4227
+ settled = true;
4228
+ server.off("request", request);
4229
+ signal.removeEventListener("abort", aborted);
4230
+ fn();
4143
4231
  };
4144
- server.on("request", (req, res) => {
4145
- const url = new URL(req.url ?? "/", "http://127.0.0.1");
4232
+ const aborted = () => settle(() => reject(signal.reason));
4233
+ const request = (req, res) => {
4234
+ let url;
4235
+ try {
4236
+ url = new URL(req.url ?? "/", "http://127.0.0.1");
4237
+ } catch {
4238
+ res.writeHead(400);
4239
+ res.end("Invalid callback URL");
4240
+ return;
4241
+ }
4146
4242
  if (url.pathname !== callbackPath) {
4147
4243
  res.writeHead(404);
4148
4244
  res.end("Not found");
@@ -4157,21 +4253,8 @@ function waitForAuthorizationCode(server, authorizationUrl, options, callbackPat
4157
4253
  };
4158
4254
  try {
4159
4255
  validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
4160
- } catch (error) {
4161
- res.writeHead(400);
4162
- res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
4163
- settle(() => reject(error instanceof Error ? error : new Error(String(error))));
4164
- return;
4165
- }
4166
- const authorizationError = callbackParameters.error;
4167
- if (authorizationError !== null) {
4168
- const description = callbackParameters.errorDescription ?? authorizationError;
4169
- res.writeHead(400);
4170
- res.end(`Authorization failed: ${description}`);
4171
- settle(() => reject(createAuthorizationError(authorizationError, description)));
4172
- return;
4173
- }
4174
- try {
4256
+ if (callbackParameters.error !== null)
4257
+ throw createAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
4175
4258
  const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
4176
4259
  res.writeHead(200, { "Content-Type": "text/html" });
4177
4260
  res.end(buildSuccessPage(options.landingPage));
@@ -4179,35 +4262,27 @@ function waitForAuthorizationCode(server, authorizationUrl, options, callbackPat
4179
4262
  } catch (error) {
4180
4263
  res.writeHead(400);
4181
4264
  res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
4182
- settle(() => reject(error instanceof Error ? error : new Error(String(error))));
4265
+ settle(() => reject(error));
4183
4266
  }
4184
- });
4267
+ };
4268
+ server.on("request", request);
4269
+ signal.addEventListener("abort", aborted, { once: true });
4185
4270
  if (options.readLine !== void 0) {
4186
- options.readLine().then((input) => {
4187
- const callbackParameters = extractCallbackParametersFromInput(input);
4188
- if (callbackParameters === null) {
4189
- settle(() => reject(new Error("OAuth callback missing authorization code")));
4271
+ void Promise.resolve().then(() => settled ? void 0 : options.readLine()).then((input) => {
4272
+ if (settled)
4190
4273
  return;
4191
- }
4192
- try {
4193
- validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
4194
- if (callbackParameters.error !== null) {
4195
- const description = callbackParameters.errorDescription ?? callbackParameters.error;
4196
- throw createAuthorizationError(callbackParameters.error, description);
4197
- }
4198
- const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
4199
- settle(() => resolve(code));
4200
- } catch (error) {
4201
- settle(() => reject(error instanceof Error ? error : new Error(String(error))));
4202
- }
4203
- }).catch((error) => {
4204
- settle(() => reject(error instanceof Error ? error : new Error(String(error))));
4205
- });
4274
+ const callbackParameters = extractCallbackParametersFromInput(input);
4275
+ if (callbackParameters === null)
4276
+ throw new Error("OAuth callback missing authorization code");
4277
+ validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
4278
+ if (callbackParameters.error !== null)
4279
+ throw createAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
4280
+ const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
4281
+ settle(() => resolve(code));
4282
+ }).catch((error) => settle(() => reject(error)));
4206
4283
  }
4207
4284
  if (options.openBrowser !== void 0) {
4208
- options.openBrowser(authorizationUrl).catch((error) => {
4209
- settle(() => reject(error));
4210
- });
4285
+ void Promise.resolve().then(() => settled ? void 0 : options.openBrowser(authorizationUrl)).catch((error) => settle(() => reject(error)));
4211
4286
  }
4212
4287
  });
4213
4288
  }
@@ -4387,6 +4462,7 @@ async function exchangeAuthorizationCode(input) {
4387
4462
  resource
4388
4463
  },
4389
4464
  fetch: input.fetch,
4465
+ signal: input.signal,
4390
4466
  now: input.now
4391
4467
  });
4392
4468
  }
@@ -4402,6 +4478,7 @@ async function refreshAccessToken(input) {
4402
4478
  resource
4403
4479
  },
4404
4480
  fetch: input.fetch,
4481
+ signal: input.signal,
4405
4482
  now: input.now
4406
4483
  });
4407
4484
  }
@@ -4413,7 +4490,9 @@ async function requestTokens(input) {
4413
4490
  if (input.clientSecret !== void 0) {
4414
4491
  body.set("client_secret", input.clientSecret);
4415
4492
  }
4416
- const signal = AbortSignal.timeout(3e4);
4493
+ input.signal?.throwIfAborted();
4494
+ const deadline = AbortSignal.timeout(3e4);
4495
+ const signal = input.signal === void 0 ? deadline : AbortSignal.any([input.signal, deadline]);
4417
4496
  const response = await fetchMcpResponse(input.fetch, input.tokenEndpoint, {
4418
4497
  method: "POST",
4419
4498
  headers: {
@@ -4521,12 +4600,34 @@ function createDefaultOAuthClientProvider(options) {
4521
4600
  const registeredClients = /* @__PURE__ */ new Map();
4522
4601
  const refreshPromises = /* @__PURE__ */ new Map();
4523
4602
  const authorizationPromises = /* @__PURE__ */ new Map();
4603
+ if (options.initialGrant !== void 0) {
4604
+ let resource;
4605
+ try {
4606
+ resource = new URL2(options.initialGrant.resource);
4607
+ } catch {
4608
+ throw new Error("OAuth initial grant resource must be an absolute HTTP URL");
4609
+ }
4610
+ if (resource.protocol !== "http:" && resource.protocol !== "https:" || resource.username || resource.password || resource.hash)
4611
+ throw new Error("OAuth initial grant resource must be an HTTP URL without credentials or fragments");
4612
+ }
4613
+ const initialGrant = options.initialGrant === void 0 ? void 0 : {
4614
+ resource: canonicalizeResourceIndicator(options.initialGrant.resource),
4615
+ tokens: normalizeStoredTokens(options.initialGrant.tokens),
4616
+ client: normalizeConfiguredClient(options.client)
4617
+ };
4618
+ if (initialGrant !== void 0 && (initialGrant.tokens === void 0 || initialGrant.client === null))
4619
+ throw new Error("OAuth initial grant requires valid tokens and the original client ID");
4620
+ let initialGrantConsumed = false;
4524
4621
  return {
4525
4622
  async authorizeRequest(input) {
4526
4623
  assertNoAccessTokenInUrl(input.requestUrl, "Protected resource request URL");
4527
4624
  const requestUrl = canonicalizeResourceIndicator(input.requestUrl);
4528
- const session = await ensureAuthorizedSession(requestUrl, void 0, input.fetch, false);
4625
+ const session = await ensureAuthorizedSession(requestUrl, void 0, input.fetch, false, false, input.signal);
4529
4626
  const accessToken = session?.tokens?.accessToken;
4627
+ if (session === null && !initialGrantConsumed && initialGrant?.resource === requestUrl && initialGrant.tokens !== void 0 && !isExpired(initialGrant.tokens, now)) {
4628
+ input.headers.set("Authorization", `Bearer ${initialGrant.tokens.accessToken}`);
4629
+ return;
4630
+ }
4530
4631
  if (session === null || accessToken === void 0 || session.tokens === void 0 || isExpired(session.tokens, now)) {
4531
4632
  return;
4532
4633
  }
@@ -4539,16 +4640,17 @@ function createDefaultOAuthClientProvider(options) {
4539
4640
  const requestUrl = canonicalizeResourceIndicator(input.requestUrl);
4540
4641
  const resource = canonicalizeResourceIndicator(input.discovery.resource);
4541
4642
  assertRequestMatchesResource(requestUrl, resource);
4542
- const forceRefresh = hasCachedAccessToken(await loadSession(resource)) && input.challenge?.params.error === "invalid_token";
4643
+ const forceRefresh = (hasCachedAccessToken(await loadSession(resource)) || !initialGrantConsumed && initialGrant?.resource === resource) && input.challenge?.params.error === "invalid_token";
4543
4644
  const session = await ensureAuthorizedSession(resource, {
4544
4645
  ...input.discovery,
4545
4646
  resource
4546
- }, input.fetch, true, forceRefresh);
4647
+ }, input.fetch, true, forceRefresh, input.signal);
4547
4648
  if (session?.tokens?.accessToken === void 0) {
4548
4649
  return { action: "fail" };
4549
4650
  }
4550
4651
  return { action: "retry" };
4551
4652
  } catch (error) {
4653
+ input.signal?.throwIfAborted();
4552
4654
  return {
4553
4655
  action: "fail",
4554
4656
  error: error instanceof Error ? error : new Error(String(error))
@@ -4556,9 +4658,13 @@ function createDefaultOAuthClientProvider(options) {
4556
4658
  }
4557
4659
  }
4558
4660
  };
4559
- async function ensureAuthorizedSession(resource, discovery, fetch2, allowInteractive, forceRefresh = false) {
4661
+ async function ensureAuthorizedSession(resource, discovery, fetch2, allowInteractive, forceRefresh = false, signal) {
4662
+ signal?.throwIfAborted();
4560
4663
  const canonicalResource = canonicalizeResourceIndicator(resource);
4561
4664
  let session = await loadSession(canonicalResource);
4665
+ if (session !== null && initialGrant?.resource === canonicalResource)
4666
+ initialGrantConsumed = true;
4667
+ signal?.throwIfAborted();
4562
4668
  if (discovery !== void 0 && getOwnString2(discovery.authorizationServerMetadata, "issuer") !== discovery.authorizationServer) {
4563
4669
  throw new Error("OAuth discovery authorization-server issuer mismatch");
4564
4670
  }
@@ -4566,12 +4672,25 @@ function createDefaultOAuthClientProvider(options) {
4566
4672
  await clearSession(canonicalResource);
4567
4673
  session = null;
4568
4674
  }
4675
+ if (session === null && discovery !== void 0 && !initialGrantConsumed && initialGrant?.resource === canonicalResource && initialGrant.tokens !== void 0 && initialGrant.client !== null) {
4676
+ assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
4677
+ session = {
4678
+ resource: canonicalResource,
4679
+ authorizationServer: discovery.authorizationServer,
4680
+ client: initialGrant.client,
4681
+ tokens: initialGrant.tokens,
4682
+ discovery: toStoredDiscovery(discovery)
4683
+ };
4684
+ await saveSession(canonicalResource, session);
4685
+ initialGrantConsumed = true;
4686
+ signal?.throwIfAborted();
4687
+ }
4569
4688
  const sessionDiscovery = resolveDiscovery(discovery, session);
4570
4689
  if (session?.tokens !== void 0 && !forceRefresh && !isExpired(session.tokens, now)) {
4571
4690
  return session;
4572
4691
  }
4573
4692
  if (session?.tokens?.refreshToken !== void 0 && sessionDiscovery !== void 0 && (forceRefresh || isExpired(session.tokens, now))) {
4574
- session = await refreshSession(canonicalResource, session, sessionDiscovery, fetch2);
4693
+ session = await refreshSession(canonicalResource, session, sessionDiscovery, fetch2, signal);
4575
4694
  if (session?.tokens !== void 0 && !isExpired(session.tokens, now)) {
4576
4695
  return session;
4577
4696
  }
@@ -4583,9 +4702,12 @@ function createDefaultOAuthClientProvider(options) {
4583
4702
  if (!allowInteractive || sessionDiscovery === void 0) {
4584
4703
  return session;
4585
4704
  }
4586
- return authorizeSession(canonicalResource, session, sessionDiscovery, fetch2);
4705
+ if (options.allowInteractive === false)
4706
+ throw new Error("OAuth interactive authorization is disabled");
4707
+ return authorizeSession(canonicalResource, session, sessionDiscovery, fetch2, signal);
4587
4708
  }
4588
- async function refreshSession(resource, session, discovery, fetch2) {
4709
+ async function refreshSession(resource, session, discovery, fetch2, signal) {
4710
+ signal?.throwIfAborted();
4589
4711
  assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
4590
4712
  const inFlight = refreshPromises.get(resource);
4591
4713
  if (inFlight !== void 0) {
@@ -4607,10 +4729,12 @@ function createDefaultOAuthClientProvider(options) {
4607
4729
  refreshToken: session.tokens.refreshToken,
4608
4730
  resource,
4609
4731
  fetch: fetch2,
4732
+ signal,
4610
4733
  now
4611
4734
  });
4612
4735
  break;
4613
4736
  } catch (error) {
4737
+ signal?.throwIfAborted();
4614
4738
  if (error instanceof OAuthError && error.error === "invalid_grant") {
4615
4739
  const clearedSession = clearSessionTokens(session);
4616
4740
  await saveSession(resource, clearedSession);
@@ -4645,7 +4769,8 @@ function createDefaultOAuthClientProvider(options) {
4645
4769
  refreshPromises.set(resource, promise);
4646
4770
  return promise;
4647
4771
  }
4648
- async function authorizeSession(resource, existingSession, discovery, fetch2) {
4772
+ async function authorizeSession(resource, existingSession, discovery, fetch2, signal) {
4773
+ signal?.throwIfAborted();
4649
4774
  const inFlight = authorizationPromises.get(resource);
4650
4775
  if (inFlight !== void 0) {
4651
4776
  return inFlight;
@@ -4661,11 +4786,14 @@ function createDefaultOAuthClientProvider(options) {
4661
4786
  openBrowser: options.browser.openBrowser,
4662
4787
  readLine: options.browser.readLine,
4663
4788
  createServer: options.browser.createServer,
4664
- landingPage: options.browser.landingPage
4789
+ landingPage: options.browser.landingPage,
4790
+ redirectUri: options.browser.redirectUri,
4791
+ signal: options.browser.signal === void 0 ? signal : signal === void 0 ? options.browser.signal : AbortSignal.any([signal, options.browser.signal]),
4792
+ timeoutMs: options.browser.timeoutMs
4665
4793
  });
4666
4794
  let resolvedClient = null;
4667
4795
  try {
4668
- resolvedClient = await resolveClient(currentSession, discovery, loopback.redirectUri, fetch2);
4796
+ resolvedClient = await resolveClient(currentSession, discovery, loopback.redirectUri, fetch2, signal);
4669
4797
  const sessionWithoutTokens = {
4670
4798
  resource,
4671
4799
  authorizationServer: discovery.authorizationServer,
@@ -4693,6 +4821,7 @@ function createDefaultOAuthClientProvider(options) {
4693
4821
  redirectUri: loopback.redirectUri,
4694
4822
  resource,
4695
4823
  fetch: fetch2,
4824
+ signal,
4696
4825
  now
4697
4826
  });
4698
4827
  const session = {
@@ -4702,6 +4831,7 @@ function createDefaultOAuthClientProvider(options) {
4702
4831
  await saveSession(resource, session);
4703
4832
  return session;
4704
4833
  } catch (error) {
4834
+ signal?.throwIfAborted();
4705
4835
  if (shouldReRegisterStoredDynamicClient(error, resolvedClient, reRegistrationAttempted)) {
4706
4836
  reRegistrationAttempted = true;
4707
4837
  await clearRegisteredClient(discovery.authorizationServer);
@@ -4727,7 +4857,8 @@ function createDefaultOAuthClientProvider(options) {
4727
4857
  authorizationPromises.set(resource, finalPromise);
4728
4858
  return finalPromise;
4729
4859
  }
4730
- async function resolveClient(existingSession, discovery, redirectUri, fetch2) {
4860
+ async function resolveClient(existingSession, discovery, redirectUri, fetch2, parentSignal) {
4861
+ parentSignal?.throwIfAborted();
4731
4862
  const configuredClient = normalizeConfiguredClient(options.client);
4732
4863
  if (options.client.mode === "static") {
4733
4864
  if (configuredClient === null) {
@@ -4777,7 +4908,8 @@ function createDefaultOAuthClientProvider(options) {
4777
4908
  }
4778
4909
  }
4779
4910
  const registrationBody = buildClientRegistrationBody(getClientMetadata(options.client), redirectUri);
4780
- const signal = AbortSignal.timeout(3e4);
4911
+ const deadline = AbortSignal.timeout(3e4);
4912
+ const signal = parentSignal === void 0 ? deadline : AbortSignal.any([parentSignal, deadline]);
4781
4913
  const response = await fetchMcpResponse(fetch2, registrationEndpoint, {
4782
4914
  method: "POST",
4783
4915
  headers: {
@@ -5340,8 +5472,10 @@ async function readJsonResponse(response, label, signal) {
5340
5472
  throw new Error(`${label} response must be valid JSON`);
5341
5473
  }
5342
5474
  }
5343
- async function fetchMetadata(fetch2, location, label) {
5344
- const signal = AbortSignal.timeout(1e4);
5475
+ async function fetchMetadata(fetch2, location, label, parentSignal) {
5476
+ parentSignal?.throwIfAborted();
5477
+ const deadline = AbortSignal.timeout(1e4);
5478
+ const signal = parentSignal === void 0 ? deadline : AbortSignal.any([deadline, parentSignal]);
5345
5479
  const response = await fetchMcpResponse(fetch2, location, {
5346
5480
  method: "GET",
5347
5481
  headers: { Accept: "application/json" },
@@ -5443,7 +5577,7 @@ var OAuthMetadataDiscovery = class {
5443
5577
  this.fetchImpl = fetch2;
5444
5578
  this.cache = cache;
5445
5579
  }
5446
- async discoverProtectedResource(resource, resourceMetadataUrl) {
5580
+ async discoverProtectedResource(resource, resourceMetadataUrl, signal) {
5447
5581
  const locations = /* @__PURE__ */ new Set([resolveProtectedResourceMetadataUrl(resource, resourceMetadataUrl)]);
5448
5582
  if (resourceMetadataUrl === void 0) {
5449
5583
  locations.add(new URL("/.well-known/oauth-protected-resource", resource).toString());
@@ -5452,17 +5586,19 @@ var OAuthMetadataDiscovery = class {
5452
5586
  for (const location of locations) {
5453
5587
  try {
5454
5588
  const metadata = validateProtectedResourceMetadata(
5455
- await fetchMetadata(this.fetchImpl, location, "Protected resource metadata"),
5589
+ await fetchMetadata(this.fetchImpl, location, "Protected resource metadata", signal),
5456
5590
  resource
5457
5591
  );
5458
5592
  return { location, metadata };
5459
5593
  } catch (error) {
5594
+ signal?.throwIfAborted();
5460
5595
  lastError = error;
5461
5596
  }
5462
5597
  }
5463
5598
  throw lastError;
5464
5599
  }
5465
- async discover(resourceUrl, { resourceMetadataUrl } = {}) {
5600
+ async discover(resourceUrl, { resourceMetadataUrl, signal } = {}) {
5601
+ signal?.throwIfAborted();
5466
5602
  const cacheKey = canonicalizeResourceIndicator(resourceUrl);
5467
5603
  resolveProtectedResourceMetadataUrl(cacheKey, resourceMetadataUrl);
5468
5604
  const memoryCachedResult = this.memoryCache.get(cacheKey);
@@ -5470,6 +5606,7 @@ var OAuthMetadataDiscovery = class {
5470
5606
  return structuredClone(memoryCachedResult);
5471
5607
  }
5472
5608
  const sharedCachedResult = await this.cache?.get(cacheKey);
5609
+ signal?.throwIfAborted();
5473
5610
  if (sharedCachedResult !== null && sharedCachedResult !== void 0 && resourceMetadataUrl === void 0) {
5474
5611
  try {
5475
5612
  const result = validateCachedDiscovery(sharedCachedResult, cacheKey);
@@ -5479,7 +5616,7 @@ var OAuthMetadataDiscovery = class {
5479
5616
  await this.cache?.delete?.(cacheKey);
5480
5617
  }
5481
5618
  }
5482
- const { location: resourceMetadataLocation, metadata: resourceMetadata } = await this.discoverProtectedResource(cacheKey, resourceMetadataUrl);
5619
+ const { location: resourceMetadataLocation, metadata: resourceMetadata } = await this.discoverProtectedResource(cacheKey, resourceMetadataUrl, signal);
5483
5620
  const authorizationServerErrors = [];
5484
5621
  for (const authorizationServer of resourceMetadata.authorization_servers) {
5485
5622
  const normalizedAuthorizationServer = validateAuthorizationServerIssuer(authorizationServer);
@@ -5487,7 +5624,7 @@ var OAuthMetadataDiscovery = class {
5487
5624
  for (const authorizationServerMetadataUrl of metadataLocations) {
5488
5625
  try {
5489
5626
  const authorizationServerMetadata = validateAuthorizationServerMetadata(
5490
- await fetchMetadata(this.fetchImpl, authorizationServerMetadataUrl, "Authorization server metadata"),
5627
+ await fetchMetadata(this.fetchImpl, authorizationServerMetadataUrl, "Authorization server metadata", signal),
5491
5628
  normalizedAuthorizationServer
5492
5629
  );
5493
5630
  const result = {
@@ -5502,6 +5639,7 @@ var OAuthMetadataDiscovery = class {
5502
5639
  await this.cache?.set(cacheKey, structuredClone(result));
5503
5640
  return result;
5504
5641
  } catch (error) {
5642
+ signal?.throwIfAborted();
5505
5643
  authorizationServerErrors.push(
5506
5644
  `${authorizationServerMetadataUrl}: ${error instanceof Error ? error.message : String(error)}`
5507
5645
  );
@@ -6642,6 +6780,7 @@ var HttpTransport = class {
6642
6780
  oauthProvider;
6643
6781
  oauthMetadataDiscovery;
6644
6782
  inFlightFetchAbortControllers = /* @__PURE__ */ new Set();
6783
+ inFlightOAuthAbortControllers = /* @__PURE__ */ new Set();
6645
6784
  openResponseReaders = /* @__PURE__ */ new Set();
6646
6785
  modernRequests = /* @__PURE__ */ new Map();
6647
6786
  modernMode = false;
@@ -6711,7 +6850,7 @@ var HttpTransport = class {
6711
6850
  this.rejectLegacyEndpoint = void 0;
6712
6851
  this.resolveLegacyEndpoint = void 0;
6713
6852
  this.toolParameterHeaders.clear();
6714
- this.abortInFlightFetches();
6853
+ this.abortInFlightFetches(reason);
6715
6854
  this.cancelOpenResponseReaders();
6716
6855
  if (!this.writeStream.destroyed && !this.writeStream.writableEnded) {
6717
6856
  this.writeStream.end();
@@ -6747,12 +6886,14 @@ var HttpTransport = class {
6747
6886
  this.resolveClosed = void 0;
6748
6887
  resolveClosed?.({ reason: closeReason });
6749
6888
  }
6750
- abortInFlightFetches() {
6751
- for (const controller of this.modernRequests.values()) controller.abort();
6889
+ abortInFlightFetches(reason) {
6890
+ for (const controller of this.modernRequests.values()) controller.abort(reason);
6752
6891
  this.modernRequests.clear();
6753
6892
  for (const abortController of this.inFlightFetchAbortControllers) {
6754
- abortController.abort();
6893
+ abortController.abort(reason);
6755
6894
  }
6895
+ for (const controller of this.inFlightOAuthAbortControllers) controller.abort(reason);
6896
+ this.inFlightOAuthAbortControllers.clear();
6756
6897
  this.inFlightFetchAbortControllers.clear();
6757
6898
  }
6758
6899
  cancelOpenResponseReaders() {
@@ -6818,7 +6959,7 @@ var HttpTransport = class {
6818
6959
  const response = await this.fetchWithOAuthRetry({
6819
6960
  url: postUrl,
6820
6961
  method: "POST",
6821
- createHeaders: () => this.createPostHeaders(message, modern),
6962
+ createHeaders: (signal) => this.createPostHeaders(message, modern, signal),
6822
6963
  body: line,
6823
6964
  controller
6824
6965
  });
@@ -6858,7 +6999,7 @@ var HttpTransport = class {
6858
6999
  this.modernRequests.delete(id);
6859
7000
  }
6860
7001
  }
6861
- async createPostHeaders(message, modern = false) {
7002
+ async createPostHeaders(message, modern = false, signal) {
6862
7003
  const headers = new Headers(this.headers);
6863
7004
  headers.set("Accept", "application/json, text/event-stream");
6864
7005
  headers.set("Content-Type", "application/json");
@@ -6888,9 +7029,9 @@ var HttpTransport = class {
6888
7029
  headers.set("Mcp-Session-Id", this.sessionId);
6889
7030
  headers.set("MCP-Protocol-Version", MCP_PROTOCOL_VERSION);
6890
7031
  }
6891
- return this.authorizeRequestHeaders(headers);
7032
+ return this.authorizeRequestHeaders(headers, signal);
6892
7033
  }
6893
- async createGetHeaders() {
7034
+ async createGetHeaders(signal) {
6894
7035
  const headers = new Headers(this.headers);
6895
7036
  headers.set("Accept", "text/event-stream");
6896
7037
  if (this.sessionId !== void 0) {
@@ -6900,20 +7041,26 @@ var HttpTransport = class {
6900
7041
  if (this.lastEventId !== void 0) {
6901
7042
  headers.set("Last-Event-ID", this.lastEventId);
6902
7043
  }
6903
- return this.authorizeRequestHeaders(headers);
7044
+ return this.authorizeRequestHeaders(headers, signal);
6904
7045
  }
6905
- async createDeleteHeaders(sessionId) {
7046
+ async createDeleteHeaders(sessionId, signal) {
6906
7047
  const headers = new Headers(this.headers);
6907
7048
  headers.set("Mcp-Session-Id", sessionId);
6908
7049
  headers.set("MCP-Protocol-Version", MCP_PROTOCOL_VERSION);
6909
- return this.authorizeRequestHeaders(headers);
7050
+ return this.authorizeRequestHeaders(headers, signal);
6910
7051
  }
6911
- async authorizeRequestHeaders(headers) {
7052
+ async authorizeRequestHeaders(headers, signal) {
7053
+ signal?.throwIfAborted();
6912
7054
  await this.oauthProvider?.authorizeRequest?.({
6913
7055
  requestUrl: new URL(this.url),
6914
7056
  headers,
6915
- fetch: this.fetchImpl
7057
+ signal,
7058
+ fetch: (url, init) => fetchMcpResponse(this.fetchImpl, url, {
7059
+ ...init,
7060
+ signal: signal === void 0 ? init?.signal : init?.signal == null ? signal : AbortSignal.any([signal, init.signal])
7061
+ })
6916
7062
  });
7063
+ signal?.throwIfAborted();
6917
7064
  return headers;
6918
7065
  }
6919
7066
  captureSessionId(response) {
@@ -6950,7 +7097,7 @@ var HttpTransport = class {
6950
7097
  return this.legacyEndpointReady;
6951
7098
  }
6952
7099
  async sendSessionTerminationRequest(sessionId, signal) {
6953
- const headers = await this.createDeleteHeaders(sessionId);
7100
+ const headers = await this.createDeleteHeaders(sessionId, signal);
6954
7101
  signal.throwIfAborted();
6955
7102
  const response = await fetchMcpResponse(this.fetchImpl, this.url, {
6956
7103
  method: "DELETE",
@@ -6973,7 +7120,7 @@ var HttpTransport = class {
6973
7120
  async consumeGetSseStream() {
6974
7121
  const response = await this.fetchWithOAuthRetry({
6975
7122
  method: "GET",
6976
- createHeaders: () => this.createGetHeaders()
7123
+ createHeaders: (signal) => this.createGetHeaders(signal)
6977
7124
  });
6978
7125
  if (this.disposed) {
6979
7126
  void response.body?.cancel().catch(() => void 0);
@@ -7056,7 +7203,7 @@ var HttpTransport = class {
7056
7203
  const message = responseBody.length === 0 ? `HTTP transport POST failed (${statusDescriptor})` : `HTTP transport POST failed (${statusDescriptor}): ${responseBody}`;
7057
7204
  throw new HttpTransportError(message, response.status, "POST");
7058
7205
  }
7059
- async maybeHandleUnauthorizedResponse(response) {
7206
+ async maybeHandleUnauthorizedResponse(response, signal) {
7060
7207
  if (response.status !== 401 || this.oauthProvider === void 0) {
7061
7208
  return false;
7062
7209
  }
@@ -7067,7 +7214,7 @@ var HttpTransport = class {
7067
7214
  const challenge = parseBearerWwwAuthenticateHeader(response.headers.get("WWW-Authenticate"));
7068
7215
  const resourceMetadataUrl = challenge?.params.resource_metadata;
7069
7216
  try {
7070
- const discovery = await discoveryClient.discover(this.url, { resourceMetadataUrl });
7217
+ const discovery = await discoveryClient.discover(this.url, { resourceMetadataUrl, signal });
7071
7218
  const providerResponse = response.clone();
7072
7219
  let result;
7073
7220
  try {
@@ -7076,8 +7223,13 @@ var HttpTransport = class {
7076
7223
  response: providerResponse,
7077
7224
  challenge,
7078
7225
  discovery,
7079
- fetch: this.fetchImpl
7226
+ signal,
7227
+ fetch: (url, init) => fetchMcpResponse(this.fetchImpl, url, {
7228
+ ...init,
7229
+ signal: init?.signal == null ? signal : AbortSignal.any([signal, init.signal])
7230
+ })
7080
7231
  });
7232
+ signal.throwIfAborted();
7081
7233
  } finally {
7082
7234
  void providerResponse.body?.cancel().catch(() => void 0);
7083
7235
  }
@@ -7207,25 +7359,30 @@ var HttpTransport = class {
7207
7359
  `);
7208
7360
  }
7209
7361
  async fetchWithOAuthRetry(input) {
7210
- const request = async () => this.fetchWithAbort(
7211
- input.url ?? this.url,
7212
- {
7362
+ const controller = input.controller ?? new AbortController();
7363
+ this.inFlightOAuthAbortControllers.add(controller);
7364
+ const request = async () => {
7365
+ controller.signal.throwIfAborted();
7366
+ const headers = await input.createHeaders(controller.signal);
7367
+ controller.signal.throwIfAborted();
7368
+ return this.fetchWithAbort(input.url ?? this.url, {
7213
7369
  method: input.method,
7214
- headers: await input.createHeaders(),
7370
+ headers,
7215
7371
  body: input.body
7216
- },
7217
- input.controller
7218
- );
7219
- let response = await request();
7220
- if (await this.maybeHandleUnauthorizedResponse(response)) {
7221
- response = await request();
7222
- }
7223
- const oauthError = this.oauthProvider === void 0 ? null : this.readOAuthChallengeError(response);
7224
- if (oauthError !== null) {
7225
- void response.body?.cancel().catch(() => void 0);
7226
- throw oauthError;
7372
+ }, controller);
7373
+ };
7374
+ try {
7375
+ let response = await request();
7376
+ if (await this.maybeHandleUnauthorizedResponse(response, controller.signal)) response = await request();
7377
+ const oauthError = this.oauthProvider === void 0 ? null : this.readOAuthChallengeError(response);
7378
+ if (oauthError !== null) {
7379
+ void response.body?.cancel().catch(() => void 0);
7380
+ throw oauthError;
7381
+ }
7382
+ return response;
7383
+ } finally {
7384
+ this.inFlightOAuthAbortControllers.delete(controller);
7227
7385
  }
7228
- return response;
7229
7386
  }
7230
7387
  readOAuthChallengeError(response) {
7231
7388
  if (response.status !== 401 && response.status !== 403) {