@frontmcp/adapters 1.7.2 → 1.8.1

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/esm/index.mjs CHANGED
@@ -363,6 +363,7 @@ Solutions:
363
363
  // libs/adapters/src/openapi/openapi.tool.ts
364
364
  import { z as z2 } from "@frontmcp/lazy-zod";
365
365
  import { tool } from "@frontmcp/sdk";
366
+ import { isRedirectResponse } from "@frontmcp/utils";
366
367
 
367
368
  // libs/adapters/src/openapi/openapi.frontmcp-schema.ts
368
369
  import { z } from "@frontmcp/lazy-zod";
@@ -853,7 +854,7 @@ function createOpenApiTool(openapiTool, options, logger) {
853
854
  }
854
855
  } catch (err) {
855
856
  const errorMessage = err instanceof Error ? err.message : String(err);
856
- throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
857
+ throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`, { cause: err });
857
858
  }
858
859
  }
859
860
  let finalBody = requestBody;
@@ -862,7 +863,7 @@ function createOpenApiTool(openapiTool, options, logger) {
862
863
  finalBody = options.bodyMapper(ctx, requestBody);
863
864
  } catch (err) {
864
865
  const errorMessage = err instanceof Error ? err.message : String(err);
865
- throw new Error(`bodyMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
866
+ throw new Error(`bodyMapper failed for tool '${openapiTool.name}': ${errorMessage}`, { cause: err });
866
867
  }
867
868
  }
868
869
  if (finalBody && !headers.has("content-type")) {
@@ -875,7 +876,8 @@ function createOpenApiTool(openapiTool, options, logger) {
875
876
  } catch (err) {
876
877
  const errorMessage = err instanceof Error ? err.message : String(err);
877
878
  throw new Error(
878
- `Failed to serialize request body for tool '${openapiTool.name}': ${errorMessage}. Body may contain circular references, BigInt, or non-serializable values.`
879
+ `Failed to serialize request body for tool '${openapiTool.name}': ${errorMessage}. Body may contain circular references, BigInt, or non-serializable values.`,
880
+ { cause: err }
879
881
  );
880
882
  }
881
883
  const maxRequestSize = options.maxRequestSize ?? DEFAULT_MAX_REQUEST_SIZE;
@@ -893,8 +895,35 @@ function createOpenApiTool(openapiTool, options, logger) {
893
895
  method: openapiTool.metadata.method.toUpperCase(),
894
896
  headers,
895
897
  body: serializedBody,
896
- signal: controller.signal
898
+ signal: controller.signal,
899
+ // SECURITY (GHSA-qh67-4345-cw2q): never auto-follow redirects. `fetch` defaults to
900
+ // `redirect: 'follow'`, which re-sends the request to a destination the UPSTREAM
901
+ // chose, not the operator. Only `baseUrl` is validated, and only for its scheme, so
902
+ // a 3xx is an unvalidated hop — to a cloud metadata address or an RFC1918 host.
903
+ // undici also strips only `Authorization` and `Cookie` across origins and forwards
904
+ // custom headers, which is exactly how this adapter injects API keys, so following
905
+ // hands the backend credential to the redirect target. `manual` makes the 3xx
906
+ // visible here so it can be refused instead of chased.
907
+ redirect: "manual"
897
908
  });
909
+ if (isRedirectResponse(response)) {
910
+ logger.warn(`[${openapiTool.name}] upstream returned a redirect; not followed`, {
911
+ status: response.status
912
+ });
913
+ return {
914
+ content: [
915
+ {
916
+ type: "text",
917
+ text: JSON.stringify({
918
+ status: response.status,
919
+ error: `Upstream returned a redirect (${response.status}); not followed to protect injected credentials.`
920
+ })
921
+ }
922
+ ],
923
+ isError: true,
924
+ _meta: { status: response.status, errorCode: "OPENAPI_REDIRECT_NOT_FOLLOWED" }
925
+ };
926
+ }
898
927
  const apiResponse = await parseResponse(response, { maxResponseSize: options.maxResponseSize });
899
928
  let transformedData = apiResponse.data;
