mcp-from-openapi 2.1.2 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/esm/index.mjs +115 -12
- package/esm/package.json +13 -8
- package/generator.d.ts +14 -0
- package/index.d.ts +1 -1
- package/index.js +116 -13
- package/package.json +13 -8
- package/types.d.ts +37 -0
package/CHANGELOG.md
CHANGED
package/esm/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
//
|
|
1
|
+
// src/generator.ts
|
|
2
2
|
import * as yaml from "yaml";
|
|
3
|
-
import * as fs from "fs/promises";
|
|
4
3
|
import * as path from "path";
|
|
4
|
+
import * as fs from "fs/promises";
|
|
5
5
|
import $RefParser from "@apidevtools/json-schema-ref-parser";
|
|
6
6
|
|
|
7
|
-
//
|
|
7
|
+
// src/types.ts
|
|
8
8
|
function isReferenceObject(obj) {
|
|
9
9
|
return obj && typeof obj === "object" && "$ref" in obj;
|
|
10
10
|
}
|
|
@@ -70,7 +70,7 @@ function toJsonSchema(schema) {
|
|
|
70
70
|
return result;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
//
|
|
73
|
+
// src/parameter-resolver.ts
|
|
74
74
|
var ParameterResolver = class {
|
|
75
75
|
namingStrategy;
|
|
76
76
|
constructor(namingStrategy) {
|
|
@@ -335,7 +335,7 @@ var ParameterResolver = class {
|
|
|
335
335
|
}
|
|
336
336
|
};
|
|
337
337
|
|
|
338
|
-
//
|
|
338
|
+
// src/response-builder.ts
|
|
339
339
|
var ResponseBuilder = class {
|
|
340
340
|
preferredStatusCodes;
|
|
341
341
|
includeAllResponses;
|
|
@@ -455,7 +455,7 @@ var ResponseBuilder = class {
|
|
|
455
455
|
}
|
|
456
456
|
};
|
|
457
457
|
|
|
458
|
-
//
|
|
458
|
+
// src/validator.ts
|
|
459
459
|
var Validator = class {
|
|
460
460
|
/**
|
|
461
461
|
* Validate an OpenAPI document
|
|
@@ -646,7 +646,7 @@ var Validator = class {
|
|
|
646
646
|
}
|
|
647
647
|
};
|
|
648
648
|
|
|
649
|
-
//
|
|
649
|
+
// src/errors.ts
|
|
650
650
|
var OpenAPIToolError = class extends Error {
|
|
651
651
|
context;
|
|
652
652
|
constructor(message, context) {
|
|
@@ -686,7 +686,7 @@ var SchemaError = class extends OpenAPIToolError {
|
|
|
686
686
|
}
|
|
687
687
|
};
|
|
688
688
|
|
|
689
|
-
//
|
|
689
|
+
// src/generator.ts
|
|
690
690
|
var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
691
691
|
document;
|
|
692
692
|
dereferencedDocument;
|
|
@@ -702,7 +702,8 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
702
702
|
headers: options.headers ?? {},
|
|
703
703
|
timeout: options.timeout ?? 3e4,
|
|
704
704
|
validate: options.validate ?? true,
|
|
705
|
-
followRedirects: options.followRedirects ?? true
|
|
705
|
+
followRedirects: options.followRedirects ?? true,
|
|
706
|
+
refResolution: options.refResolution ?? {}
|
|
706
707
|
};
|
|
707
708
|
}
|
|
708
709
|
/**
|
|
@@ -807,6 +808,106 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
807
808
|
const validator = new Validator();
|
|
808
809
|
return validator.validate(this.document);
|
|
809
810
|
}
|
|
811
|
+
/**
|
|
812
|
+
* Hostnames and IP patterns that are blocked by default to prevent SSRF.
|
|
813
|
+
* Covers RFC 1918/6598 private ranges, link-local, loopback, and cloud metadata endpoints.
|
|
814
|
+
*/
|
|
815
|
+
static BLOCKED_HOSTNAME_PATTERNS = [
|
|
816
|
+
"localhost",
|
|
817
|
+
"metadata.google.internal",
|
|
818
|
+
/^127\.\d+\.\d+\.\d+$/,
|
|
819
|
+
// 127.0.0.0/8 loopback
|
|
820
|
+
/^10\.\d+\.\d+\.\d+$/,
|
|
821
|
+
// 10.0.0.0/8 private
|
|
822
|
+
/^172\.(1[6-9]|2\d|3[01])\.\d+\.\d+$/,
|
|
823
|
+
// 172.16.0.0/12 private
|
|
824
|
+
/^192\.168\.\d+\.\d+$/,
|
|
825
|
+
// 192.168.0.0/16 private
|
|
826
|
+
/^169\.254\.\d+\.\d+$/,
|
|
827
|
+
// 169.254.0.0/16 link-local / cloud metadata
|
|
828
|
+
/^0\.0\.0\.0$/,
|
|
829
|
+
// unspecified
|
|
830
|
+
"::1",
|
|
831
|
+
// IPv6 loopback
|
|
832
|
+
/^fd[0-9a-f]{2}:/i,
|
|
833
|
+
// fd00::/8 IPv6 ULA
|
|
834
|
+
/^fe80:/i,
|
|
835
|
+
// fe80::/10 IPv6 link-local
|
|
836
|
+
/^\[::1\]$/,
|
|
837
|
+
// bracketed IPv6 loopback
|
|
838
|
+
/^\[fd[0-9a-f]{2}:/i,
|
|
839
|
+
// bracketed IPv6 ULA
|
|
840
|
+
/^\[fe80:/i
|
|
841
|
+
// bracketed IPv6 link-local
|
|
842
|
+
];
|
|
843
|
+
/**
|
|
844
|
+
* Check whether a hostname is blocked (internal/private IP or explicit blocklist).
|
|
845
|
+
*/
|
|
846
|
+
isBlockedHost(hostname, refOpts) {
|
|
847
|
+
if (refOpts.allowInternalIPs) {
|
|
848
|
+
return refOpts.blockedHosts.includes(hostname);
|
|
849
|
+
}
|
|
850
|
+
if (refOpts.blockedHosts.includes(hostname)) {
|
|
851
|
+
return true;
|
|
852
|
+
}
|
|
853
|
+
for (const pattern of _OpenAPIToolGenerator.BLOCKED_HOSTNAME_PATTERNS) {
|
|
854
|
+
if (typeof pattern === "string") {
|
|
855
|
+
if (hostname === pattern) return true;
|
|
856
|
+
} else {
|
|
857
|
+
if (pattern.test(hostname)) return true;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Build $RefParser options based on refResolution configuration.
|
|
864
|
+
* Defaults: allow http/https, block file://, block internal IPs.
|
|
865
|
+
*/
|
|
866
|
+
buildRefParserOptions() {
|
|
867
|
+
const raw = this.options.refResolution;
|
|
868
|
+
const refOpts = {
|
|
869
|
+
allowedProtocols: raw.allowedProtocols ?? ["http", "https"],
|
|
870
|
+
allowedHosts: raw.allowedHosts ?? [],
|
|
871
|
+
blockedHosts: raw.blockedHosts ?? [],
|
|
872
|
+
allowInternalIPs: raw.allowInternalIPs ?? false
|
|
873
|
+
};
|
|
874
|
+
const allowedProtocols = new Set(refOpts.allowedProtocols);
|
|
875
|
+
const hasNetworkProtocol = allowedProtocols.size > 0 && !([...allowedProtocols].length === 1 && allowedProtocols.has("file"));
|
|
876
|
+
if (allowedProtocols.size === 0) {
|
|
877
|
+
return { resolve: { external: false } };
|
|
878
|
+
}
|
|
879
|
+
const resolveConfig = {
|
|
880
|
+
external: true,
|
|
881
|
+
file: allowedProtocols.has("file") ? void 0 : false
|
|
882
|
+
};
|
|
883
|
+
if (hasNetworkProtocol) {
|
|
884
|
+
const hasHostAllowlist = refOpts.allowedHosts.length > 0;
|
|
885
|
+
const hostAllowSet = new Set(refOpts.allowedHosts);
|
|
886
|
+
resolveConfig["http"] = {
|
|
887
|
+
canRead: (file) => {
|
|
888
|
+
try {
|
|
889
|
+
const parsed = new URL(file.url);
|
|
890
|
+
const protocol = parsed.protocol.replace(":", "");
|
|
891
|
+
if (!allowedProtocols.has(protocol)) {
|
|
892
|
+
return false;
|
|
893
|
+
}
|
|
894
|
+
if (hasHostAllowlist && !hostAllowSet.has(parsed.hostname)) {
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
if (this.isBlockedHost(parsed.hostname, refOpts)) {
|
|
898
|
+
return false;
|
|
899
|
+
}
|
|
900
|
+
return true;
|
|
901
|
+
} catch {
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
} else {
|
|
907
|
+
resolveConfig["http"] = false;
|
|
908
|
+
}
|
|
909
|
+
return { resolve: resolveConfig };
|
|
910
|
+
}
|
|
810
911
|
/**
|
|
811
912
|
* Initialize the generator (dereference if needed)
|
|
812
913
|
*/
|
|
@@ -819,8 +920,10 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
819
920
|
}
|
|
820
921
|
if (this.options.dereference && !this.dereferencedDocument) {
|
|
821
922
|
try {
|
|
923
|
+
const refParserOptions = this.buildRefParserOptions();
|
|
822
924
|
this.dereferencedDocument = await $RefParser.dereference(
|
|
823
|
-
JSON.parse(JSON.stringify(this.document))
|
|
925
|
+
JSON.parse(JSON.stringify(this.document)),
|
|
926
|
+
refParserOptions
|
|
824
927
|
);
|
|
825
928
|
} catch (error) {
|
|
826
929
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1024,7 +1127,7 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
1024
1127
|
}
|
|
1025
1128
|
};
|
|
1026
1129
|
|
|
1027
|
-
//
|
|
1130
|
+
// src/schema-builder.ts
|
|
1028
1131
|
var SchemaBuilder = class {
|
|
1029
1132
|
/**
|
|
1030
1133
|
* Merge multiple schemas into one
|
|
@@ -1303,7 +1406,7 @@ var SchemaBuilder = class {
|
|
|
1303
1406
|
}
|
|
1304
1407
|
};
|
|
1305
1408
|
|
|
1306
|
-
//
|
|
1409
|
+
// src/security-resolver.ts
|
|
1307
1410
|
var SecurityResolver = class {
|
|
1308
1411
|
/**
|
|
1309
1412
|
* Resolve security parameters from mapper entries
|
package/esm/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-from-openapi",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Production-ready library for converting OpenAPI specifications into MCP tool definitions",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -20,13 +20,12 @@
|
|
|
20
20
|
],
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "git+https://github.com/agentfront/
|
|
24
|
-
"directory": "libs/mcp-from-openapi"
|
|
23
|
+
"url": "git+https://github.com/agentfront/mcp-from-openapi.git"
|
|
25
24
|
},
|
|
26
25
|
"bugs": {
|
|
27
|
-
"url": "https://github.com/agentfront/
|
|
26
|
+
"url": "https://github.com/agentfront/mcp-from-openapi/issues"
|
|
28
27
|
},
|
|
29
|
-
"homepage": "https://github.com/agentfront/
|
|
28
|
+
"homepage": "https://github.com/agentfront/mcp-from-openapi#readme",
|
|
30
29
|
"engines": {
|
|
31
30
|
"node": ">=18.0.0"
|
|
32
31
|
},
|
|
@@ -46,19 +45,25 @@
|
|
|
46
45
|
"types": "../index.d.ts",
|
|
47
46
|
"default": "./index.mjs"
|
|
48
47
|
}
|
|
49
|
-
}
|
|
50
|
-
"./esm": null
|
|
48
|
+
}
|
|
51
49
|
},
|
|
52
50
|
"dependencies": {
|
|
53
51
|
"@apidevtools/json-schema-ref-parser": "^11.9.3",
|
|
54
52
|
"openapi-types": "^12.1.3",
|
|
55
|
-
"yaml": "^2.8.
|
|
53
|
+
"yaml": "^2.8.3"
|
|
56
54
|
},
|
|
57
55
|
"peerDependencies": {
|
|
58
56
|
"zod": "^4.0.0"
|
|
59
57
|
},
|
|
60
58
|
"devDependencies": {
|
|
59
|
+
"@swc/core": "~1.5.7",
|
|
60
|
+
"@swc/helpers": "^0.5.18",
|
|
61
|
+
"@swc/jest": "~0.2.38",
|
|
62
|
+
"@types/jest": "^29.5.0",
|
|
61
63
|
"@types/node": "^24.0.0",
|
|
64
|
+
"esbuild": "^0.27.2",
|
|
65
|
+
"jest": "^29.7.0",
|
|
66
|
+
"tslib": "^2.8.1",
|
|
62
67
|
"typescript": "^5.0.0",
|
|
63
68
|
"zod": "^4.0.0"
|
|
64
69
|
}
|
package/generator.d.ts
CHANGED
|
@@ -34,6 +34,20 @@ export declare class OpenAPIToolGenerator {
|
|
|
34
34
|
* Validate the OpenAPI document
|
|
35
35
|
*/
|
|
36
36
|
validate(): Promise<ValidationResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Hostnames and IP patterns that are blocked by default to prevent SSRF.
|
|
39
|
+
* Covers RFC 1918/6598 private ranges, link-local, loopback, and cloud metadata endpoints.
|
|
40
|
+
*/
|
|
41
|
+
private static readonly BLOCKED_HOSTNAME_PATTERNS;
|
|
42
|
+
/**
|
|
43
|
+
* Check whether a hostname is blocked (internal/private IP or explicit blocklist).
|
|
44
|
+
*/
|
|
45
|
+
private isBlockedHost;
|
|
46
|
+
/**
|
|
47
|
+
* Build $RefParser options based on refResolution configuration.
|
|
48
|
+
* Defaults: allow http/https, block file://, block internal IPs.
|
|
49
|
+
*/
|
|
50
|
+
private buildRefParserOptions;
|
|
37
51
|
/**
|
|
38
52
|
* Initialize the generator (dereference if needed)
|
|
39
53
|
*/
|
package/index.d.ts
CHANGED
|
@@ -5,6 +5,6 @@ export { ResponseBuilder } from './response-builder';
|
|
|
5
5
|
export { Validator } from './validator';
|
|
6
6
|
export { SecurityResolver, createSecurityContext } from './security-resolver';
|
|
7
7
|
export { OpenAPIToolError, LoadError, ParseError, ValidationError, GenerationError, SchemaError } from './errors';
|
|
8
|
-
export type { McpOpenAPITool, ParameterMapper, ToolMetadata, FrontMcpExtensionData, SerializationInfo, SecurityRequirement, SecurityParameterInfo, ServerInfo, LoadOptions, GenerateOptions, NamingStrategy, OperationWithContext, OpenAPIDocument, OpenAPIVersion, HTTPMethod, ParameterLocation, AuthType, OperationObject, ParameterObject, RequestBodyObject, ResponseObject, ResponsesObject, MediaTypeObject, HeaderObject, ExampleObject, PathItemObject, PathsObject, ServerObject, SecuritySchemeObject, ReferenceObject, TagObject, ExternalDocumentationObject, ServerVariableObject, EncodingObject, SecurityRequirementObject, SchemaObject, ValidationResult, ValidationErrorDetail, ValidationWarning, } from './types';
|
|
8
|
+
export type { McpOpenAPITool, ParameterMapper, ToolMetadata, FrontMcpExtensionData, SerializationInfo, SecurityRequirement, SecurityParameterInfo, ServerInfo, RefResolutionOptions, LoadOptions, GenerateOptions, NamingStrategy, OperationWithContext, OpenAPIDocument, OpenAPIVersion, HTTPMethod, ParameterLocation, AuthType, OperationObject, ParameterObject, RequestBodyObject, ResponseObject, ResponsesObject, MediaTypeObject, HeaderObject, ExampleObject, PathItemObject, PathsObject, ServerObject, SecuritySchemeObject, ReferenceObject, TagObject, ExternalDocumentationObject, ServerVariableObject, EncodingObject, SecurityRequirementObject, SchemaObject, ValidationResult, ValidationErrorDetail, ValidationWarning, } from './types';
|
|
9
9
|
export type { SecurityContext, ResolvedSecurity, DigestAuthCredentials, ClientCertificate, AWSCredentials, SignatureData, } from './security-resolver';
|
|
10
10
|
export { isReferenceObject, toJsonSchema } from './types';
|
package/index.js
CHANGED
|
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
GenerationError: () => GenerationError,
|
|
@@ -48,13 +48,13 @@ __export(index_exports, {
|
|
|
48
48
|
});
|
|
49
49
|
module.exports = __toCommonJS(index_exports);
|
|
50
50
|
|
|
51
|
-
//
|
|
51
|
+
// src/generator.ts
|
|
52
52
|
var yaml = __toESM(require("yaml"));
|
|
53
|
-
var fs = __toESM(require("fs/promises"));
|
|
54
53
|
var path = __toESM(require("path"));
|
|
54
|
+
var fs = __toESM(require("fs/promises"));
|
|
55
55
|
var import_json_schema_ref_parser = __toESM(require("@apidevtools/json-schema-ref-parser"));
|
|
56
56
|
|
|
57
|
-
//
|
|
57
|
+
// src/types.ts
|
|
58
58
|
function isReferenceObject(obj) {
|
|
59
59
|
return obj && typeof obj === "object" && "$ref" in obj;
|
|
60
60
|
}
|
|
@@ -120,7 +120,7 @@ function toJsonSchema(schema) {
|
|
|
120
120
|
return result;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
//
|
|
123
|
+
// src/parameter-resolver.ts
|
|
124
124
|
var ParameterResolver = class {
|
|
125
125
|
namingStrategy;
|
|
126
126
|
constructor(namingStrategy) {
|
|
@@ -385,7 +385,7 @@ var ParameterResolver = class {
|
|
|
385
385
|
}
|
|
386
386
|
};
|
|
387
387
|
|
|
388
|
-
//
|
|
388
|
+
// src/response-builder.ts
|
|
389
389
|
var ResponseBuilder = class {
|
|
390
390
|
preferredStatusCodes;
|
|
391
391
|
includeAllResponses;
|
|
@@ -505,7 +505,7 @@ var ResponseBuilder = class {
|
|
|
505
505
|
}
|
|
506
506
|
};
|
|
507
507
|
|
|
508
|
-
//
|
|
508
|
+
// src/validator.ts
|
|
509
509
|
var Validator = class {
|
|
510
510
|
/**
|
|
511
511
|
* Validate an OpenAPI document
|
|
@@ -696,7 +696,7 @@ var Validator = class {
|
|
|
696
696
|
}
|
|
697
697
|
};
|
|
698
698
|
|
|
699
|
-
//
|
|
699
|
+
// src/errors.ts
|
|
700
700
|
var OpenAPIToolError = class extends Error {
|
|
701
701
|
context;
|
|
702
702
|
constructor(message, context) {
|
|
@@ -736,7 +736,7 @@ var SchemaError = class extends OpenAPIToolError {
|
|
|
736
736
|
}
|
|
737
737
|
};
|
|
738
738
|
|
|
739
|
-
//
|
|
739
|
+
// src/generator.ts
|
|
740
740
|
var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
741
741
|
document;
|
|
742
742
|
dereferencedDocument;
|
|
@@ -752,7 +752,8 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
752
752
|
headers: options.headers ?? {},
|
|
753
753
|
timeout: options.timeout ?? 3e4,
|
|
754
754
|
validate: options.validate ?? true,
|
|
755
|
-
followRedirects: options.followRedirects ?? true
|
|
755
|
+
followRedirects: options.followRedirects ?? true,
|
|
756
|
+
refResolution: options.refResolution ?? {}
|
|
756
757
|
};
|
|
757
758
|
}
|
|
758
759
|
/**
|
|
@@ -857,6 +858,106 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
857
858
|
const validator = new Validator();
|
|
858
859
|
return validator.validate(this.document);
|
|
859
860
|
}
|
|
861
|
+
/**
|
|
862
|
+
* Hostnames and IP patterns that are blocked by default to prevent SSRF.
|
|
863
|
+
* Covers RFC 1918/6598 private ranges, link-local, loopback, and cloud metadata endpoints.
|
|
864
|
+
*/
|
|
865
|
+
static BLOCKED_HOSTNAME_PATTERNS = [
|
|
866
|
+
"localhost",
|
|
867
|
+
"metadata.google.internal",
|
|
868
|
+
/^127\.\d+\.\d+\.\d+$/,
|
|
869
|
+
// 127.0.0.0/8 loopback
|
|
870
|
+
/^10\.\d+\.\d+\.\d+$/,
|
|
871
|
+
// 10.0.0.0/8 private
|
|
872
|
+
/^172\.(1[6-9]|2\d|3[01])\.\d+\.\d+$/,
|
|
873
|
+
// 172.16.0.0/12 private
|
|
874
|
+
/^192\.168\.\d+\.\d+$/,
|
|
875
|
+
// 192.168.0.0/16 private
|
|
876
|
+
/^169\.254\.\d+\.\d+$/,
|
|
877
|
+
// 169.254.0.0/16 link-local / cloud metadata
|
|
878
|
+
/^0\.0\.0\.0$/,
|
|
879
|
+
// unspecified
|
|
880
|
+
"::1",
|
|
881
|
+
// IPv6 loopback
|
|
882
|
+
/^fd[0-9a-f]{2}:/i,
|
|
883
|
+
// fd00::/8 IPv6 ULA
|
|
884
|
+
/^fe80:/i,
|
|
885
|
+
// fe80::/10 IPv6 link-local
|
|
886
|
+
/^\[::1\]$/,
|
|
887
|
+
// bracketed IPv6 loopback
|
|
888
|
+
/^\[fd[0-9a-f]{2}:/i,
|
|
889
|
+
// bracketed IPv6 ULA
|
|
890
|
+
/^\[fe80:/i
|
|
891
|
+
// bracketed IPv6 link-local
|
|
892
|
+
];
|
|
893
|
+
/**
|
|
894
|
+
* Check whether a hostname is blocked (internal/private IP or explicit blocklist).
|
|
895
|
+
*/
|
|
896
|
+
isBlockedHost(hostname, refOpts) {
|
|
897
|
+
if (refOpts.allowInternalIPs) {
|
|
898
|
+
return refOpts.blockedHosts.includes(hostname);
|
|
899
|
+
}
|
|
900
|
+
if (refOpts.blockedHosts.includes(hostname)) {
|
|
901
|
+
return true;
|
|
902
|
+
}
|
|
903
|
+
for (const pattern of _OpenAPIToolGenerator.BLOCKED_HOSTNAME_PATTERNS) {
|
|
904
|
+
if (typeof pattern === "string") {
|
|
905
|
+
if (hostname === pattern) return true;
|
|
906
|
+
} else {
|
|
907
|
+
if (pattern.test(hostname)) return true;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return false;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Build $RefParser options based on refResolution configuration.
|
|
914
|
+
* Defaults: allow http/https, block file://, block internal IPs.
|
|
915
|
+
*/
|
|
916
|
+
buildRefParserOptions() {
|
|
917
|
+
const raw = this.options.refResolution;
|
|
918
|
+
const refOpts = {
|
|
919
|
+
allowedProtocols: raw.allowedProtocols ?? ["http", "https"],
|
|
920
|
+
allowedHosts: raw.allowedHosts ?? [],
|
|
921
|
+
blockedHosts: raw.blockedHosts ?? [],
|
|
922
|
+
allowInternalIPs: raw.allowInternalIPs ?? false
|
|
923
|
+
};
|
|
924
|
+
const allowedProtocols = new Set(refOpts.allowedProtocols);
|
|
925
|
+
const hasNetworkProtocol = allowedProtocols.size > 0 && !([...allowedProtocols].length === 1 && allowedProtocols.has("file"));
|
|
926
|
+
if (allowedProtocols.size === 0) {
|
|
927
|
+
return { resolve: { external: false } };
|
|
928
|
+
}
|
|
929
|
+
const resolveConfig = {
|
|
930
|
+
external: true,
|
|
931
|
+
file: allowedProtocols.has("file") ? void 0 : false
|
|
932
|
+
};
|
|
933
|
+
if (hasNetworkProtocol) {
|
|
934
|
+
const hasHostAllowlist = refOpts.allowedHosts.length > 0;
|
|
935
|
+
const hostAllowSet = new Set(refOpts.allowedHosts);
|
|
936
|
+
resolveConfig["http"] = {
|
|
937
|
+
canRead: (file) => {
|
|
938
|
+
try {
|
|
939
|
+
const parsed = new URL(file.url);
|
|
940
|
+
const protocol = parsed.protocol.replace(":", "");
|
|
941
|
+
if (!allowedProtocols.has(protocol)) {
|
|
942
|
+
return false;
|
|
943
|
+
}
|
|
944
|
+
if (hasHostAllowlist && !hostAllowSet.has(parsed.hostname)) {
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
947
|
+
if (this.isBlockedHost(parsed.hostname, refOpts)) {
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
return true;
|
|
951
|
+
} catch {
|
|
952
|
+
return false;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
} else {
|
|
957
|
+
resolveConfig["http"] = false;
|
|
958
|
+
}
|
|
959
|
+
return { resolve: resolveConfig };
|
|
960
|
+
}
|
|
860
961
|
/**
|
|
861
962
|
* Initialize the generator (dereference if needed)
|
|
862
963
|
*/
|
|
@@ -869,8 +970,10 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
869
970
|
}
|
|
870
971
|
if (this.options.dereference && !this.dereferencedDocument) {
|
|
871
972
|
try {
|
|
973
|
+
const refParserOptions = this.buildRefParserOptions();
|
|
872
974
|
this.dereferencedDocument = await import_json_schema_ref_parser.default.dereference(
|
|
873
|
-
JSON.parse(JSON.stringify(this.document))
|
|
975
|
+
JSON.parse(JSON.stringify(this.document)),
|
|
976
|
+
refParserOptions
|
|
874
977
|
);
|
|
875
978
|
} catch (error) {
|
|
876
979
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1074,7 +1177,7 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
1074
1177
|
}
|
|
1075
1178
|
};
|
|
1076
1179
|
|
|
1077
|
-
//
|
|
1180
|
+
// src/schema-builder.ts
|
|
1078
1181
|
var SchemaBuilder = class {
|
|
1079
1182
|
/**
|
|
1080
1183
|
* Merge multiple schemas into one
|
|
@@ -1353,7 +1456,7 @@ var SchemaBuilder = class {
|
|
|
1353
1456
|
}
|
|
1354
1457
|
};
|
|
1355
1458
|
|
|
1356
|
-
//
|
|
1459
|
+
// src/security-resolver.ts
|
|
1357
1460
|
var SecurityResolver = class {
|
|
1358
1461
|
/**
|
|
1359
1462
|
* Resolve security parameters from mapper entries
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-from-openapi",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Production-ready library for converting OpenAPI specifications into MCP tool definitions",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -20,13 +20,12 @@
|
|
|
20
20
|
],
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "git+https://github.com/agentfront/
|
|
24
|
-
"directory": "libs/mcp-from-openapi"
|
|
23
|
+
"url": "git+https://github.com/agentfront/mcp-from-openapi.git"
|
|
25
24
|
},
|
|
26
25
|
"bugs": {
|
|
27
|
-
"url": "https://github.com/agentfront/
|
|
26
|
+
"url": "https://github.com/agentfront/mcp-from-openapi/issues"
|
|
28
27
|
},
|
|
29
|
-
"homepage": "https://github.com/agentfront/
|
|
28
|
+
"homepage": "https://github.com/agentfront/mcp-from-openapi#readme",
|
|
30
29
|
"engines": {
|
|
31
30
|
"node": ">=18.0.0"
|
|
32
31
|
},
|
|
@@ -46,19 +45,25 @@
|
|
|
46
45
|
"types": "./index.d.ts",
|
|
47
46
|
"default": "./esm/index.mjs"
|
|
48
47
|
}
|
|
49
|
-
}
|
|
50
|
-
"./esm": null
|
|
48
|
+
}
|
|
51
49
|
},
|
|
52
50
|
"dependencies": {
|
|
53
51
|
"@apidevtools/json-schema-ref-parser": "^11.9.3",
|
|
54
52
|
"openapi-types": "^12.1.3",
|
|
55
|
-
"yaml": "^2.8.
|
|
53
|
+
"yaml": "^2.8.3"
|
|
56
54
|
},
|
|
57
55
|
"peerDependencies": {
|
|
58
56
|
"zod": "^4.0.0"
|
|
59
57
|
},
|
|
60
58
|
"devDependencies": {
|
|
59
|
+
"@swc/core": "~1.5.7",
|
|
60
|
+
"@swc/helpers": "^0.5.18",
|
|
61
|
+
"@swc/jest": "~0.2.38",
|
|
62
|
+
"@types/jest": "^29.5.0",
|
|
61
63
|
"@types/node": "^24.0.0",
|
|
64
|
+
"esbuild": "^0.27.2",
|
|
65
|
+
"jest": "^29.7.0",
|
|
66
|
+
"tslib": "^2.8.1",
|
|
62
67
|
"typescript": "^5.0.0",
|
|
63
68
|
"zod": "^4.0.0"
|
|
64
69
|
}
|
package/types.d.ts
CHANGED
|
@@ -326,6 +326,37 @@ export interface ServerInfo {
|
|
|
326
326
|
*/
|
|
327
327
|
variables?: Record<string, ServerVariableObject>;
|
|
328
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Controls how external $ref pointers are resolved during dereferencing.
|
|
331
|
+
* By default, only http/https protocols are allowed and internal/private
|
|
332
|
+
* IP addresses are blocked to prevent SSRF attacks.
|
|
333
|
+
*/
|
|
334
|
+
export interface RefResolutionOptions {
|
|
335
|
+
/**
|
|
336
|
+
* Protocols allowed for external $ref resolution.
|
|
337
|
+
* Any protocol string is accepted (http, https, ftp, ws, wss, etc.).
|
|
338
|
+
* @default ['http', 'https']
|
|
339
|
+
*/
|
|
340
|
+
allowedProtocols?: string[];
|
|
341
|
+
/**
|
|
342
|
+
* Hostnames allowed for external $ref resolution (network protocols only).
|
|
343
|
+
* When set, only refs pointing to these hosts are resolved.
|
|
344
|
+
* When not set, all hosts are allowed except blocked internal ranges.
|
|
345
|
+
*/
|
|
346
|
+
allowedHosts?: string[];
|
|
347
|
+
/**
|
|
348
|
+
* Additional hostnames/IPs to block. Applied on top of the built-in
|
|
349
|
+
* internal IP block list (localhost, 169.254.x.x, 10.x.x.x, etc.).
|
|
350
|
+
*/
|
|
351
|
+
blockedHosts?: string[];
|
|
352
|
+
/**
|
|
353
|
+
* Disable the built-in internal/private IP block list.
|
|
354
|
+
* WARNING: Enabling this may expose your application to SSRF attacks
|
|
355
|
+
* against cloud metadata endpoints and internal services.
|
|
356
|
+
* @default false
|
|
357
|
+
*/
|
|
358
|
+
allowInternalIPs?: boolean;
|
|
359
|
+
}
|
|
329
360
|
/**
|
|
330
361
|
* Options for loading OpenAPI specifications
|
|
331
362
|
*/
|
|
@@ -359,6 +390,12 @@ export interface LoadOptions {
|
|
|
359
390
|
* @default true
|
|
360
391
|
*/
|
|
361
392
|
followRedirects?: boolean;
|
|
393
|
+
/**
|
|
394
|
+
* Controls external $ref resolution security.
|
|
395
|
+
* By default, file:// is blocked and internal IPs are blocked.
|
|
396
|
+
* @see RefResolutionOptions
|
|
397
|
+
*/
|
|
398
|
+
refResolution?: RefResolutionOptions;
|
|
362
399
|
}
|
|
363
400
|
/**
|
|
364
401
|
* Operation object with additional context for filtering
|