mcp-from-openapi 2.2.0 → 2.3.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/README.md +61 -554
- package/esm/index.mjs +132 -10
- package/esm/package.json +4 -3
- package/format-resolver.d.ts +12 -0
- package/generator.d.ts +1 -1
- package/index.d.ts +2 -1
- package/index.js +135 -11
- package/package.json +4 -3
- package/types.d.ts +22 -2
- package/CHANGELOG.md +0 -83
package/esm/index.mjs
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import * as yaml from "yaml";
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import * as fs from "fs/promises";
|
|
5
|
-
import $RefParser from "@apidevtools/json-schema-ref-parser";
|
|
6
5
|
|
|
7
6
|
// src/types.ts
|
|
8
7
|
function isReferenceObject(obj) {
|
|
@@ -686,6 +685,115 @@ var SchemaError = class extends OpenAPIToolError {
|
|
|
686
685
|
}
|
|
687
686
|
};
|
|
688
687
|
|
|
688
|
+
// src/format-resolver.ts
|
|
689
|
+
var BUILTIN_FORMAT_RESOLVERS = {
|
|
690
|
+
// String formats
|
|
691
|
+
uuid: (schema) => ({
|
|
692
|
+
...schema,
|
|
693
|
+
pattern: schema.pattern ?? "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
|
|
694
|
+
description: schema.description || "UUID string (RFC 4122)"
|
|
695
|
+
}),
|
|
696
|
+
"date-time": (schema) => ({
|
|
697
|
+
...schema,
|
|
698
|
+
description: schema.description || "ISO 8601 date-time (e.g., 2024-01-15T09:30:00Z)"
|
|
699
|
+
}),
|
|
700
|
+
date: (schema) => ({
|
|
701
|
+
...schema,
|
|
702
|
+
pattern: schema.pattern ?? "^\\d{4}-\\d{2}-\\d{2}$",
|
|
703
|
+
description: schema.description || "ISO 8601 date (e.g., 2024-01-15)"
|
|
704
|
+
}),
|
|
705
|
+
time: (schema) => ({
|
|
706
|
+
...schema,
|
|
707
|
+
pattern: schema.pattern ?? "^\\d{2}:\\d{2}:\\d{2}",
|
|
708
|
+
description: schema.description || "ISO 8601 time (e.g., 09:30:00)"
|
|
709
|
+
}),
|
|
710
|
+
email: (schema) => ({
|
|
711
|
+
...schema,
|
|
712
|
+
description: schema.description || "Email address (RFC 5322)"
|
|
713
|
+
}),
|
|
714
|
+
uri: (schema) => ({
|
|
715
|
+
...schema,
|
|
716
|
+
description: schema.description || "URI (RFC 3986)"
|
|
717
|
+
}),
|
|
718
|
+
"uri-reference": (schema) => ({
|
|
719
|
+
...schema,
|
|
720
|
+
description: schema.description || "URI reference (RFC 3986)"
|
|
721
|
+
}),
|
|
722
|
+
hostname: (schema) => ({
|
|
723
|
+
...schema,
|
|
724
|
+
description: schema.description || "Internet hostname (RFC 1123)"
|
|
725
|
+
}),
|
|
726
|
+
ipv4: (schema) => ({
|
|
727
|
+
...schema,
|
|
728
|
+
pattern: schema.pattern ?? "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$",
|
|
729
|
+
description: schema.description || "IPv4 address"
|
|
730
|
+
}),
|
|
731
|
+
ipv6: (schema) => ({
|
|
732
|
+
...schema,
|
|
733
|
+
description: schema.description || "IPv6 address (RFC 4291)"
|
|
734
|
+
}),
|
|
735
|
+
// Integer formats
|
|
736
|
+
int32: (schema) => ({
|
|
737
|
+
...schema,
|
|
738
|
+
minimum: schema.minimum ?? -2147483648,
|
|
739
|
+
maximum: schema.maximum ?? 2147483647
|
|
740
|
+
}),
|
|
741
|
+
int64: (schema) => ({
|
|
742
|
+
...schema,
|
|
743
|
+
minimum: schema.minimum ?? Number.MIN_SAFE_INTEGER,
|
|
744
|
+
maximum: schema.maximum ?? Number.MAX_SAFE_INTEGER
|
|
745
|
+
}),
|
|
746
|
+
// Binary/encoding formats
|
|
747
|
+
byte: (schema) => ({
|
|
748
|
+
...schema,
|
|
749
|
+
pattern: schema.pattern ?? "^[A-Za-z0-9+/]*={0,2}$",
|
|
750
|
+
description: schema.description || "Base64-encoded string (RFC 4648)"
|
|
751
|
+
}),
|
|
752
|
+
binary: (schema) => ({
|
|
753
|
+
...schema,
|
|
754
|
+
description: schema.description || "Binary data"
|
|
755
|
+
}),
|
|
756
|
+
// Sensitive data formats
|
|
757
|
+
password: (schema) => ({
|
|
758
|
+
...schema,
|
|
759
|
+
description: schema.description || "Password (sensitive, UI should mask input)"
|
|
760
|
+
})
|
|
761
|
+
};
|
|
762
|
+
function resolveSchemaFormats(schema, resolvers) {
|
|
763
|
+
if (!schema || typeof schema !== "object") return schema;
|
|
764
|
+
let result = { ...schema };
|
|
765
|
+
const format = result["format"];
|
|
766
|
+
if (format && resolvers[format]) {
|
|
767
|
+
result = { ...resolvers[format](result) };
|
|
768
|
+
}
|
|
769
|
+
if (result["properties"] && typeof result["properties"] === "object") {
|
|
770
|
+
const props = {};
|
|
771
|
+
for (const [key, value] of Object.entries(result["properties"])) {
|
|
772
|
+
props[key] = resolveSchemaFormats(value, resolvers);
|
|
773
|
+
}
|
|
774
|
+
result["properties"] = props;
|
|
775
|
+
}
|
|
776
|
+
if (result["items"]) {
|
|
777
|
+
if (Array.isArray(result["items"])) {
|
|
778
|
+
result["items"] = result["items"].map((item) => resolveSchemaFormats(item, resolvers));
|
|
779
|
+
} else {
|
|
780
|
+
result["items"] = resolveSchemaFormats(result["items"], resolvers);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
if (result["additionalProperties"] && typeof result["additionalProperties"] === "object") {
|
|
784
|
+
result["additionalProperties"] = resolveSchemaFormats(result["additionalProperties"], resolvers);
|
|
785
|
+
}
|
|
786
|
+
for (const key of ["allOf", "anyOf", "oneOf"]) {
|
|
787
|
+
if (result[key] && Array.isArray(result[key])) {
|
|
788
|
+
result[key] = result[key].map((s) => resolveSchemaFormats(s, resolvers));
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
if (result["not"] && typeof result["not"] === "object") {
|
|
792
|
+
result["not"] = resolveSchemaFormats(result["not"], resolvers);
|
|
793
|
+
}
|
|
794
|
+
return result;
|
|
795
|
+
}
|
|
796
|
+
|
|
689
797
|
// src/generator.ts
|
|
690
798
|
var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
691
799
|
document;
|
|
@@ -909,17 +1017,12 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
909
1017
|
return { resolve: resolveConfig };
|
|
910
1018
|
}
|
|
911
1019
|
/**
|
|
912
|
-
* Initialize the generator (dereference if needed)
|
|
1020
|
+
* Initialize the generator (dereference if needed, then validate)
|
|
913
1021
|
*/
|
|
914
1022
|
async initialize() {
|
|
915
|
-
if (this.options.validate) {
|
|
916
|
-
const result = await this.validate();
|
|
917
|
-
if (!result.valid) {
|
|
918
|
-
throw new ParseError("Invalid OpenAPI document", { errors: result.errors });
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
1023
|
if (this.options.dereference && !this.dereferencedDocument) {
|
|
922
1024
|
try {
|
|
1025
|
+
const { default: $RefParser } = await import("@apidevtools/json-schema-ref-parser");
|
|
923
1026
|
const refParserOptions = this.buildRefParserOptions();
|
|
924
1027
|
this.dereferencedDocument = await $RefParser.dereference(
|
|
925
1028
|
JSON.parse(JSON.stringify(this.document)),
|
|
@@ -932,6 +1035,14 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
932
1035
|
});
|
|
933
1036
|
}
|
|
934
1037
|
}
|
|
1038
|
+
if (this.options.validate) {
|
|
1039
|
+
const validator = new Validator();
|
|
1040
|
+
const documentToValidate = this.dereferencedDocument ?? this.document;
|
|
1041
|
+
const result = await validator.validate(documentToValidate);
|
|
1042
|
+
if (!result.valid) {
|
|
1043
|
+
throw new ParseError("Invalid OpenAPI document", { errors: result.errors });
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
935
1046
|
}
|
|
936
1047
|
/**
|
|
937
1048
|
* Generate all tools from the OpenAPI specification
|
|
@@ -1000,11 +1111,18 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
1000
1111
|
const name = this.generateToolName(pathStr, method, operation.operationId, options);
|
|
1001
1112
|
const description = operation.summary || operation.description || `${method.toUpperCase()} ${pathStr}`;
|
|
1002
1113
|
const metadata = this.extractMetadata(pathStr, method, operation, document, outputSchema);
|
|
1114
|
+
const formatResolvers = {
|
|
1115
|
+
...options.resolveFormats ? BUILTIN_FORMAT_RESOLVERS : {},
|
|
1116
|
+
...options.formatResolvers
|
|
1117
|
+
};
|
|
1118
|
+
const hasFormatResolvers = Object.keys(formatResolvers).length > 0;
|
|
1119
|
+
const resolvedInputSchema = hasFormatResolvers ? resolveSchemaFormats(inputSchema, formatResolvers) : inputSchema;
|
|
1120
|
+
const resolvedOutputSchema = hasFormatResolvers && outputSchema ? resolveSchemaFormats(outputSchema, formatResolvers) : outputSchema;
|
|
1003
1121
|
return {
|
|
1004
1122
|
name,
|
|
1005
1123
|
description,
|
|
1006
|
-
inputSchema,
|
|
1007
|
-
outputSchema,
|
|
1124
|
+
inputSchema: resolvedInputSchema,
|
|
1125
|
+
outputSchema: resolvedOutputSchema,
|
|
1008
1126
|
mapper,
|
|
1009
1127
|
metadata
|
|
1010
1128
|
};
|
|
@@ -1454,6 +1572,7 @@ var SecurityResolver = class {
|
|
|
1454
1572
|
if (requiresSignature) {
|
|
1455
1573
|
resolved.requiresSignature = true;
|
|
1456
1574
|
resolved.signatureInfo = {
|
|
1575
|
+
/* c8 ignore next -- signatureScheme is always set from security.scheme */
|
|
1457
1576
|
scheme: signatureScheme || "unknown"
|
|
1458
1577
|
};
|
|
1459
1578
|
}
|
|
@@ -1499,6 +1618,7 @@ var SecurityResolver = class {
|
|
|
1499
1618
|
return this.resolveBasicAuth(context);
|
|
1500
1619
|
case "digest":
|
|
1501
1620
|
return this.resolveDigestAuth(context);
|
|
1621
|
+
/* c8 ignore next -- hoba is part of the same fall-through as mutual/negotiate/vapid/scram */
|
|
1502
1622
|
case "hoba":
|
|
1503
1623
|
case "mutual":
|
|
1504
1624
|
case "negotiate":
|
|
@@ -1646,6 +1766,7 @@ function createSecurityContext(auth) {
|
|
|
1646
1766
|
};
|
|
1647
1767
|
}
|
|
1648
1768
|
export {
|
|
1769
|
+
BUILTIN_FORMAT_RESOLVERS,
|
|
1649
1770
|
GenerationError,
|
|
1650
1771
|
LoadError,
|
|
1651
1772
|
OpenAPIToolError,
|
|
@@ -1660,5 +1781,6 @@ export {
|
|
|
1660
1781
|
Validator,
|
|
1661
1782
|
createSecurityContext,
|
|
1662
1783
|
isReferenceObject,
|
|
1784
|
+
resolveSchemaFormats,
|
|
1663
1785
|
toJsonSchema
|
|
1664
1786
|
};
|
package/esm/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-from-openapi",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.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",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://github.com/agentfront/mcp-from-openapi#readme",
|
|
29
29
|
"engines": {
|
|
30
|
-
"node": ">=
|
|
30
|
+
"node": ">=20.0.0"
|
|
31
31
|
},
|
|
32
32
|
"type": "module",
|
|
33
33
|
"main": "../index.js",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@apidevtools/json-schema-ref-parser": "^
|
|
51
|
+
"@apidevtools/json-schema-ref-parser": "^15.3.5",
|
|
52
52
|
"openapi-types": "^12.1.3",
|
|
53
53
|
"yaml": "^2.8.3"
|
|
54
54
|
},
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"@swc/helpers": "^0.5.18",
|
|
61
61
|
"@swc/jest": "~0.2.38",
|
|
62
62
|
"@types/jest": "^29.5.0",
|
|
63
|
+
"@types/json-schema": "^7.0.15",
|
|
63
64
|
"@types/node": "^24.0.0",
|
|
64
65
|
"esbuild": "^0.27.2",
|
|
65
66
|
"jest": "^29.7.0",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { JsonSchema, FormatResolver } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Built-in format resolvers that enrich JSON Schema with concrete constraints.
|
|
4
|
+
* Each resolver only adds constraints if not already present on the schema.
|
|
5
|
+
*/
|
|
6
|
+
export declare const BUILTIN_FORMAT_RESOLVERS: Record<string, FormatResolver>;
|
|
7
|
+
/**
|
|
8
|
+
* Recursively resolve format fields in a JSON Schema tree.
|
|
9
|
+
* For each schema node with a `format` field, the matching resolver
|
|
10
|
+
* is applied to enrich the schema with concrete constraints.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveSchemaFormats(schema: JsonSchema, resolvers: Record<string, FormatResolver>): JsonSchema;
|
package/generator.d.ts
CHANGED
package/index.d.ts
CHANGED
|
@@ -4,7 +4,8 @@ export { ParameterResolver } from './parameter-resolver';
|
|
|
4
4
|
export { ResponseBuilder } from './response-builder';
|
|
5
5
|
export { Validator } from './validator';
|
|
6
6
|
export { SecurityResolver, createSecurityContext } from './security-resolver';
|
|
7
|
+
export { BUILTIN_FORMAT_RESOLVERS, resolveSchemaFormats } from './format-resolver';
|
|
7
8
|
export { OpenAPIToolError, LoadError, ParseError, ValidationError, GenerationError, SchemaError } from './errors';
|
|
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
|
+
export type { McpOpenAPITool, ParameterMapper, ToolMetadata, FrontMcpExtensionData, SerializationInfo, SecurityRequirement, SecurityParameterInfo, ServerInfo, RefResolutionOptions, LoadOptions, GenerateOptions, FormatResolver, JsonSchema, 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
10
|
export type { SecurityContext, ResolvedSecurity, DigestAuthCredentials, ClientCertificate, AWSCredentials, SignatureData, } from './security-resolver';
|
|
10
11
|
export { isReferenceObject, toJsonSchema } from './types';
|
package/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
BUILTIN_FORMAT_RESOLVERS: () => BUILTIN_FORMAT_RESOLVERS,
|
|
33
34
|
GenerationError: () => GenerationError,
|
|
34
35
|
LoadError: () => LoadError,
|
|
35
36
|
OpenAPIToolError: () => OpenAPIToolError,
|
|
@@ -44,6 +45,7 @@ __export(index_exports, {
|
|
|
44
45
|
Validator: () => Validator,
|
|
45
46
|
createSecurityContext: () => createSecurityContext,
|
|
46
47
|
isReferenceObject: () => isReferenceObject,
|
|
48
|
+
resolveSchemaFormats: () => resolveSchemaFormats,
|
|
47
49
|
toJsonSchema: () => toJsonSchema
|
|
48
50
|
});
|
|
49
51
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -52,7 +54,6 @@ module.exports = __toCommonJS(index_exports);
|
|
|
52
54
|
var yaml = __toESM(require("yaml"));
|
|
53
55
|
var path = __toESM(require("path"));
|
|
54
56
|
var fs = __toESM(require("fs/promises"));
|
|
55
|
-
var import_json_schema_ref_parser = __toESM(require("@apidevtools/json-schema-ref-parser"));
|
|
56
57
|
|
|
57
58
|
// src/types.ts
|
|
58
59
|
function isReferenceObject(obj) {
|
|
@@ -736,6 +737,115 @@ var SchemaError = class extends OpenAPIToolError {
|
|
|
736
737
|
}
|
|
737
738
|
};
|
|
738
739
|
|
|
740
|
+
// src/format-resolver.ts
|
|
741
|
+
var BUILTIN_FORMAT_RESOLVERS = {
|
|
742
|
+
// String formats
|
|
743
|
+
uuid: (schema) => ({
|
|
744
|
+
...schema,
|
|
745
|
+
pattern: schema.pattern ?? "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
|
|
746
|
+
description: schema.description || "UUID string (RFC 4122)"
|
|
747
|
+
}),
|
|
748
|
+
"date-time": (schema) => ({
|
|
749
|
+
...schema,
|
|
750
|
+
description: schema.description || "ISO 8601 date-time (e.g., 2024-01-15T09:30:00Z)"
|
|
751
|
+
}),
|
|
752
|
+
date: (schema) => ({
|
|
753
|
+
...schema,
|
|
754
|
+
pattern: schema.pattern ?? "^\\d{4}-\\d{2}-\\d{2}$",
|
|
755
|
+
description: schema.description || "ISO 8601 date (e.g., 2024-01-15)"
|
|
756
|
+
}),
|
|
757
|
+
time: (schema) => ({
|
|
758
|
+
...schema,
|
|
759
|
+
pattern: schema.pattern ?? "^\\d{2}:\\d{2}:\\d{2}",
|
|
760
|
+
description: schema.description || "ISO 8601 time (e.g., 09:30:00)"
|
|
761
|
+
}),
|
|
762
|
+
email: (schema) => ({
|
|
763
|
+
...schema,
|
|
764
|
+
description: schema.description || "Email address (RFC 5322)"
|
|
765
|
+
}),
|
|
766
|
+
uri: (schema) => ({
|
|
767
|
+
...schema,
|
|
768
|
+
description: schema.description || "URI (RFC 3986)"
|
|
769
|
+
}),
|
|
770
|
+
"uri-reference": (schema) => ({
|
|
771
|
+
...schema,
|
|
772
|
+
description: schema.description || "URI reference (RFC 3986)"
|
|
773
|
+
}),
|
|
774
|
+
hostname: (schema) => ({
|
|
775
|
+
...schema,
|
|
776
|
+
description: schema.description || "Internet hostname (RFC 1123)"
|
|
777
|
+
}),
|
|
778
|
+
ipv4: (schema) => ({
|
|
779
|
+
...schema,
|
|
780
|
+
pattern: schema.pattern ?? "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$",
|
|
781
|
+
description: schema.description || "IPv4 address"
|
|
782
|
+
}),
|
|
783
|
+
ipv6: (schema) => ({
|
|
784
|
+
...schema,
|
|
785
|
+
description: schema.description || "IPv6 address (RFC 4291)"
|
|
786
|
+
}),
|
|
787
|
+
// Integer formats
|
|
788
|
+
int32: (schema) => ({
|
|
789
|
+
...schema,
|
|
790
|
+
minimum: schema.minimum ?? -2147483648,
|
|
791
|
+
maximum: schema.maximum ?? 2147483647
|
|
792
|
+
}),
|
|
793
|
+
int64: (schema) => ({
|
|
794
|
+
...schema,
|
|
795
|
+
minimum: schema.minimum ?? Number.MIN_SAFE_INTEGER,
|
|
796
|
+
maximum: schema.maximum ?? Number.MAX_SAFE_INTEGER
|
|
797
|
+
}),
|
|
798
|
+
// Binary/encoding formats
|
|
799
|
+
byte: (schema) => ({
|
|
800
|
+
...schema,
|
|
801
|
+
pattern: schema.pattern ?? "^[A-Za-z0-9+/]*={0,2}$",
|
|
802
|
+
description: schema.description || "Base64-encoded string (RFC 4648)"
|
|
803
|
+
}),
|
|
804
|
+
binary: (schema) => ({
|
|
805
|
+
...schema,
|
|
806
|
+
description: schema.description || "Binary data"
|
|
807
|
+
}),
|
|
808
|
+
// Sensitive data formats
|
|
809
|
+
password: (schema) => ({
|
|
810
|
+
...schema,
|
|
811
|
+
description: schema.description || "Password (sensitive, UI should mask input)"
|
|
812
|
+
})
|
|
813
|
+
};
|
|
814
|
+
function resolveSchemaFormats(schema, resolvers) {
|
|
815
|
+
if (!schema || typeof schema !== "object") return schema;
|
|
816
|
+
let result = { ...schema };
|
|
817
|
+
const format = result["format"];
|
|
818
|
+
if (format && resolvers[format]) {
|
|
819
|
+
result = { ...resolvers[format](result) };
|
|
820
|
+
}
|
|
821
|
+
if (result["properties"] && typeof result["properties"] === "object") {
|
|
822
|
+
const props = {};
|
|
823
|
+
for (const [key, value] of Object.entries(result["properties"])) {
|
|
824
|
+
props[key] = resolveSchemaFormats(value, resolvers);
|
|
825
|
+
}
|
|
826
|
+
result["properties"] = props;
|
|
827
|
+
}
|
|
828
|
+
if (result["items"]) {
|
|
829
|
+
if (Array.isArray(result["items"])) {
|
|
830
|
+
result["items"] = result["items"].map((item) => resolveSchemaFormats(item, resolvers));
|
|
831
|
+
} else {
|
|
832
|
+
result["items"] = resolveSchemaFormats(result["items"], resolvers);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
if (result["additionalProperties"] && typeof result["additionalProperties"] === "object") {
|
|
836
|
+
result["additionalProperties"] = resolveSchemaFormats(result["additionalProperties"], resolvers);
|
|
837
|
+
}
|
|
838
|
+
for (const key of ["allOf", "anyOf", "oneOf"]) {
|
|
839
|
+
if (result[key] && Array.isArray(result[key])) {
|
|
840
|
+
result[key] = result[key].map((s) => resolveSchemaFormats(s, resolvers));
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if (result["not"] && typeof result["not"] === "object") {
|
|
844
|
+
result["not"] = resolveSchemaFormats(result["not"], resolvers);
|
|
845
|
+
}
|
|
846
|
+
return result;
|
|
847
|
+
}
|
|
848
|
+
|
|
739
849
|
// src/generator.ts
|
|
740
850
|
var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
741
851
|
document;
|
|
@@ -959,19 +1069,14 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
959
1069
|
return { resolve: resolveConfig };
|
|
960
1070
|
}
|
|
961
1071
|
/**
|
|
962
|
-
* Initialize the generator (dereference if needed)
|
|
1072
|
+
* Initialize the generator (dereference if needed, then validate)
|
|
963
1073
|
*/
|
|
964
1074
|
async initialize() {
|
|
965
|
-
if (this.options.validate) {
|
|
966
|
-
const result = await this.validate();
|
|
967
|
-
if (!result.valid) {
|
|
968
|
-
throw new ParseError("Invalid OpenAPI document", { errors: result.errors });
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
1075
|
if (this.options.dereference && !this.dereferencedDocument) {
|
|
972
1076
|
try {
|
|
1077
|
+
const { default: $RefParser } = await import("@apidevtools/json-schema-ref-parser");
|
|
973
1078
|
const refParserOptions = this.buildRefParserOptions();
|
|
974
|
-
this.dereferencedDocument = await
|
|
1079
|
+
this.dereferencedDocument = await $RefParser.dereference(
|
|
975
1080
|
JSON.parse(JSON.stringify(this.document)),
|
|
976
1081
|
refParserOptions
|
|
977
1082
|
);
|
|
@@ -982,6 +1087,14 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
982
1087
|
});
|
|
983
1088
|
}
|
|
984
1089
|
}
|
|
1090
|
+
if (this.options.validate) {
|
|
1091
|
+
const validator = new Validator();
|
|
1092
|
+
const documentToValidate = this.dereferencedDocument ?? this.document;
|
|
1093
|
+
const result = await validator.validate(documentToValidate);
|
|
1094
|
+
if (!result.valid) {
|
|
1095
|
+
throw new ParseError("Invalid OpenAPI document", { errors: result.errors });
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
985
1098
|
}
|
|
986
1099
|
/**
|
|
987
1100
|
* Generate all tools from the OpenAPI specification
|
|
@@ -1050,11 +1163,18 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
|
|
|
1050
1163
|
const name = this.generateToolName(pathStr, method, operation.operationId, options);
|
|
1051
1164
|
const description = operation.summary || operation.description || `${method.toUpperCase()} ${pathStr}`;
|
|
1052
1165
|
const metadata = this.extractMetadata(pathStr, method, operation, document, outputSchema);
|
|
1166
|
+
const formatResolvers = {
|
|
1167
|
+
...options.resolveFormats ? BUILTIN_FORMAT_RESOLVERS : {},
|
|
1168
|
+
...options.formatResolvers
|
|
1169
|
+
};
|
|
1170
|
+
const hasFormatResolvers = Object.keys(formatResolvers).length > 0;
|
|
1171
|
+
const resolvedInputSchema = hasFormatResolvers ? resolveSchemaFormats(inputSchema, formatResolvers) : inputSchema;
|
|
1172
|
+
const resolvedOutputSchema = hasFormatResolvers && outputSchema ? resolveSchemaFormats(outputSchema, formatResolvers) : outputSchema;
|
|
1053
1173
|
return {
|
|
1054
1174
|
name,
|
|
1055
1175
|
description,
|
|
1056
|
-
inputSchema,
|
|
1057
|
-
outputSchema,
|
|
1176
|
+
inputSchema: resolvedInputSchema,
|
|
1177
|
+
outputSchema: resolvedOutputSchema,
|
|
1058
1178
|
mapper,
|
|
1059
1179
|
metadata
|
|
1060
1180
|
};
|
|
@@ -1504,6 +1624,7 @@ var SecurityResolver = class {
|
|
|
1504
1624
|
if (requiresSignature) {
|
|
1505
1625
|
resolved.requiresSignature = true;
|
|
1506
1626
|
resolved.signatureInfo = {
|
|
1627
|
+
/* c8 ignore next -- signatureScheme is always set from security.scheme */
|
|
1507
1628
|
scheme: signatureScheme || "unknown"
|
|
1508
1629
|
};
|
|
1509
1630
|
}
|
|
@@ -1549,6 +1670,7 @@ var SecurityResolver = class {
|
|
|
1549
1670
|
return this.resolveBasicAuth(context);
|
|
1550
1671
|
case "digest":
|
|
1551
1672
|
return this.resolveDigestAuth(context);
|
|
1673
|
+
/* c8 ignore next -- hoba is part of the same fall-through as mutual/negotiate/vapid/scram */
|
|
1552
1674
|
case "hoba":
|
|
1553
1675
|
case "mutual":
|
|
1554
1676
|
case "negotiate":
|
|
@@ -1697,6 +1819,7 @@ function createSecurityContext(auth) {
|
|
|
1697
1819
|
}
|
|
1698
1820
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1699
1821
|
0 && (module.exports = {
|
|
1822
|
+
BUILTIN_FORMAT_RESOLVERS,
|
|
1700
1823
|
GenerationError,
|
|
1701
1824
|
LoadError,
|
|
1702
1825
|
OpenAPIToolError,
|
|
@@ -1711,5 +1834,6 @@ function createSecurityContext(auth) {
|
|
|
1711
1834
|
Validator,
|
|
1712
1835
|
createSecurityContext,
|
|
1713
1836
|
isReferenceObject,
|
|
1837
|
+
resolveSchemaFormats,
|
|
1714
1838
|
toJsonSchema
|
|
1715
1839
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-from-openapi",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.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",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://github.com/agentfront/mcp-from-openapi#readme",
|
|
29
29
|
"engines": {
|
|
30
|
-
"node": ">=
|
|
30
|
+
"node": ">=20.0.0"
|
|
31
31
|
},
|
|
32
32
|
"type": "commonjs",
|
|
33
33
|
"main": "./index.js",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@apidevtools/json-schema-ref-parser": "^
|
|
51
|
+
"@apidevtools/json-schema-ref-parser": "^15.3.5",
|
|
52
52
|
"openapi-types": "^12.1.3",
|
|
53
53
|
"yaml": "^2.8.3"
|
|
54
54
|
},
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"@swc/helpers": "^0.5.18",
|
|
61
61
|
"@swc/jest": "~0.2.38",
|
|
62
62
|
"@types/jest": "^29.5.0",
|
|
63
|
+
"@types/json-schema": "^7.0.15",
|
|
63
64
|
"@types/node": "^24.0.0",
|
|
64
65
|
"esbuild": "^0.27.2",
|
|
65
66
|
"jest": "^29.7.0",
|
package/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { JSONSchema } from 'zod/v4/core';
|
|
2
2
|
/** JSON Schema type from Zod v4 */
|
|
3
|
-
type JsonSchema = JSONSchema.JSONSchema;
|
|
3
|
+
export type JsonSchema = JSONSchema.JSONSchema;
|
|
4
4
|
import type { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
|
|
5
5
|
/**
|
|
6
6
|
* OpenAPI specification version 3.0.x or 3.1.x
|
|
@@ -457,7 +457,28 @@ export interface GenerateOptions {
|
|
|
457
457
|
* @default false
|
|
458
458
|
*/
|
|
459
459
|
includeSecurityInInput?: boolean;
|
|
460
|
+
/**
|
|
461
|
+
* Enable built-in format-to-schema resolution.
|
|
462
|
+
* Enriches schemas with concrete constraints (patterns, descriptions, min/max)
|
|
463
|
+
* based on OpenAPI format values (uuid, date-time, email, int32, etc.).
|
|
464
|
+
* @default false
|
|
465
|
+
*/
|
|
466
|
+
resolveFormats?: boolean;
|
|
467
|
+
/**
|
|
468
|
+
* Custom format resolvers. Keys are format names, values are functions
|
|
469
|
+
* that receive the original schema and return an enriched schema.
|
|
470
|
+
*
|
|
471
|
+
* When used with `resolveFormats: true`, custom resolvers are merged with
|
|
472
|
+
* built-in resolvers (custom takes precedence for the same format).
|
|
473
|
+
* When used without `resolveFormats`, only custom resolvers are applied.
|
|
474
|
+
*/
|
|
475
|
+
formatResolvers?: Record<string, FormatResolver>;
|
|
460
476
|
}
|
|
477
|
+
/**
|
|
478
|
+
* A function that enriches a JSON Schema based on its format field.
|
|
479
|
+
* Receives the schema and returns a new schema with additional constraints.
|
|
480
|
+
*/
|
|
481
|
+
export type FormatResolver = (schema: JsonSchema) => JsonSchema;
|
|
461
482
|
/**
|
|
462
483
|
* Naming strategy for resolving parameter conflicts
|
|
463
484
|
*/
|
|
@@ -530,4 +551,3 @@ export interface ValidationWarning {
|
|
|
530
551
|
*/
|
|
531
552
|
code?: string;
|
|
532
553
|
}
|
|
533
|
-
export {};
|
package/CHANGELOG.md
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
## [2.2.0] - 2026-04-07
|
|
2
|
-
|
|
3
|
-
## [2.1.2] - 2025-12-27
|
|
4
|
-
|
|
5
|
-
### Changed
|
|
6
|
-
|
|
7
|
-
- Added `publish-alpha` Nx target to enable alpha publishing flow using common script.
|
|
8
|
-
|
|
9
|
-
## [2.1.1] - 2025-12-24
|
|
10
|
-
|
|
11
|
-
### Fixed
|
|
12
|
-
|
|
13
|
-
- Revised build setup to emit CJS and ESM bundles with corrected export map and sideEffects flag to improve tree shaking.
|
|
14
|
-
- Synced package entry points and type outputs with the new build artifacts while preserving the public API surface.
|
|
15
|
-
|
|
16
|
-
## [2.1.0] - 2025-12-19
|
|
17
|
-
|
|
18
|
-
### Added
|
|
19
|
-
|
|
20
|
-
- Introduced logger support within the OpenAPI adapter to improve observability.
|
|
21
|
-
- Expanded security handling with automatic auth type routing and enhanced documentation.
|
|
22
|
-
|
|
23
|
-
### Changed
|
|
24
|
-
|
|
25
|
-
- Refined security resolver examples and guidance for broader authentication scheme coverage.
|
|
26
|
-
|
|
27
|
-
## [2.0.0] - 2025-12-11
|
|
28
|
-
|
|
29
|
-
### Breaking
|
|
30
|
-
|
|
31
|
-
- Migrated to Zod v4 (now a peer dependency) and aligned JSON schema types with Zod’s v4 JSONSchema
|
|
32
|
-
- Renamed utility export `toJSONSchema7` to `toJsonSchema`, affecting consumers of the types/helpers
|
|
33
|
-
|
|
34
|
-
### Changed
|
|
35
|
-
|
|
36
|
-
- Updated SWC/Jest config to inline ES2022 settings and modernized tooling versions
|
|
37
|
-
- Adjusted exports formatting and minor parameter resolver refactors for consistency
|
|
38
|
-
|
|
39
|
-
# Changelog
|
|
40
|
-
|
|
41
|
-
## [1.0.0] - 2025-11-21
|
|
42
|
-
|
|
43
|
-
### Features
|
|
44
|
-
|
|
45
|
-
- Production-ready library for converting OpenAPI specifications into MCP tool definitions
|
|
46
|
-
- OpenAPI 3.0+ and Swagger 2.0 support
|
|
47
|
-
- Comprehensive operation parsing:
|
|
48
|
-
- RESTful endpoint detection (GET, POST, PUT, PATCH, DELETE, etc.)
|
|
49
|
-
- Path parameter extraction and validation
|
|
50
|
-
- Query parameter handling
|
|
51
|
-
- Request body schema conversion
|
|
52
|
-
- Response schema parsing
|
|
53
|
-
- Advanced OpenAPI features:
|
|
54
|
-
- Reference resolution ($ref) across the entire specification
|
|
55
|
-
- Security scheme detection and configuration
|
|
56
|
-
- Parameter conflict resolution
|
|
57
|
-
- Operation naming controls and customization
|
|
58
|
-
- Request mapper generation:
|
|
59
|
-
- Automatic parameter mapping from MCP tool inputs to HTTP requests
|
|
60
|
-
- Type-safe parameter handling
|
|
61
|
-
- Support for different parameter locations (path, query, header, cookie)
|
|
62
|
-
- Request body transformation
|
|
63
|
-
- Tool metadata generation:
|
|
64
|
-
- Descriptive tool names from operation IDs
|
|
65
|
-
- Documentation from OpenAPI descriptions
|
|
66
|
-
- Schema validation rules
|
|
67
|
-
- Multiple input formats:
|
|
68
|
-
- JSON OpenAPI specifications
|
|
69
|
-
- YAML OpenAPI specifications
|
|
70
|
-
- Inline specification objects
|
|
71
|
-
- URL references to remote specifications
|
|
72
|
-
- Type-safe TypeScript implementation
|
|
73
|
-
- Node.js 18+ compatibility
|
|
74
|
-
|
|
75
|
-
### Documentation
|
|
76
|
-
|
|
77
|
-
- Complete API reference
|
|
78
|
-
- OpenAPI conversion examples
|
|
79
|
-
- Integration guides for MCP servers
|
|
80
|
-
- Best practices for tool generation
|
|
81
|
-
|
|
82
|
-
This is the first official release of `mcp-from-openapi`, extracted from the FrontMCP framework to be published as a
|
|
83
|
-
standalone, reusable library for the community.
|