@frontmcp/adapters 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/index.mjs +28 -1
- package/esm/openapi/index.mjs +28 -1
- package/esm/package.json +6 -6
- package/index.js +28 -1
- package/openapi/index.js +28 -1
- package/openapi/openapi.tool.d.ts.map +1 -1
- package/package.json +6 -6
package/esm/index.mjs
CHANGED
|
@@ -893,8 +893,35 @@ function createOpenApiTool(openapiTool, options, logger) {
|
|
|
893
893
|
method: openapiTool.metadata.method.toUpperCase(),
|
|
894
894
|
headers,
|
|
895
895
|
body: serializedBody,
|
|
896
|
-
signal: controller.signal
|
|
896
|
+
signal: controller.signal,
|
|
897
|
+
// SECURITY (GHSA-qh67-4345-cw2q): never auto-follow redirects. `fetch` defaults to
|
|
898
|
+
// `redirect: 'follow'`, which re-sends the request to a destination the UPSTREAM
|
|
899
|
+
// chose, not the operator. Only `baseUrl` is validated, and only for its scheme, so
|
|
900
|
+
// a 3xx is an unvalidated hop — to a cloud metadata address or an RFC1918 host.
|
|
901
|
+
// undici also strips only `Authorization` and `Cookie` across origins and forwards
|
|
902
|
+
// custom headers, which is exactly how this adapter injects API keys, so following
|
|
903
|
+
// hands the backend credential to the redirect target. `manual` makes the 3xx
|
|
904
|
+
// visible here so it can be refused instead of chased.
|
|
905
|
+
redirect: "manual"
|
|
897
906
|
});
|
|
907
|
+
if (response.status >= 300 && response.status < 400) {
|
|
908
|
+
logger.warn(`[${openapiTool.name}] upstream returned a redirect; not followed`, {
|
|
909
|
+
status: response.status
|
|
910
|
+
});
|
|
911
|
+
return {
|
|
912
|
+
content: [
|
|
913
|
+
{
|
|
914
|
+
type: "text",
|
|
915
|
+
text: JSON.stringify({
|
|
916
|
+
status: response.status,
|
|
917
|
+
error: `Upstream returned a redirect (${response.status}); not followed to protect injected credentials.`
|
|
918
|
+
})
|
|
919
|
+
}
|
|
920
|
+
],
|
|
921
|
+
isError: true,
|
|
922
|
+
_meta: { status: response.status, errorCode: "OPENAPI_REDIRECT_NOT_FOLLOWED" }
|
|
923
|
+
};
|
|
924
|
+
}
|
|
898
925
|
const apiResponse = await parseResponse(response, { maxResponseSize: options.maxResponseSize });
|
|
899
926
|
let transformedData = apiResponse.data;
|
|
900
927
|
if (postToolTransform) {
|
package/esm/openapi/index.mjs
CHANGED
|
@@ -893,8 +893,35 @@ function createOpenApiTool(openapiTool, options, logger) {
|
|
|
893
893
|
method: openapiTool.metadata.method.toUpperCase(),
|
|
894
894
|
headers,
|
|
895
895
|
body: serializedBody,
|
|
896
|
-
signal: controller.signal
|
|
896
|
+
signal: controller.signal,
|
|
897
|
+
// SECURITY (GHSA-qh67-4345-cw2q): never auto-follow redirects. `fetch` defaults to
|
|
898
|
+
// `redirect: 'follow'`, which re-sends the request to a destination the UPSTREAM
|
|
899
|
+
// chose, not the operator. Only `baseUrl` is validated, and only for its scheme, so
|
|
900
|
+
// a 3xx is an unvalidated hop — to a cloud metadata address or an RFC1918 host.
|
|
901
|
+
// undici also strips only `Authorization` and `Cookie` across origins and forwards
|
|
902
|
+
// custom headers, which is exactly how this adapter injects API keys, so following
|
|
903
|
+
// hands the backend credential to the redirect target. `manual` makes the 3xx
|
|
904
|
+
// visible here so it can be refused instead of chased.
|
|
905
|
+
redirect: "manual"
|
|
897
906
|
});
|
|
907
|
+
if (response.status >= 300 && response.status < 400) {
|
|
908
|
+
logger.warn(`[${openapiTool.name}] upstream returned a redirect; not followed`, {
|
|
909
|
+
status: response.status
|
|
910
|
+
});
|
|
911
|
+
return {
|
|
912
|
+
content: [
|
|
913
|
+
{
|
|
914
|
+
type: "text",
|
|
915
|
+
text: JSON.stringify({
|
|
916
|
+
status: response.status,
|
|
917
|
+
error: `Upstream returned a redirect (${response.status}); not followed to protect injected credentials.`
|
|
918
|
+
})
|
|
919
|
+
}
|
|
920
|
+
],
|
|
921
|
+
isError: true,
|
|
922
|
+
_meta: { status: response.status, errorCode: "OPENAPI_REDIRECT_NOT_FOLLOWED" }
|
|
923
|
+
};
|
|
924
|
+
}
|
|
898
925
|
const apiResponse = await parseResponse(response, { maxResponseSize: options.maxResponseSize });
|
|
899
926
|
let transformedData = apiResponse.data;
|
|
900
927
|
if (postToolTransform) {
|
package/esm/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/adapters",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
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.
|
|
68
|
-
"@frontmcp/di": "1.
|
|
69
|
-
"@frontmcp/sdk": "1.
|
|
70
|
-
"@frontmcp/utils": "1.
|
|
67
|
+
"@frontmcp/auth": "1.8.0",
|
|
68
|
+
"@frontmcp/di": "1.8.0",
|
|
69
|
+
"@frontmcp/sdk": "1.8.0",
|
|
70
|
+
"@frontmcp/utils": "1.8.0",
|
|
71
71
|
"js-yaml": "^4.1.0",
|
|
72
72
|
"mcp-from-openapi": "2.5.1",
|
|
73
73
|
"openapi-types": "^12.1.3"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
|
-
"@frontmcp/lazy-zod": "1.
|
|
76
|
+
"@frontmcp/lazy-zod": "1.8.0"
|
|
77
77
|
}
|
|
78
78
|
}
|
package/index.js
CHANGED
|
@@ -928,8 +928,35 @@ function createOpenApiTool(openapiTool, options, logger) {
|
|
|
928
928
|
method: openapiTool.metadata.method.toUpperCase(),
|
|
929
929
|
headers,
|
|
930
930
|
body: serializedBody,
|
|
931
|
-
signal: controller.signal
|
|
931
|
+
signal: controller.signal,
|
|
932
|
+
// SECURITY (GHSA-qh67-4345-cw2q): never auto-follow redirects. `fetch` defaults to
|
|
933
|
+
// `redirect: 'follow'`, which re-sends the request to a destination the UPSTREAM
|
|
934
|
+
// chose, not the operator. Only `baseUrl` is validated, and only for its scheme, so
|
|
935
|
+
// a 3xx is an unvalidated hop — to a cloud metadata address or an RFC1918 host.
|
|
936
|
+
// undici also strips only `Authorization` and `Cookie` across origins and forwards
|
|
937
|
+
// custom headers, which is exactly how this adapter injects API keys, so following
|
|
938
|
+
// hands the backend credential to the redirect target. `manual` makes the 3xx
|
|
939
|
+
// visible here so it can be refused instead of chased.
|
|
940
|
+
redirect: "manual"
|
|
932
941
|
});
|
|
942
|
+
if (response.status >= 300 && response.status < 400) {
|
|
943
|
+
logger.warn(`[${openapiTool.name}] upstream returned a redirect; not followed`, {
|
|
944
|
+
status: response.status
|
|
945
|
+
});
|
|
946
|
+
return {
|
|
947
|
+
content: [
|
|
948
|
+
{
|
|
949
|
+
type: "text",
|
|
950
|
+
text: JSON.stringify({
|
|
951
|
+
status: response.status,
|
|
952
|
+
error: `Upstream returned a redirect (${response.status}); not followed to protect injected credentials.`
|
|
953
|
+
})
|
|
954
|
+
}
|
|
955
|
+
],
|
|
956
|
+
isError: true,
|
|
957
|
+
_meta: { status: response.status, errorCode: "OPENAPI_REDIRECT_NOT_FOLLOWED" }
|
|
958
|
+
};
|
|
959
|
+
}
|
|
933
960
|
const apiResponse = await parseResponse(response, { maxResponseSize: options.maxResponseSize });
|
|
934
961
|
let transformedData = apiResponse.data;
|
|
935
962
|
if (postToolTransform) {
|
package/openapi/index.js
CHANGED
|
@@ -928,8 +928,35 @@ function createOpenApiTool(openapiTool, options, logger) {
|
|
|
928
928
|
method: openapiTool.metadata.method.toUpperCase(),
|
|
929
929
|
headers,
|
|
930
930
|
body: serializedBody,
|
|
931
|
-
signal: controller.signal
|
|
931
|
+
signal: controller.signal,
|
|
932
|
+
// SECURITY (GHSA-qh67-4345-cw2q): never auto-follow redirects. `fetch` defaults to
|
|
933
|
+
// `redirect: 'follow'`, which re-sends the request to a destination the UPSTREAM
|
|
934
|
+
// chose, not the operator. Only `baseUrl` is validated, and only for its scheme, so
|
|
935
|
+
// a 3xx is an unvalidated hop — to a cloud metadata address or an RFC1918 host.
|
|
936
|
+
// undici also strips only `Authorization` and `Cookie` across origins and forwards
|
|
937
|
+
// custom headers, which is exactly how this adapter injects API keys, so following
|
|
938
|
+
// hands the backend credential to the redirect target. `manual` makes the 3xx
|
|
939
|
+
// visible here so it can be refused instead of chased.
|
|
940
|
+
redirect: "manual"
|
|
932
941
|
});
|
|
942
|
+
if (response.status >= 300 && response.status < 400) {
|
|
943
|
+
logger.warn(`[${openapiTool.name}] upstream returned a redirect; not followed`, {
|
|
944
|
+
status: response.status
|
|
945
|
+
});
|
|
946
|
+
return {
|
|
947
|
+
content: [
|
|
948
|
+
{
|
|
949
|
+
type: "text",
|
|
950
|
+
text: JSON.stringify({
|
|
951
|
+
status: response.status,
|
|
952
|
+
error: `Upstream returned a redirect (${response.status}); not followed to protect injected credentials.`
|
|
953
|
+
})
|
|
954
|
+
}
|
|
955
|
+
],
|
|
956
|
+
isError: true,
|
|
957
|
+
_meta: { status: response.status, errorCode: "OPENAPI_REDIRECT_NOT_FOLLOWED" }
|
|
958
|
+
};
|
|
959
|
+
}
|
|
933
960
|
const apiResponse = await parseResponse(response, { maxResponseSize: options.maxResponseSize });
|
|
934
961
|
let transformedData = apiResponse.data;
|
|
935
962
|
if (postToolTransform) {
|
|
@@ -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,
|
|
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,cA6SpH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/adapters",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
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.
|
|
68
|
-
"@frontmcp/di": "1.
|
|
69
|
-
"@frontmcp/sdk": "1.
|
|
70
|
-
"@frontmcp/utils": "1.
|
|
67
|
+
"@frontmcp/auth": "1.8.0",
|
|
68
|
+
"@frontmcp/di": "1.8.0",
|
|
69
|
+
"@frontmcp/sdk": "1.8.0",
|
|
70
|
+
"@frontmcp/utils": "1.8.0",
|
|
71
71
|
"js-yaml": "^4.1.0",
|
|
72
72
|
"mcp-from-openapi": "2.5.1",
|
|
73
73
|
"openapi-types": "^12.1.3"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
|
-
"@frontmcp/lazy-zod": "1.
|
|
76
|
+
"@frontmcp/lazy-zod": "1.8.0"
|
|
77
77
|
}
|
|
78
78
|
}
|