900
929
  if (postToolTransform) {
@@ -982,7 +1011,7 @@ async function safeInject(transform, ctx, timeoutMs = DEFAULT_TRANSFORM_TIMEOUT_
982
1011
  return result;
983
1012
  } catch (err) {
984
1013
  const errorMessage = err instanceof Error ? err.message : String(err);
985
- throw new Error(`Input transform for '${transform.inputKey}' failed: ${errorMessage}`);
1014
+ throw new Error(`Input transform for '${transform.inputKey}' failed: ${errorMessage}`, { cause: err });
986
1015
  } finally {
987
1016
  if (timeoutId !== void 0) {
988
1017
  clearTimeout(timeoutId);
@@ -363,6 +363,7 @@ Solutions:
363
363
  // libs/adapters/src/openapi/openapi.tool.ts
364
364
  import { z as z2 } from "@frontmcp/lazy-zod";
365
365
  import { tool } from "@frontmcp/sdk";
366
+ import { isRedirectResponse } from "@frontmcp/utils";
366
367
 
367
368
  // libs/adapters/src/openapi/openapi.frontmcp-schema.ts
368
369
  import { z } from "@frontmcp/lazy-zod";
@@ -853,7 +854,7 @@ function createOpenApiTool(openapiTool, options, logger) {
853
854
  }
854
855
  } catch (err) {
855
856
  const errorMessage = err instanceof Error ? err.message : String(err);
856
- throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
857
+ throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`, { cause: err });
857
858
  }
858
859
  }
859
860
  let finalBody = requestBody;
@@ -862,7 +863,7 @@ function createOpenApiTool(openapiTool, options, logger) {
862
863
  finalBody = options.bodyMapper(ctx, requestBody);
863
864
  } catch (err) {
864
865
  const errorMessage = err instanceof Error ? err.message : String(err);
865
- throw new Error(`bodyMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
866
+ throw new Error(`bodyMapper failed for tool '${openapiTool.name}': ${errorMessage}`, { cause: err });
866
867
  }
867
868
  }
868
869
  if (finalBody && !headers.has("content-type")) {
@@ -875,7 +876,8 @@ function createOpenApiTool(openapiTool, options, logger) {
875
876
  } catch (err) {
876
877
  const errorMessage = err instanceof Error ? err.message : String(err);
877
878
  throw new Error(
878
- `Failed to serialize request body for tool '${openapiTool.name}': ${errorMessage}. Body may contain circular references, BigInt, or non-serializable values.`
879
+ `Failed to serialize request body for tool '${openapiTool.name}': ${errorMessage}. Body may contain circular references, BigInt, or non-serializable values.`,
880
+ { cause: err }
879
881
  );
880
882
  }
881
883
  const maxRequestSize = options.maxRequestSize ?? DEFAULT_MAX_REQUEST_SIZE;
@@ -893,8 +895,35 @@ function createOpenApiTool(openapiTool, options, logger) {
893
895
  method: openapiTool.metadata.method.toUpperCase(),
894
896
  headers,
895
897
  body: serializedBody,
896
- signal: controller.signal
898
+ signal: controller.signal,
899
+ // SECURITY (GHSA-qh67-4345-cw2q): never auto-follow redirects. `fetch` defaults to
900
+ // `redirect: 'follow'`, which re-sends the request to a destination the UPSTREAM
901
+ // chose, not the operator. Only `baseUrl` is validated, and only for its scheme, so
902
+ // a 3xx is an unvalidated hop — to a cloud metadata address or an RFC1918 host.
903
+ // undici also strips only `Authorization` and `Cookie` across origins and forwards
904
+ // custom headers, which is exactly how this adapter injects API keys, so following
905
+ // hands the backend credential to the redirect target. `manual` makes the 3xx
906
+ // visible here so it can be refused instead of chased.
907
+ redirect: "manual"
897
908
  });
909
+ if (isRedirectResponse(response)) {
910
+ logger.warn(`[${openapiTool.name}] upstream returned a redirect; not followed`, {
911
+ status: response.status
912
+ });
913
+ return {
914
+ content: [
915
+ {
916
+ type: "text",
917
+ text: JSON.stringify({
918
+ status: response.status,
919
+ error: `Upstream returned a redirect (${response.status}); not followed to protect injected credentials.`
920
+ })
921
+ }
922
+ ],
923
+ isError: true,
924
+ _meta: { status: response.status, errorCode: "OPENAPI_REDIRECT_NOT_FOLLOWED" }
925
+ };
926
+ }
898
927
  const apiResponse = await parseResponse(response, { maxResponseSize: options.maxResponseSize });
899
928
  let transformedData = apiResponse.data;
900
929
  if (postToolTransform) {
@@ -982,7 +1011,7 @@ async function safeInject(transform, ctx, timeoutMs = DEFAULT_TRANSFORM_TIMEOUT_
982
1011
  return result;
983
1012
  } catch (err) {
984
1013
  const errorMessage = err instanceof Error ? err.message : String(err);
985
- throw new Error(`Input transform for '${transform.inputKey}' failed: ${errorMessage}`);
1014
+ throw new Error(`Input transform for '${transform.inputKey}' failed: ${errorMessage}`, { cause: err });
986
1015
  } finally {
987
1016
  if (timeoutId !== void 0) {
988
1017
  clearTimeout(timeoutId);
package/esm/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/adapters",
3
- "version": "1.7.2",
3
+ "version": "1.8.1",
4
4
  "description": "Adapters for the FrontMCP framework",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -64,15 +64,15 @@
64
64
  "node": ">=24.0.0"
65
65
  },
66
66
  "dependencies": {
67
- "@frontmcp/auth": "1.7.2",
68
- "@frontmcp/di": "1.7.2",
69
- "@frontmcp/sdk": "1.7.2",
70
- "@frontmcp/utils": "1.7.2",
67
+ "@frontmcp/auth": "1.8.1",
68
+ "@frontmcp/di": "1.8.1",
69
+ "@frontmcp/sdk": "1.8.1",
70
+ "@frontmcp/utils": "1.8.1",
71
71
  "js-yaml": "^4.1.0",
72
- "mcp-from-openapi": "2.5.1",
72
+ "mcp-from-openapi": "2.8.0",
73
73
  "openapi-types": "^12.1.3"
74
74
  },
75
75
  "peerDependencies": {
76
- "@frontmcp/lazy-zod": "1.7.2"
76
+ "@frontmcp/lazy-zod": "1.8.1"
77
77
  }
78
78
  }
@@ -769,7 +769,7 @@ var NpmSource = class {
769
769
  };
770
770
 
771
771
  // libs/adapters/src/skills/sources/saas-pull.source.ts
772
- import { dirname, ensureDir, pathResolve, readFile, writeFile } from "@frontmcp/utils";
772
+ import { dirname, ensureDir, isRedirectResponse, pathResolve, readFile, writeFile } from "@frontmcp/utils";
773
773
  var DEFAULT_CACHE_DIR = ".frontmcp/skilled-openapi";
774
774
  var DEFAULT_PULL_TIMEOUT_MS = 3e4;
775
775
  var SaasPullSource = class {
@@ -807,7 +807,8 @@ var SaasPullSource = class {
807
807
  bundle = await this.loadCache();
808
808
  if (!bundle) {
809
809
  throw new Error(
810
- `[saas-source] initial pull failed and no cached bundle is available at ${this.cachePath()}: ${e.message}`
810
+ `[saas-source] initial pull failed and no cached bundle is available at ${this.cachePath()}: ${e.message}`,
811
+ { cause: e }
811
812
  );
812
813
  }
813
814
  this.logger.warn(`[saas-source] using cached bundle "${bundle.bundleId}@${bundle.version}"`);
@@ -882,7 +883,10 @@ var SaasPullSource = class {
882
883
  const controller = new AbortController();
883
884
  const timer = setTimeout(() => controller.abort(), DEFAULT_PULL_TIMEOUT_MS);
884
885
  try {
885
- const res = await fetch(url, { method: "GET", headers, signal: controller.signal });
886
+ const res = await fetch(url, { method: "GET", headers, signal: controller.signal, redirect: "manual" });
887
+ if (isRedirectResponse(res)) {
888
+ throw new Error(`pull endpoint returned a redirect (${res.status}); not followed to protect the bearer token`);
889
+ }
886
890
  const body = await res.text();
887
891
  return { status: res.status, body };
888
892
  } finally {
package/index.js CHANGED
@@ -398,6 +398,7 @@ Solutions:
398
398
  // libs/adapters/src/openapi/openapi.tool.ts
399
399
  var import_lazy_zod2 = require("@frontmcp/lazy-zod");
400
400
  var import_sdk3 = require("@frontmcp/sdk");
401
+ var import_utils3 = require("@frontmcp/utils");
401
402
 
402
403
  // libs/adapters/src/openapi/openapi.frontmcp-schema.ts
403
404
  var import_lazy_zod = require("@frontmcp/lazy-zod");
@@ -888,7 +889,7 @@ function createOpenApiTool(openapiTool, options, logger) {
888
889
  }
889
890
  } catch (err) {
890
891
  const errorMessage = err instanceof Error ? err.message : String(err);
891
- throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
892
+ throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`, { cause: err });
892
893
  }
893
894
  }
894
895
  let finalBody = requestBody;
@@ -897,7 +898,7 @@ function createOpenApiTool(openapiTool, options, logger) {
897
898
  finalBody = options.bodyMapper(ctx, requestBody);
898
899
  } catch (err) {
899
900
  const errorMessage = err instanceof Error ? err.message : String(err);
900
- throw new Error(`bodyMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
901
+ throw new Error(`bodyMapper failed for tool '${openapiTool.name}': ${errorMessage}`, { cause: err });
901
902
  }
902
903
  }
903
904
  if (finalBody && !headers.has("content-type")) {
@@ -910,7 +911,8 @@ function createOpenApiTool(openapiTool, options, logger) {
910
911
  } catch (err) {
911
912
  const errorMessage = err instanceof Error ? err.message : String(err);
912
913
  throw new Error(
913
- `Failed to serialize request body for tool '${openapiTool.name}': ${errorMessage}. Body may contain circular references, BigInt, or non-serializable values.`
914
+ `Failed to serialize request body for tool '${openapiTool.name}': ${errorMessage}. Body may contain circular references, BigInt, or non-serializable values.`,
915
+ { cause: err }
914
916
  );
915
917
  }
916
918
  const maxRequestSize = options.maxRequestSize ?? DEFAULT_MAX_REQUEST_SIZE;
@@ -928,8 +930,35 @@ function createOpenApiTool(openapiTool, options, logger) {
928
930
  method: openapiTool.metadata.method.toUpperCase(),
929
931
  headers,
930
932
  body: serializedBody,
931
- signal: controller.signal
933
+ signal: controller.signal,
934
+ // SECURITY (GHSA-qh67-4345-cw2q): never auto-follow redirects. `fetch` defaults to
935
+ // `redirect: 'follow'`, which re-sends the request to a destination the UPSTREAM
936
+ // chose, not the operator. Only `baseUrl` is validated, and only for its scheme, so
937
+ // a 3xx is an unvalidated hop — to a cloud metadata address or an RFC1918 host.
938
+ // undici also strips only `Authorization` and `Cookie` across origins and forwards
939
+ // custom headers, which is exactly how this adapter injects API keys, so following
940
+ // hands the backend credential to the redirect target. `manual` makes the 3xx
941
+ // visible here so it can be refused instead of chased.
942
+ redirect: "manual"
932
943
  });
944
+ if ((0, import_utils3.isRedirectResponse)(response)) {
945
+ logger.warn(`[${openapiTool.name}] upstream returned a redirect; not followed`, {
946
+ status: response.status
947
+ });
948
+ return {
949
+ content: [
950
+ {
951
+ type: "text",
952
+ text: JSON.stringify({
953
+ status: response.status,
954
+ error: `Upstream returned a redirect (${response.status}); not followed to protect injected credentials.`
955
+ })
956
+ }
957
+ ],
958
+ isError: true,
959
+ _meta: { status: response.status, errorCode: "OPENAPI_REDIRECT_NOT_FOLLOWED" }
960
+ };
961
+ }
933
962
  const apiResponse = await parseResponse(response, { maxResponseSize: options.maxResponseSize });
934
963
  let transformedData = apiResponse.data;
935
964
  if (postToolTransform) {
@@ -1017,7 +1046,7 @@ async function safeInject(transform, ctx, timeoutMs = DEFAULT_TRANSFORM_TIMEOUT_
1017
1046
  return result;
1018
1047
  } catch (err) {
1019
1048
  const errorMessage = err instanceof Error ? err.message : String(err);
1020
- throw new Error(`Input transform for '${transform.inputKey}' failed: ${errorMessage}`);
1049
+ throw new Error(`Input transform for '${transform.inputKey}' failed: ${errorMessage}`, { cause: err });
1021
1050
  } finally {
1022
1051
  if (timeoutId !== void 0) {
1023
1052
  clearTimeout(timeoutId);
package/openapi/index.js CHANGED
@@ -398,6 +398,7 @@ Solutions:
398
398
  // libs/adapters/src/openapi/openapi.tool.ts
399
399
  var import_lazy_zod2 = require("@frontmcp/lazy-zod");
400
400
  var import_sdk3 = require("@frontmcp/sdk");
401
+ var import_utils3 = require("@frontmcp/utils");
401
402
 
402
403
  // libs/adapters/src/openapi/openapi.frontmcp-schema.ts
403
404
  var import_lazy_zod = require("@frontmcp/lazy-zod");
@@ -888,7 +889,7 @@ function createOpenApiTool(openapiTool, options, logger) {
888
889
  }
889
890
  } catch (err) {
890
891
  const errorMessage = err instanceof Error ? err.message : String(err);
891
- throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
892
+ throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`, { cause: err });
892
893
  }
893
894
  }
894
895
  let finalBody = requestBody;
@@ -897,7 +898,7 @@ function createOpenApiTool(openapiTool, options, logger) {
897
898
  finalBody = options.bodyMapper(ctx, requestBody);
898
899
  } catch (err) {
899
900
  const errorMessage = err instanceof Error ? err.message : String(err);
900
- throw new Error(`bodyMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
901
+ throw new Error(`bodyMapper failed for tool '${openapiTool.name}': ${errorMessage}`, { cause: err });
901
902
  }
902
903
  }
903
904
  if (finalBody && !headers.has("content-type")) {
@@ -910,7 +911,8 @@ function createOpenApiTool(openapiTool, options, logger) {
910
911
  } catch (err) {
911
912
  const errorMessage = err instanceof Error ? err.message : String(err);
912
913
  throw new Error(
913
- `Failed to serialize request body for tool '${openapiTool.name}': ${errorMessage}. Body may contain circular references, BigInt, or non-serializable values.`
914
+ `Failed to serialize request body for tool '${openapiTool.name}': ${errorMessage}. Body may contain circular references, BigInt, or non-serializable values.`,
915
+ { cause: err }
914
916
  );
915
917
  }
916
918
  const maxRequestSize = options.maxRequestSize ?? DEFAULT_MAX_REQUEST_SIZE;
@@ -928,8 +930,35 @@ function createOpenApiTool(openapiTool, options, logger) {
928
930
  method: openapiTool.metadata.method.toUpperCase(),
929
931
  headers,
930
932
  body: serializedBody,
931
- signal: controller.signal
933
+ signal: controller.signal,
934
+ // SECURITY (GHSA-qh67-4345-cw2q): never auto-follow redirects. `fetch` defaults to
935
+ // `redirect: 'follow'`, which re-sends the request to a destination the UPSTREAM
936
+ // chose, not the operator. Only `baseUrl` is validated, and only for its scheme, so
937
+ // a 3xx is an unvalidated hop — to a cloud metadata address or an RFC1918 host.
938
+ // undici also strips only `Authorization` and `Cookie` across origins and forwards
939
+ // custom headers, which is exactly how this adapter injects API keys, so following
940
+ // hands the backend credential to the redirect target. `manual` makes the 3xx
941
+ // visible here so it can be refused instead of chased.
942
+ redirect: "manual"
932
943
  });
944
+ if ((0, import_utils3.isRedirectResponse)(response)) {
945
+ logger.warn(`[${openapiTool.name}] upstream returned a redirect; not followed`, {
946
+ status: response.status
947
+ });
948
+ return {
949
+ content: [
950
+ {
951
+ type: "text",
952
+ text: JSON.stringify({
953
+ status: response.status,
954
+ error: `Upstream returned a redirect (${response.status}); not followed to protect injected credentials.`
955
+ })
956
+ }
957
+ ],
958
+ isError: true,
959
+ _meta: { status: response.status, errorCode: "OPENAPI_REDIRECT_NOT_FOLLOWED" }
960
+ };
961
+ }
933
962
  const apiResponse = await parseResponse(response, { maxResponseSize: options.maxResponseSize });
934
963
  let transformedData = apiResponse.data;
935
964
  if (postToolTransform) {
@@ -1017,7 +1046,7 @@ async function safeInject(transform, ctx, timeoutMs = DEFAULT_TRANSFORM_TIMEOUT_
1017
1046
  return result;
1018
1047
  } catch (err) {
1019
1048
  const errorMessage = err instanceof Error ? err.message : String(err);
1020
- throw new Error(`Input transform for '${transform.inputKey}' failed: ${errorMessage}`);
1049
+ throw new Error(`Input transform for '${transform.inputKey}' failed: ${errorMessage}`, { cause: err });
1021
1050
  } finally {
1022
1051
  if (timeoutId !== void 0) {
1023
1052
  clearTimeout(timeoutId);
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.tool.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAI1D,OAAO,KAAK,EAIV,qBAAqB,EAEtB,MAAM,iBAAiB,CAAC;AAMzB;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,cAAc,cA+QpH"}
1
+ {"version":3,"file":"openapi.tool.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAK1D,OAAO,KAAK,EAIV,qBAAqB,EAEtB,MAAM,iBAAiB,CAAC;AAMzB;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,cAAc,cA8SpH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/adapters",
3
- "version": "1.7.2",
3
+ "version": "1.8.1",
4
4
  "description": "Adapters for the FrontMCP framework",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -64,15 +64,15 @@
64
64
  "node": ">=24.0.0"
65
65
  },
66
66
  "dependencies": {
67
- "@frontmcp/auth": "1.7.2",
68
- "@frontmcp/di": "1.7.2",
69
- "@frontmcp/sdk": "1.7.2",
70
- "@frontmcp/utils": "1.7.2",
67
+ "@frontmcp/auth": "1.8.1",
68
+ "@frontmcp/di": "1.8.1",
69
+ "@frontmcp/sdk": "1.8.1",
70
+ "@frontmcp/utils": "1.8.1",
71
71
  "js-yaml": "^4.1.0",
72
- "mcp-from-openapi": "2.5.1",
72
+ "mcp-from-openapi": "2.8.0",
73
73
  "openapi-types": "^12.1.3"
74
74
  },
75
75
  "peerDependencies": {
76
- "@frontmcp/lazy-zod": "1.7.2"
76
+ "@frontmcp/lazy-zod": "1.8.1"
77
77
  }
78
78
  }
package/skills/index.js CHANGED
@@ -905,7 +905,8 @@ var SaasPullSource = class {
905
905
  bundle = await this.loadCache();
906
906
  if (!bundle) {
907
907
  throw new Error(
908
- `[saas-source] initial pull failed and no cached bundle is available at ${this.cachePath()}: ${e.message}`
908
+ `[saas-source] initial pull failed and no cached bundle is available at ${this.cachePath()}: ${e.message}`,
909
+ { cause: e }
909
910
  );
910
911
  }
911
912
  this.logger.warn(`[saas-source] using cached bundle "${bundle.bundleId}@${bundle.version}"`);
@@ -980,7 +981,10 @@ var SaasPullSource = class {
980
981
  const controller = new AbortController();
981
982
  const timer = setTimeout(() => controller.abort(), DEFAULT_PULL_TIMEOUT_MS);
982
983
  try {
983
- const res = await fetch(url, { method: "GET", headers, signal: controller.signal });
984
+ const res = await fetch(url, { method: "GET", headers, signal: controller.signal, redirect: "manual" });
985
+ if ((0, import_utils.isRedirectResponse)(res)) {
986
+ throw new Error(`pull endpoint returned a redirect (${res.status}); not followed to protect the bearer token`);
987
+ }
984
988
  const body = await res.text();
985
989
  return { status: res.status, body };
986
990
  } finally {
@@ -1 +1 @@
1
- {"version":3,"file":"saas-pull.source.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/saas-pull.source.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAKjH;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,cAAe,YAAW,iBAAiB;IASpD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAGL,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,cAAc,EACtB,IAAI,GAAE,gBAAqB;IAKxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC5B,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI;IAO9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,OAAO,CAAC,YAAY;IAcpB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;YAatC,QAAQ;cAqBN,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;YAgBlG,SAAS;IAiBvB,OAAO,CAAC,SAAS;YAKH,YAAY;YAeZ,SAAS;IAkBvB,OAAO,CAAC,MAAM;CASf"}
1
+ {"version":3,"file":"saas-pull.source.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/saas-pull.source.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAKjH;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,cAAe,YAAW,iBAAiB;IASpD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAGL,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,cAAc,EACtB,IAAI,GAAE,gBAAqB;IAKxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAqC5B,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI;IAO9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,OAAO,CAAC,YAAY;IAcpB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;YAatC,QAAQ;cAqBN,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;YAoBlG,SAAS;IAiBvB,OAAO,CAAC,SAAS;YAKH,YAAY;YAeZ,SAAS;IAkBvB,OAAO,CAAC,MAAM;CASf"}