@frontmcp/adapters 0.6.0 → 0.6.2
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 +1168 -0
- package/esm/openapi/index.mjs +1168 -0
- package/esm/package.json +63 -0
- package/index.js +1193 -0
- package/openapi/index.js +1192 -0
- package/package.json +30 -12
- package/src/index.js +0 -8
- package/src/index.js.map +0 -1
- package/src/openapi/README.md +0 -1215
- package/src/openapi/index.js +0 -8
- package/src/openapi/index.js.map +0 -1
- package/src/openapi/openapi.adapter.js +0 -434
- package/src/openapi/openapi.adapter.js.map +0 -1
- package/src/openapi/openapi.frontmcp-schema.js +0 -358
- package/src/openapi/openapi.frontmcp-schema.js.map +0 -1
- package/src/openapi/openapi.security.js +0 -242
- package/src/openapi/openapi.security.js.map +0 -1
- package/src/openapi/openapi.tool.js +0 -267
- package/src/openapi/openapi.tool.js.map +0 -1
- package/src/openapi/openapi.types.js +0 -29
- package/src/openapi/openapi.types.js.map +0 -1
- package/src/openapi/openapi.utils.js +0 -229
- package/src/openapi/openapi.utils.js.map +0 -1
- /package/{src/index.d.ts → index.d.ts} +0 -0
- /package/{src/openapi → openapi}/index.d.ts +0 -0
- /package/{src/openapi → openapi}/openapi.adapter.d.ts +0 -0
- /package/{src/openapi → openapi}/openapi.frontmcp-schema.d.ts +0 -0
- /package/{src/openapi → openapi}/openapi.security.d.ts +0 -0
- /package/{src/openapi → openapi}/openapi.tool.d.ts +0 -0
- /package/{src/openapi → openapi}/openapi.types.d.ts +0 -0
- /package/{src/openapi → openapi}/openapi.utils.d.ts +0 -0
package/index.js
ADDED
|
@@ -0,0 +1,1193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
20
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
21
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
22
|
+
if (decorator = decorators[i])
|
|
23
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
24
|
+
if (kind && result) __defProp(target, key, result);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// libs/adapters/src/index.ts
|
|
29
|
+
var index_exports = {};
|
|
30
|
+
__export(index_exports, {
|
|
31
|
+
FRONTMCP_EXTENSION_KEY: () => FRONTMCP_EXTENSION_KEY,
|
|
32
|
+
OpenapiAdapter: () => OpenapiAdapter
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
|
|
36
|
+
// libs/adapters/src/openapi/openapi.adapter.ts
|
|
37
|
+
var import_sdk2 = require("@frontmcp/sdk");
|
|
38
|
+
var import_mcp_from_openapi2 = require("mcp-from-openapi");
|
|
39
|
+
|
|
40
|
+
// libs/adapters/src/openapi/openapi.tool.ts
|
|
41
|
+
var import_zod2 = require("zod");
|
|
42
|
+
var import_sdk = require("@frontmcp/sdk");
|
|
43
|
+
var import_zod_from_json_schema = require("zod-from-json-schema");
|
|
44
|
+
|
|
45
|
+
// libs/adapters/src/openapi/openapi.utils.ts
|
|
46
|
+
function coerceToString(value, paramName, location) {
|
|
47
|
+
if (value === null || value === void 0) {
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
if (typeof value === "object") {
|
|
51
|
+
if (Array.isArray(value)) {
|
|
52
|
+
if (location === "query") {
|
|
53
|
+
return value.map(String).join(",");
|
|
54
|
+
}
|
|
55
|
+
throw new Error(`${location} parameter '${paramName}' cannot be an array. Received: ${JSON.stringify(value)}`);
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`${location} parameter '${paramName}' cannot be an object. Received: ${JSON.stringify(value)}`);
|
|
58
|
+
}
|
|
59
|
+
return String(value);
|
|
60
|
+
}
|
|
61
|
+
function validateBaseUrl(url) {
|
|
62
|
+
try {
|
|
63
|
+
const parsed = new URL(url);
|
|
64
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
65
|
+
throw new Error(`Unsupported protocol: ${parsed.protocol}. Only http: and https: are supported.`);
|
|
66
|
+
}
|
|
67
|
+
return parsed;
|
|
68
|
+
} catch (err) {
|
|
69
|
+
if (err instanceof Error && err.message.includes("Unsupported protocol")) {
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
throw new Error(`Invalid base URL: ${url}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function appendCookie(headers, name, value) {
|
|
76
|
+
if (!/^[\w!#$%&'*+\-.^`|~]+$/.test(name)) {
|
|
77
|
+
throw new Error(`Invalid cookie name: '${name}'. Cookie names must be valid tokens.`);
|
|
78
|
+
}
|
|
79
|
+
const existing = headers.get("Cookie") || "";
|
|
80
|
+
const cookiePair = `${name}=${encodeURIComponent(coerceToString(value, name, "cookie"))}`;
|
|
81
|
+
const combined = existing ? `${existing}; ${cookiePair}` : cookiePair;
|
|
82
|
+
headers.set("Cookie", combined);
|
|
83
|
+
}
|
|
84
|
+
function buildRequest(tool2, input, security, baseUrl) {
|
|
85
|
+
const rawBaseUrl = tool2.metadata.servers?.[0]?.url || baseUrl;
|
|
86
|
+
validateBaseUrl(rawBaseUrl);
|
|
87
|
+
const apiBaseUrl = rawBaseUrl.replace(/\/+$/, "");
|
|
88
|
+
let path = tool2.metadata.path;
|
|
89
|
+
const queryParams = new URLSearchParams();
|
|
90
|
+
const headers = new Headers({
|
|
91
|
+
accept: "application/json",
|
|
92
|
+
...security.headers
|
|
93
|
+
});
|
|
94
|
+
let body;
|
|
95
|
+
for (const mapper of tool2.mapper) {
|
|
96
|
+
if (mapper.security) continue;
|
|
97
|
+
const value = input[mapper.inputKey];
|
|
98
|
+
if (value === void 0 || value === null) {
|
|
99
|
+
if (mapper.required) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Required ${mapper.type} parameter '${mapper.key}' (input: '${mapper.inputKey}') is missing for operation '${tool2.name}'`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
switch (mapper.type) {
|
|
107
|
+
case "path":
|
|
108
|
+
path = path.replaceAll(`{${mapper.key}}`, encodeURIComponent(coerceToString(value, mapper.key, "path")));
|
|
109
|
+
break;
|
|
110
|
+
case "query":
|
|
111
|
+
queryParams.set(mapper.key, coerceToString(value, mapper.key, "query"));
|
|
112
|
+
break;
|
|
113
|
+
case "header": {
|
|
114
|
+
const headerValue = coerceToString(value, mapper.key, "header");
|
|
115
|
+
if (/[\r\n\x00\f\v]/.test(headerValue)) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Invalid header value for '${mapper.key}': contains control characters (possible header injection attack)`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
headers.set(mapper.key, headerValue);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case "cookie":
|
|
124
|
+
appendCookie(headers, mapper.key, value);
|
|
125
|
+
break;
|
|
126
|
+
case "body":
|
|
127
|
+
if (!body) body = {};
|
|
128
|
+
body[mapper.key] = value;
|
|
129
|
+
break;
|
|
130
|
+
default:
|
|
131
|
+
throw new Error(
|
|
132
|
+
`Unknown mapper type '${mapper.type}' for parameter '${mapper.key}' in operation '${tool2.name}'`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
Object.entries(security.query).forEach(([key, value]) => {
|
|
137
|
+
if (queryParams.has(key)) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Query parameter collision: '${key}' is provided both as user input and security parameter. This could indicate a security misconfiguration in operation '${tool2.name}'.`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
queryParams.set(key, coerceToString(value, key, "security query"));
|
|
143
|
+
});
|
|
144
|
+
if (security.cookies && Object.keys(security.cookies).length > 0) {
|
|
145
|
+
Object.entries(security.cookies).forEach(([key, value]) => {
|
|
146
|
+
appendCookie(headers, key, value);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (path.includes("{")) {
|
|
150
|
+
throw new Error(`Failed to resolve all path parameters in ${path} for operation ${tool2.name}`);
|
|
151
|
+
}
|
|
152
|
+
const queryString = queryParams.toString();
|
|
153
|
+
const url = `${apiBaseUrl}${path}${queryString ? `?${queryString}` : ""}`;
|
|
154
|
+
return { url, headers, body };
|
|
155
|
+
}
|
|
156
|
+
function applyAdditionalHeaders(headers, additionalHeaders) {
|
|
157
|
+
if (!additionalHeaders) return;
|
|
158
|
+
Object.entries(additionalHeaders).forEach(([key, value]) => {
|
|
159
|
+
headers.set(key, value);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
var DEFAULT_MAX_RESPONSE_SIZE = 10 * 1024 * 1024;
|
|
163
|
+
async function parseResponse(response, options) {
|
|
164
|
+
const maxSize = options?.maxResponseSize ?? DEFAULT_MAX_RESPONSE_SIZE;
|
|
165
|
+
if (!response.ok) {
|
|
166
|
+
throw new Error(`API request failed: ${response.status}`);
|
|
167
|
+
}
|
|
168
|
+
const contentLength = response.headers.get("content-length");
|
|
169
|
+
if (contentLength) {
|
|
170
|
+
const length = parseInt(contentLength, 10);
|
|
171
|
+
if (!isNaN(length) && isFinite(length) && length > maxSize) {
|
|
172
|
+
throw new Error(`Response size (${length} bytes) exceeds maximum allowed (${maxSize} bytes)`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const text = await response.text();
|
|
176
|
+
const byteSize = new TextEncoder().encode(text).length;
|
|
177
|
+
if (byteSize > maxSize) {
|
|
178
|
+
throw new Error(`Response size (${byteSize} bytes) exceeds maximum allowed (${maxSize} bytes)`);
|
|
179
|
+
}
|
|
180
|
+
const contentType = response.headers.get("content-type");
|
|
181
|
+
if (contentType?.toLowerCase().includes("application/json")) {
|
|
182
|
+
try {
|
|
183
|
+
return { data: JSON.parse(text) };
|
|
184
|
+
} catch {
|
|
185
|
+
return { data: text };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return { data: text };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// libs/adapters/src/openapi/openapi.security.ts
|
|
192
|
+
var import_mcp_from_openapi = require("mcp-from-openapi");
|
|
193
|
+
async function createSecurityContextFromAuth(tool2, ctx, options) {
|
|
194
|
+
if (options.securityResolver) {
|
|
195
|
+
return await options.securityResolver(tool2, ctx);
|
|
196
|
+
}
|
|
197
|
+
if (options.authProviderMapper) {
|
|
198
|
+
const context = (0, import_mcp_from_openapi.createSecurityContext)({});
|
|
199
|
+
const securitySchemes = /* @__PURE__ */ new Set();
|
|
200
|
+
for (const mapper of tool2.mapper) {
|
|
201
|
+
if (mapper.security?.scheme) {
|
|
202
|
+
securitySchemes.add(mapper.security.scheme);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
for (const scheme of securitySchemes) {
|
|
206
|
+
const authExtractor = options.authProviderMapper[scheme];
|
|
207
|
+
if (authExtractor) {
|
|
208
|
+
try {
|
|
209
|
+
const token = authExtractor(ctx);
|
|
210
|
+
if (token !== void 0 && token !== null && typeof token !== "string") {
|
|
211
|
+
throw new Error(
|
|
212
|
+
`authProviderMapper['${scheme}'] must return a string or undefined, but returned: ${typeof token}`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if (token === "") {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`authProviderMapper['${scheme}'] returned empty string. Return undefined/null if no token is available, or provide a valid token.`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
if (token) {
|
|
221
|
+
const schemeMapper = tool2.mapper.find((m) => m.security?.scheme === scheme);
|
|
222
|
+
const schemeType = schemeMapper?.security?.type?.toLowerCase();
|
|
223
|
+
const httpScheme = schemeMapper?.security?.httpScheme?.toLowerCase();
|
|
224
|
+
if (schemeType === "apikey") {
|
|
225
|
+
if (!context.apiKey) {
|
|
226
|
+
context.apiKey = token;
|
|
227
|
+
}
|
|
228
|
+
} else if (schemeType === "http" && httpScheme === "basic") {
|
|
229
|
+
if (!context.basic) {
|
|
230
|
+
context.basic = token;
|
|
231
|
+
}
|
|
232
|
+
} else if (schemeType === "oauth2") {
|
|
233
|
+
if (!context.oauth2Token) {
|
|
234
|
+
context.oauth2Token = token;
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
if (!context.jwt) {
|
|
238
|
+
context.jwt = token;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} catch (err) {
|
|
243
|
+
if (err instanceof Error && err.message.includes("authProviderMapper")) {
|
|
244
|
+
throw err;
|
|
245
|
+
}
|
|
246
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
247
|
+
throw new Error(`authProviderMapper['${scheme}'] threw an error: ${errorMessage}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const hasAnyAuth = context.jwt || context.apiKey || context.basic || context.oauth2Token;
|
|
252
|
+
const authToken = ctx.authInfo?.token;
|
|
253
|
+
if (!hasAnyAuth && authToken) {
|
|
254
|
+
if (typeof authToken !== "string") {
|
|
255
|
+
throw new Error(`authInfo.token must be a string, but got: ${typeof authToken}`);
|
|
256
|
+
}
|
|
257
|
+
context.jwt = authToken;
|
|
258
|
+
}
|
|
259
|
+
return context;
|
|
260
|
+
}
|
|
261
|
+
if (options.staticAuth) {
|
|
262
|
+
return (0, import_mcp_from_openapi.createSecurityContext)(options.staticAuth);
|
|
263
|
+
}
|
|
264
|
+
return (0, import_mcp_from_openapi.createSecurityContext)({
|
|
265
|
+
jwt: ctx.authInfo?.token
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
function extractSecuritySchemes(tools) {
|
|
269
|
+
const schemes = /* @__PURE__ */ new Set();
|
|
270
|
+
for (const tool2 of tools) {
|
|
271
|
+
for (const mapper of tool2.mapper) {
|
|
272
|
+
if (mapper.security?.scheme) {
|
|
273
|
+
schemes.add(mapper.security.scheme);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return schemes;
|
|
278
|
+
}
|
|
279
|
+
function validateSecurityConfiguration(tools, options) {
|
|
280
|
+
const result = {
|
|
281
|
+
valid: true,
|
|
282
|
+
missingMappings: [],
|
|
283
|
+
warnings: [],
|
|
284
|
+
securityRiskScore: "low"
|
|
285
|
+
};
|
|
286
|
+
const securitySchemes = extractSecuritySchemes(tools);
|
|
287
|
+
const includeSecurityInInput = options.generateOptions?.includeSecurityInInput ?? false;
|
|
288
|
+
if (includeSecurityInInput) {
|
|
289
|
+
result.securityRiskScore = "high";
|
|
290
|
+
result.warnings.push(
|
|
291
|
+
"SECURITY WARNING: includeSecurityInInput is enabled. Users will provide authentication directly in tool inputs. This increases security risk as credentials may be logged or exposed."
|
|
292
|
+
);
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
if (options.securityResolver) {
|
|
296
|
+
result.securityRiskScore = "low";
|
|
297
|
+
result.warnings.push(
|
|
298
|
+
"INFO: Using custom securityResolver. Ensure your resolver properly validates and secures credentials from context."
|
|
299
|
+
);
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
if (options.staticAuth && Object.keys(options.staticAuth).length > 0) {
|
|
303
|
+
result.securityRiskScore = "medium";
|
|
304
|
+
result.warnings.push(
|
|
305
|
+
"SECURITY INFO: Using staticAuth with hardcoded credentials. Ensure credentials are stored securely (environment variables, secrets manager)."
|
|
306
|
+
);
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
const schemesInInput = new Set(options.securitySchemesInInput || []);
|
|
310
|
+
if (options.authProviderMapper || schemesInInput.size > 0) {
|
|
311
|
+
result.securityRiskScore = schemesInInput.size > 0 ? "medium" : "low";
|
|
312
|
+
if (schemesInInput.size > 0) {
|
|
313
|
+
result.warnings.push(
|
|
314
|
+
`INFO: Per-scheme security control enabled. Schemes in input: ${Array.from(schemesInInput).join(", ")}`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
for (const scheme of securitySchemes) {
|
|
318
|
+
if (schemesInInput.has(scheme)) {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (!options.authProviderMapper?.[scheme]) {
|
|
322
|
+
result.valid = false;
|
|
323
|
+
result.missingMappings.push(scheme);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (!result.valid) {
|
|
327
|
+
result.warnings.push(
|
|
328
|
+
`ERROR: Missing auth provider mappings for security schemes: ${result.missingMappings.join(", ")}`
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
return result;
|
|
332
|
+
}
|
|
333
|
+
if (securitySchemes.size > 0) {
|
|
334
|
+
result.securityRiskScore = "medium";
|
|
335
|
+
result.warnings.push(
|
|
336
|
+
`INFO: No auth configuration provided. Using default ctx.authInfo.token for all security schemes: ${Array.from(
|
|
337
|
+
securitySchemes
|
|
338
|
+
).join(", ")}`
|
|
339
|
+
);
|
|
340
|
+
result.warnings.push(
|
|
341
|
+
"RECOMMENDATION: For multiple auth providers, use authProviderMapper or securityResolver to map each security scheme to the correct auth provider."
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
return result;
|
|
345
|
+
}
|
|
346
|
+
async function resolveToolSecurity(tool2, ctx, options) {
|
|
347
|
+
const securityResolver = new import_mcp_from_openapi.SecurityResolver();
|
|
348
|
+
const securityContext = await createSecurityContextFromAuth(tool2, ctx, options);
|
|
349
|
+
const hasAuth = securityContext.jwt || securityContext.apiKey || securityContext.basic || securityContext.oauth2Token || securityContext.apiKeys && Object.keys(securityContext.apiKeys).length > 0 || securityContext.customHeaders && Object.keys(securityContext.customHeaders).length > 0;
|
|
350
|
+
const requiresSecurity = tool2.mapper.some((m) => m.security && m.required === true);
|
|
351
|
+
if (requiresSecurity && !hasAuth) {
|
|
352
|
+
const requiredSchemes = tool2.mapper.filter((m) => m.security && m.required === true).map((m) => m.security?.scheme ?? "unknown");
|
|
353
|
+
const uniqueSchemes = [...new Set(requiredSchemes)];
|
|
354
|
+
const schemesStr = uniqueSchemes.join(", ") || "unknown";
|
|
355
|
+
const firstScheme = uniqueSchemes[0] || "BearerAuth";
|
|
356
|
+
throw new Error(
|
|
357
|
+
`Authentication required for tool '${tool2.name}' but no auth configuration found.
|
|
358
|
+
Required security schemes: ${schemesStr}
|
|
359
|
+
Solutions:
|
|
360
|
+
1. Add authProviderMapper: { '${firstScheme}': (ctx) => ctx.authInfo.user?.token }
|
|
361
|
+
2. Add securityResolver: (tool, ctx) => ({ jwt: ctx.authInfo.token })
|
|
362
|
+
3. Add staticAuth: { jwt: process.env.API_TOKEN }
|
|
363
|
+
4. Set generateOptions.includeSecurityInInput: true (not recommended for production)`
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
return await securityResolver.resolve(tool2.mapper, securityContext);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// libs/adapters/src/openapi/openapi.frontmcp-schema.ts
|
|
370
|
+
var import_zod = require("zod");
|
|
371
|
+
var FRONTMCP_SCHEMA_VERSION = "1.0";
|
|
372
|
+
var SUPPORTED_VERSIONS = ["1.0"];
|
|
373
|
+
var FrontMcpAnnotationsSchema = import_zod.z.object({
|
|
374
|
+
/** Human-readable title for the tool */
|
|
375
|
+
title: import_zod.z.string().optional(),
|
|
376
|
+
/** If true, the tool does not modify its environment */
|
|
377
|
+
readOnlyHint: import_zod.z.boolean().optional(),
|
|
378
|
+
/** If true, the tool may perform destructive updates */
|
|
379
|
+
destructiveHint: import_zod.z.boolean().optional(),
|
|
380
|
+
/** If true, calling repeatedly with same args has no additional effect */
|
|
381
|
+
idempotentHint: import_zod.z.boolean().optional(),
|
|
382
|
+
/** If true, tool may interact with external entities */
|
|
383
|
+
openWorldHint: import_zod.z.boolean().optional()
|
|
384
|
+
});
|
|
385
|
+
var FrontMcpCacheSchema = import_zod.z.object({
|
|
386
|
+
/** Time-to-live in seconds for cached responses */
|
|
387
|
+
ttl: import_zod.z.number().int().positive().optional(),
|
|
388
|
+
/** If true, cache window slides on each access */
|
|
389
|
+
slideWindow: import_zod.z.boolean().optional()
|
|
390
|
+
});
|
|
391
|
+
var FrontMcpCodeCallSchema = import_zod.z.object({
|
|
392
|
+
/** Whether this tool can be used via CodeCall */
|
|
393
|
+
enabledInCodeCall: import_zod.z.boolean().optional(),
|
|
394
|
+
/** If true, stays visible in list_tools when CodeCall is active */
|
|
395
|
+
visibleInListTools: import_zod.z.boolean().optional()
|
|
396
|
+
});
|
|
397
|
+
var FrontMcpExampleSchema = import_zod.z.object({
|
|
398
|
+
/** Description of the example */
|
|
399
|
+
description: import_zod.z.string(),
|
|
400
|
+
/** Example input values */
|
|
401
|
+
input: import_zod.z.record(import_zod.z.string(), import_zod.z.any()),
|
|
402
|
+
/** Expected output (optional) */
|
|
403
|
+
output: import_zod.z.any().optional()
|
|
404
|
+
});
|
|
405
|
+
var KNOWN_EXTENSION_FIELDS = /* @__PURE__ */ new Set([
|
|
406
|
+
"version",
|
|
407
|
+
"annotations",
|
|
408
|
+
"cache",
|
|
409
|
+
"codecall",
|
|
410
|
+
"tags",
|
|
411
|
+
"hideFromDiscovery",
|
|
412
|
+
"examples"
|
|
413
|
+
]);
|
|
414
|
+
var KNOWN_ANNOTATION_FIELDS = /* @__PURE__ */ new Set([
|
|
415
|
+
"title",
|
|
416
|
+
"readOnlyHint",
|
|
417
|
+
"destructiveHint",
|
|
418
|
+
"idempotentHint",
|
|
419
|
+
"openWorldHint"
|
|
420
|
+
]);
|
|
421
|
+
var KNOWN_CACHE_FIELDS = /* @__PURE__ */ new Set(["ttl", "slideWindow"]);
|
|
422
|
+
var KNOWN_CODECALL_FIELDS = /* @__PURE__ */ new Set(["enabledInCodeCall", "visibleInListTools"]);
|
|
423
|
+
function validateFrontMcpExtension(rawData, toolName, logger) {
|
|
424
|
+
const warnings = [];
|
|
425
|
+
if (rawData === null || rawData === void 0) {
|
|
426
|
+
return { success: true, data: null, warnings: [] };
|
|
427
|
+
}
|
|
428
|
+
if (typeof rawData !== "object" || Array.isArray(rawData)) {
|
|
429
|
+
warnings.push(`x-frontmcp must be an object, got ${Array.isArray(rawData) ? "array" : typeof rawData}`);
|
|
430
|
+
logger.warn(`[${toolName}] Invalid x-frontmcp extension: ${warnings[0]}`);
|
|
431
|
+
return { success: false, data: null, warnings };
|
|
432
|
+
}
|
|
433
|
+
const data = rawData;
|
|
434
|
+
const version = typeof data["version"] === "string" ? data["version"] : FRONTMCP_SCHEMA_VERSION;
|
|
435
|
+
if (!SUPPORTED_VERSIONS.includes(version)) {
|
|
436
|
+
warnings.push(`Unsupported x-frontmcp version '${version}'. Supported versions: ${SUPPORTED_VERSIONS.join(", ")}`);
|
|
437
|
+
logger.warn(`[${toolName}] ${warnings[0]}`);
|
|
438
|
+
return { success: false, data: null, warnings };
|
|
439
|
+
}
|
|
440
|
+
const validData = extractValidFields(data, version, warnings);
|
|
441
|
+
if (warnings.length > 0) {
|
|
442
|
+
logger.warn(`[${toolName}] x-frontmcp extension has invalid fields that were ignored:`);
|
|
443
|
+
warnings.forEach((w) => logger.warn(` - ${w}`));
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
success: true,
|
|
447
|
+
data: validData,
|
|
448
|
+
warnings
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function extractValidFields(data, version, warnings) {
|
|
452
|
+
const result = {
|
|
453
|
+
version
|
|
454
|
+
};
|
|
455
|
+
for (const key of Object.keys(data)) {
|
|
456
|
+
if (!KNOWN_EXTENSION_FIELDS.has(key)) {
|
|
457
|
+
warnings.push(`Unknown field '${key}' in x-frontmcp (will be ignored)`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (data["annotations"] !== void 0) {
|
|
461
|
+
const annotationsResult = FrontMcpAnnotationsSchema.safeParse(data["annotations"]);
|
|
462
|
+
if (annotationsResult.success) {
|
|
463
|
+
result.annotations = annotationsResult.data;
|
|
464
|
+
} else {
|
|
465
|
+
const issues = formatZodIssues(annotationsResult.error.issues, "annotations");
|
|
466
|
+
warnings.push(...issues);
|
|
467
|
+
result.annotations = extractValidAnnotations(data["annotations"], warnings);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (data["cache"] !== void 0) {
|
|
471
|
+
const cacheResult = FrontMcpCacheSchema.safeParse(data["cache"]);
|
|
472
|
+
if (cacheResult.success) {
|
|
473
|
+
result.cache = cacheResult.data;
|
|
474
|
+
} else {
|
|
475
|
+
const issues = formatZodIssues(cacheResult.error.issues, "cache");
|
|
476
|
+
warnings.push(...issues);
|
|
477
|
+
result.cache = extractValidCache(data["cache"], warnings);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
if (data["codecall"] !== void 0) {
|
|
481
|
+
const codecallResult = FrontMcpCodeCallSchema.safeParse(data["codecall"]);
|
|
482
|
+
if (codecallResult.success) {
|
|
483
|
+
result.codecall = codecallResult.data;
|
|
484
|
+
} else {
|
|
485
|
+
const issues = formatZodIssues(codecallResult.error.issues, "codecall");
|
|
486
|
+
warnings.push(...issues);
|
|
487
|
+
result.codecall = extractValidCodeCall(data["codecall"], warnings);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (data["tags"] !== void 0) {
|
|
491
|
+
const tagsSchema = import_zod.z.array(import_zod.z.string());
|
|
492
|
+
const tagsResult = tagsSchema.safeParse(data["tags"]);
|
|
493
|
+
if (tagsResult.success) {
|
|
494
|
+
result.tags = tagsResult.data;
|
|
495
|
+
} else {
|
|
496
|
+
warnings.push(`Invalid 'tags': expected array of strings`);
|
|
497
|
+
result.tags = extractValidTags(data["tags"]);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (data["hideFromDiscovery"] !== void 0) {
|
|
501
|
+
if (typeof data["hideFromDiscovery"] === "boolean") {
|
|
502
|
+
result.hideFromDiscovery = data["hideFromDiscovery"];
|
|
503
|
+
} else {
|
|
504
|
+
warnings.push(`Invalid 'hideFromDiscovery': expected boolean, got ${typeof data["hideFromDiscovery"]}`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
if (data["examples"] !== void 0) {
|
|
508
|
+
const examplesSchema = import_zod.z.array(FrontMcpExampleSchema);
|
|
509
|
+
const examplesResult = examplesSchema.safeParse(data["examples"]);
|
|
510
|
+
if (examplesResult.success) {
|
|
511
|
+
result.examples = examplesResult.data;
|
|
512
|
+
} else {
|
|
513
|
+
warnings.push(`Invalid 'examples': some examples have invalid format`);
|
|
514
|
+
result.examples = extractValidExamples(data["examples"], warnings);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return result;
|
|
518
|
+
}
|
|
519
|
+
function extractValidAnnotations(data, warnings) {
|
|
520
|
+
if (typeof data !== "object" || data === null) return void 0;
|
|
521
|
+
const obj = data;
|
|
522
|
+
const result = {};
|
|
523
|
+
for (const field of KNOWN_ANNOTATION_FIELDS) {
|
|
524
|
+
if (obj[field] !== void 0) {
|
|
525
|
+
if (field === "title" && typeof obj[field] === "string") {
|
|
526
|
+
result.title = obj[field];
|
|
527
|
+
} else if (field !== "title" && typeof obj[field] === "boolean") {
|
|
528
|
+
result[field] = obj[field];
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
for (const key of Object.keys(obj)) {
|
|
533
|
+
if (!KNOWN_ANNOTATION_FIELDS.has(key)) {
|
|
534
|
+
warnings.push(`Unknown field 'annotations.${key}' (will be ignored)`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
538
|
+
}
|
|
539
|
+
function extractValidCache(data, warnings) {
|
|
540
|
+
if (typeof data !== "object" || data === null) return void 0;
|
|
541
|
+
const obj = data;
|
|
542
|
+
const result = {};
|
|
543
|
+
if (typeof obj["ttl"] === "number" && Number.isInteger(obj["ttl"]) && obj["ttl"] > 0) {
|
|
544
|
+
result.ttl = obj["ttl"];
|
|
545
|
+
} else if (obj["ttl"] !== void 0) {
|
|
546
|
+
warnings.push(`Invalid 'cache.ttl': expected positive integer`);
|
|
547
|
+
}
|
|
548
|
+
if (typeof obj["slideWindow"] === "boolean") {
|
|
549
|
+
result.slideWindow = obj["slideWindow"];
|
|
550
|
+
} else if (obj["slideWindow"] !== void 0) {
|
|
551
|
+
warnings.push(`Invalid 'cache.slideWindow': expected boolean`);
|
|
552
|
+
}
|
|
553
|
+
for (const key of Object.keys(obj)) {
|
|
554
|
+
if (!KNOWN_CACHE_FIELDS.has(key)) {
|
|
555
|
+
warnings.push(`Unknown field 'cache.${key}' (will be ignored)`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
559
|
+
}
|
|
560
|
+
function extractValidCodeCall(data, warnings) {
|
|
561
|
+
if (typeof data !== "object" || data === null) return void 0;
|
|
562
|
+
const obj = data;
|
|
563
|
+
const result = {};
|
|
564
|
+
if (typeof obj["enabledInCodeCall"] === "boolean") {
|
|
565
|
+
result.enabledInCodeCall = obj["enabledInCodeCall"];
|
|
566
|
+
} else if (obj["enabledInCodeCall"] !== void 0) {
|
|
567
|
+
warnings.push(`Invalid 'codecall.enabledInCodeCall': expected boolean`);
|
|
568
|
+
}
|
|
569
|
+
if (typeof obj["visibleInListTools"] === "boolean") {
|
|
570
|
+
result.visibleInListTools = obj["visibleInListTools"];
|
|
571
|
+
} else if (obj["visibleInListTools"] !== void 0) {
|
|
572
|
+
warnings.push(`Invalid 'codecall.visibleInListTools': expected boolean`);
|
|
573
|
+
}
|
|
574
|
+
for (const key of Object.keys(obj)) {
|
|
575
|
+
if (!KNOWN_CODECALL_FIELDS.has(key)) {
|
|
576
|
+
warnings.push(`Unknown field 'codecall.${key}' (will be ignored)`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
580
|
+
}
|
|
581
|
+
function extractValidTags(data) {
|
|
582
|
+
if (!Array.isArray(data)) return void 0;
|
|
583
|
+
const validTags = data.filter((item) => typeof item === "string");
|
|
584
|
+
return validTags.length > 0 ? validTags : void 0;
|
|
585
|
+
}
|
|
586
|
+
function extractValidExamples(data, warnings) {
|
|
587
|
+
if (!Array.isArray(data)) return void 0;
|
|
588
|
+
const validExamples = [];
|
|
589
|
+
for (let i = 0; i < data.length; i++) {
|
|
590
|
+
const item = data[i];
|
|
591
|
+
const result = FrontMcpExampleSchema.safeParse(item);
|
|
592
|
+
if (result.success) {
|
|
593
|
+
validExamples.push(result.data);
|
|
594
|
+
} else {
|
|
595
|
+
warnings.push(
|
|
596
|
+
`Invalid example at index ${i}: ${formatZodIssues(result.error.issues, `examples[${i}]`).join(", ")}`
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return validExamples.length > 0 ? validExamples : void 0;
|
|
601
|
+
}
|
|
602
|
+
function formatZodIssues(issues, prefix) {
|
|
603
|
+
return issues.map((issue) => {
|
|
604
|
+
const path = issue.path.length > 0 ? `${prefix}.${issue.path.join(".")}` : prefix;
|
|
605
|
+
return `Invalid '${path}': ${issue.message}`;
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// libs/adapters/src/openapi/openapi.tool.ts
|
|
610
|
+
function createOpenApiTool(openapiTool, options, logger) {
|
|
611
|
+
const metadata = openapiTool.metadata;
|
|
612
|
+
const inputTransforms = metadata.adapter?.inputTransforms ?? [];
|
|
613
|
+
const toolTransform = metadata.adapter?.toolTransform ?? {};
|
|
614
|
+
const frontmcpValidation = validateFrontMcpExtension(metadata.frontmcp, openapiTool.name, logger);
|
|
615
|
+
const frontmcpExt = frontmcpValidation.data;
|
|
616
|
+
const schemaResult = getZodSchemaFromJsonSchema(openapiTool.inputSchema, openapiTool.name, logger);
|
|
617
|
+
const toolMetadata = {
|
|
618
|
+
id: openapiTool.name,
|
|
619
|
+
name: openapiTool.name,
|
|
620
|
+
description: openapiTool.description,
|
|
621
|
+
inputSchema: schemaResult.schema.shape || {},
|
|
622
|
+
rawInputSchema: openapiTool.inputSchema
|
|
623
|
+
};
|
|
624
|
+
if (schemaResult.conversionFailed) {
|
|
625
|
+
toolMetadata["_schemaConversionFailed"] = true;
|
|
626
|
+
toolMetadata["_schemaConversionError"] = schemaResult.error;
|
|
627
|
+
}
|
|
628
|
+
if (frontmcpExt) {
|
|
629
|
+
if (frontmcpExt.annotations) {
|
|
630
|
+
toolMetadata["annotations"] = { ...frontmcpExt.annotations };
|
|
631
|
+
}
|
|
632
|
+
if (frontmcpExt.tags) {
|
|
633
|
+
toolMetadata["tags"] = [...frontmcpExt.tags];
|
|
634
|
+
}
|
|
635
|
+
if (frontmcpExt.hideFromDiscovery !== void 0) {
|
|
636
|
+
toolMetadata["hideFromDiscovery"] = frontmcpExt.hideFromDiscovery;
|
|
637
|
+
}
|
|
638
|
+
if (frontmcpExt.examples) {
|
|
639
|
+
toolMetadata["examples"] = [...frontmcpExt.examples];
|
|
640
|
+
}
|
|
641
|
+
if (frontmcpExt.cache) {
|
|
642
|
+
toolMetadata["cache"] = { ...frontmcpExt.cache };
|
|
643
|
+
}
|
|
644
|
+
if (frontmcpExt.codecall) {
|
|
645
|
+
toolMetadata["codecall"] = { ...frontmcpExt.codecall };
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (toolTransform.annotations) {
|
|
649
|
+
toolMetadata["annotations"] = {
|
|
650
|
+
...toolMetadata["annotations"] || {},
|
|
651
|
+
...toolTransform.annotations
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
if (toolTransform.tags) {
|
|
655
|
+
const existingTags = toolMetadata["tags"] || [];
|
|
656
|
+
toolMetadata["tags"] = [...existingTags, ...toolTransform.tags];
|
|
657
|
+
}
|
|
658
|
+
if (toolTransform.hideFromDiscovery !== void 0) {
|
|
659
|
+
toolMetadata["hideFromDiscovery"] = toolTransform.hideFromDiscovery;
|
|
660
|
+
}
|
|
661
|
+
if (toolTransform.examples) {
|
|
662
|
+
const existingExamples = toolMetadata["examples"] || [];
|
|
663
|
+
toolMetadata["examples"] = [...existingExamples, ...toolTransform.examples];
|
|
664
|
+
}
|
|
665
|
+
if (toolTransform.ui) {
|
|
666
|
+
toolMetadata["ui"] = toolTransform.ui;
|
|
667
|
+
}
|
|
668
|
+
return (0, import_sdk.tool)(toolMetadata)(async (input, toolCtx) => {
|
|
669
|
+
const ctx = toolCtx.context;
|
|
670
|
+
const transformContext = {
|
|
671
|
+
ctx,
|
|
672
|
+
env: process.env,
|
|
673
|
+
tool: openapiTool
|
|
674
|
+
};
|
|
675
|
+
const injectedInput = await injectTransformedValues(
|
|
676
|
+
input,
|
|
677
|
+
inputTransforms,
|
|
678
|
+
transformContext
|
|
679
|
+
);
|
|
680
|
+
const security = await resolveToolSecurity(openapiTool, ctx, options);
|
|
681
|
+
const { url, headers, body: requestBody } = buildRequest(openapiTool, injectedInput, security, options.baseUrl);
|
|
682
|
+
applyAdditionalHeaders(headers, options.additionalHeaders);
|
|
683
|
+
if (options.headersMapper) {
|
|
684
|
+
try {
|
|
685
|
+
const mappedHeaders = options.headersMapper(ctx, headers);
|
|
686
|
+
if (mappedHeaders && typeof mappedHeaders.forEach === "function") {
|
|
687
|
+
mappedHeaders.forEach((value, key) => {
|
|
688
|
+
headers.set(key, value);
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
} catch (err) {
|
|
692
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
693
|
+
throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
let finalBody = requestBody;
|
|
697
|
+
if (options.bodyMapper && requestBody) {
|
|
698
|
+
try {
|
|
699
|
+
finalBody = options.bodyMapper(ctx, requestBody);
|
|
700
|
+
} catch (err) {
|
|
701
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
702
|
+
throw new Error(`bodyMapper failed for tool '${openapiTool.name}': ${errorMessage}`);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (finalBody && !headers.has("content-type")) {
|
|
706
|
+
headers.set("content-type", "application/json");
|
|
707
|
+
}
|
|
708
|
+
let serializedBody;
|
|
709
|
+
if (finalBody) {
|
|
710
|
+
try {
|
|
711
|
+
serializedBody = JSON.stringify(finalBody);
|
|
712
|
+
} catch (err) {
|
|
713
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
714
|
+
throw new Error(
|
|
715
|
+
`Failed to serialize request body for tool '${openapiTool.name}': ${errorMessage}. Body may contain circular references, BigInt, or non-serializable values.`
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
const maxRequestSize = options.maxRequestSize ?? DEFAULT_MAX_REQUEST_SIZE;
|
|
719
|
+
if (serializedBody.length > maxRequestSize) {
|
|
720
|
+
throw new Error(
|
|
721
|
+
`Request body size (${serializedBody.length} bytes) exceeds maximum allowed (${maxRequestSize} bytes)`
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const requestTimeout = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
726
|
+
const controller = new AbortController();
|
|
727
|
+
const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
|
|
728
|
+
try {
|
|
729
|
+
const response = await fetch(url, {
|
|
730
|
+
method: openapiTool.metadata.method.toUpperCase(),
|
|
731
|
+
headers,
|
|
732
|
+
body: serializedBody,
|
|
733
|
+
signal: controller.signal
|
|
734
|
+
});
|
|
735
|
+
return await parseResponse(response, { maxResponseSize: options.maxResponseSize });
|
|
736
|
+
} catch (err) {
|
|
737
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
738
|
+
throw new Error(`Request timeout after ${requestTimeout}ms for tool '${openapiTool.name}'`);
|
|
739
|
+
}
|
|
740
|
+
throw err;
|
|
741
|
+
} finally {
|
|
742
|
+
clearTimeout(timeoutId);
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
var DEFAULT_TRANSFORM_TIMEOUT_MS = 5e3;
|
|
747
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
748
|
+
var DEFAULT_MAX_REQUEST_SIZE = 10 * 1024 * 1024;
|
|
749
|
+
var RESERVED_KEYS = ["__proto__", "constructor", "prototype"];
|
|
750
|
+
async function safeInject(transform, ctx, timeoutMs = DEFAULT_TRANSFORM_TIMEOUT_MS) {
|
|
751
|
+
let timeoutId;
|
|
752
|
+
try {
|
|
753
|
+
const result = await Promise.race([
|
|
754
|
+
Promise.resolve(transform.inject(ctx)),
|
|
755
|
+
new Promise((_, reject) => {
|
|
756
|
+
timeoutId = setTimeout(() => reject(new Error(`Transform timeout after ${timeoutMs}ms`)), timeoutMs);
|
|
757
|
+
})
|
|
758
|
+
]);
|
|
759
|
+
return result;
|
|
760
|
+
} catch (err) {
|
|
761
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
762
|
+
throw new Error(`Input transform for '${transform.inputKey}' failed: ${errorMessage}`);
|
|
763
|
+
} finally {
|
|
764
|
+
if (timeoutId !== void 0) {
|
|
765
|
+
clearTimeout(timeoutId);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
function isPlainObject(value) {
|
|
770
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
771
|
+
}
|
|
772
|
+
async function injectTransformedValues(input, transforms, ctx) {
|
|
773
|
+
if (!isPlainObject(input)) {
|
|
774
|
+
throw new Error(`Invalid input type: expected object, got ${input === null ? "null" : typeof input}`);
|
|
775
|
+
}
|
|
776
|
+
if (transforms.length === 0) return input;
|
|
777
|
+
const result = { ...input };
|
|
778
|
+
for (const transform of transforms) {
|
|
779
|
+
if (RESERVED_KEYS.includes(transform.inputKey)) {
|
|
780
|
+
throw new Error(
|
|
781
|
+
`Invalid inputKey '${transform.inputKey}': reserved keys (${RESERVED_KEYS.join(", ")}) cannot be used`
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
const value = await safeInject(transform, ctx);
|
|
785
|
+
if (value !== void 0) {
|
|
786
|
+
result[transform.inputKey] = value;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return result;
|
|
790
|
+
}
|
|
791
|
+
function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
|
|
792
|
+
if (typeof jsonSchema !== "object" || jsonSchema === null) {
|
|
793
|
+
logger.warn(`[${toolName}] No valid JSON schema provided, using permissive schema`);
|
|
794
|
+
return { schema: import_zod2.z.looseObject({}), conversionFailed: true, error: "No valid JSON schema" };
|
|
795
|
+
}
|
|
796
|
+
try {
|
|
797
|
+
const zodSchema = (0, import_zod_from_json_schema.convertJsonSchemaToZod)(jsonSchema);
|
|
798
|
+
if (typeof zodSchema?.parse !== "function") {
|
|
799
|
+
throw new Error("Conversion did not produce a valid Zod schema.");
|
|
800
|
+
}
|
|
801
|
+
return { schema: zodSchema, conversionFailed: false };
|
|
802
|
+
} catch (err) {
|
|
803
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
804
|
+
logger.warn(
|
|
805
|
+
`[${toolName}] Failed to generate Zod schema, using permissive schema. Tool will accept any input but may fail at API level. Error: ${errorMessage}`
|
|
806
|
+
);
|
|
807
|
+
return { schema: import_zod2.z.looseObject({}), conversionFailed: true, error: errorMessage };
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// libs/adapters/src/openapi/openapi.adapter.ts
|
|
812
|
+
var RESERVED_KEYS2 = ["__proto__", "constructor", "prototype"];
|
|
813
|
+
function createConsoleLogger(prefix) {
|
|
814
|
+
const formatMessage = (level, msg) => `[${prefix}] ${level}: ${msg}`;
|
|
815
|
+
return {
|
|
816
|
+
verbose: (msg, ...args) => console.debug(formatMessage("VERBOSE", msg), ...args),
|
|
817
|
+
debug: (msg, ...args) => console.debug(formatMessage("DEBUG", msg), ...args),
|
|
818
|
+
info: (msg, ...args) => console.info(formatMessage("INFO", msg), ...args),
|
|
819
|
+
warn: (msg, ...args) => console.warn(formatMessage("WARN", msg), ...args),
|
|
820
|
+
error: (msg, ...args) => console.error(formatMessage("ERROR", msg), ...args),
|
|
821
|
+
child: (childPrefix) => createConsoleLogger(`${prefix}:${childPrefix}`)
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
var OpenapiAdapter = class extends import_sdk2.DynamicAdapter {
|
|
825
|
+
generator;
|
|
826
|
+
logger;
|
|
827
|
+
options;
|
|
828
|
+
constructor(options) {
|
|
829
|
+
super();
|
|
830
|
+
this.options = options;
|
|
831
|
+
this.logger = options.logger ?? createConsoleLogger(`openapi:${options.name}`);
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Receive the SDK logger. Called by the SDK before fetch().
|
|
835
|
+
*/
|
|
836
|
+
setLogger(logger) {
|
|
837
|
+
this.logger = logger;
|
|
838
|
+
}
|
|
839
|
+
async fetch() {
|
|
840
|
+
if (!this.generator) {
|
|
841
|
+
this.generator = await this.initializeGenerator();
|
|
842
|
+
}
|
|
843
|
+
const hasPerSchemeControl = this.options.securitySchemesInInput && this.options.securitySchemesInInput.length > 0;
|
|
844
|
+
const includeSecurityInInput = hasPerSchemeControl || (this.options.generateOptions?.includeSecurityInInput ?? false);
|
|
845
|
+
let openapiTools = await this.generator.generateTools({
|
|
846
|
+
includeOperations: this.options.generateOptions?.includeOperations,
|
|
847
|
+
excludeOperations: this.options.generateOptions?.excludeOperations,
|
|
848
|
+
filterFn: this.options.generateOptions?.filterFn,
|
|
849
|
+
namingStrategy: this.options.generateOptions?.namingStrategy,
|
|
850
|
+
preferredStatusCodes: this.options.generateOptions?.preferredStatusCodes ?? [200, 201, 202, 204],
|
|
851
|
+
includeDeprecated: this.options.generateOptions?.includeDeprecated ?? false,
|
|
852
|
+
includeAllResponses: this.options.generateOptions?.includeAllResponses ?? true,
|
|
853
|
+
includeSecurityInInput,
|
|
854
|
+
maxSchemaDepth: this.options.generateOptions?.maxSchemaDepth,
|
|
855
|
+
includeExamples: this.options.generateOptions?.includeExamples
|
|
856
|
+
});
|
|
857
|
+
if (hasPerSchemeControl) {
|
|
858
|
+
openapiTools = openapiTools.map((tool2) => this.filterSecuritySchemes(tool2));
|
|
859
|
+
}
|
|
860
|
+
const validation = validateSecurityConfiguration(openapiTools, this.options);
|
|
861
|
+
this.logger.info("Security Analysis:");
|
|
862
|
+
this.logger.info(` Security Risk Score: ${validation.securityRiskScore.toUpperCase()}`);
|
|
863
|
+
this.logger.info(` Valid Configuration: ${validation.valid ? "YES" : "NO"}`);
|
|
864
|
+
if (validation.warnings.length > 0) {
|
|
865
|
+
this.logger.info("Messages:");
|
|
866
|
+
validation.warnings.forEach((warning) => {
|
|
867
|
+
if (warning.startsWith("ERROR:")) {
|
|
868
|
+
this.logger.error(` - ${warning}`);
|
|
869
|
+
} else if (warning.startsWith("SECURITY WARNING:")) {
|
|
870
|
+
this.logger.warn(` - ${warning}`);
|
|
871
|
+
} else {
|
|
872
|
+
this.logger.info(` - ${warning}`);
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
if (!validation.valid) {
|
|
877
|
+
throw new Error(
|
|
878
|
+
`[OpenAPI Adapter: ${this.options.name}] Invalid security configuration.
|
|
879
|
+
Missing auth provider mappings for security schemes: ${validation.missingMappings.join(", ")}
|
|
880
|
+
|
|
881
|
+
Your OpenAPI spec requires these security schemes, but no auth configuration was provided.
|
|
882
|
+
|
|
883
|
+
Add one of the following to your adapter configuration:
|
|
884
|
+
|
|
885
|
+
1. authProviderMapper (recommended):
|
|
886
|
+
authProviderMapper: {
|
|
887
|
+
` + validation.missingMappings.map((s) => ` '${s}': (authInfo) => authInfo.user?.${s.toLowerCase()}Token,`).join("\n") + `
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
2. securityResolver:
|
|
891
|
+
securityResolver: (tool, authInfo) => ({ jwt: authInfo.token })
|
|
892
|
+
|
|
893
|
+
3. staticAuth:
|
|
894
|
+
staticAuth: { jwt: process.env.API_TOKEN }
|
|
895
|
+
|
|
896
|
+
4. Include security in input (NOT recommended for production):
|
|
897
|
+
generateOptions: { includeSecurityInInput: true }`
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
let transformedTools = openapiTools;
|
|
901
|
+
if (this.options.descriptionMode && this.options.descriptionMode !== "summaryOnly") {
|
|
902
|
+
transformedTools = transformedTools.map((tool2) => this.applyDescriptionMode(tool2));
|
|
903
|
+
}
|
|
904
|
+
if (this.options.toolTransforms) {
|
|
905
|
+
transformedTools = transformedTools.map((tool2) => this.applyToolTransforms(tool2));
|
|
906
|
+
}
|
|
907
|
+
if (this.options.inputTransforms) {
|
|
908
|
+
transformedTools = transformedTools.map((tool2) => this.applyInputTransforms(tool2));
|
|
909
|
+
}
|
|
910
|
+
const tools = transformedTools.map((openapiTool) => createOpenApiTool(openapiTool, this.options, this.logger));
|
|
911
|
+
return { tools };
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Initialize the OpenAPI tool generator from URL or spec
|
|
915
|
+
* @private
|
|
916
|
+
*/
|
|
917
|
+
async initializeGenerator() {
|
|
918
|
+
if ("url" in this.options) {
|
|
919
|
+
return await import_mcp_from_openapi2.OpenAPIToolGenerator.fromURL(this.options.url, {
|
|
920
|
+
baseUrl: this.options.baseUrl,
|
|
921
|
+
validate: this.options.loadOptions?.validate ?? true,
|
|
922
|
+
dereference: this.options.loadOptions?.dereference ?? true,
|
|
923
|
+
headers: this.options.loadOptions?.headers,
|
|
924
|
+
timeout: this.options.loadOptions?.timeout,
|
|
925
|
+
followRedirects: this.options.loadOptions?.followRedirects
|
|
926
|
+
});
|
|
927
|
+
} else if ("spec" in this.options) {
|
|
928
|
+
return await import_mcp_from_openapi2.OpenAPIToolGenerator.fromJSON(this.options.spec, {
|
|
929
|
+
baseUrl: this.options.baseUrl,
|
|
930
|
+
validate: this.options.loadOptions?.validate ?? true,
|
|
931
|
+
dereference: this.options.loadOptions?.dereference ?? true
|
|
932
|
+
});
|
|
933
|
+
} else {
|
|
934
|
+
throw new Error("Either url or spec must be provided in OpenApiAdapterOptions");
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Apply description mode to generate description from summary/description
|
|
939
|
+
* @private
|
|
940
|
+
*/
|
|
941
|
+
applyDescriptionMode(tool2) {
|
|
942
|
+
const mode = this.options.descriptionMode || "summaryOnly";
|
|
943
|
+
const metadata = tool2.metadata;
|
|
944
|
+
const summary = metadata["operationSummary"];
|
|
945
|
+
const opDescription = metadata["operationDescription"];
|
|
946
|
+
const operationId = metadata["operationId"];
|
|
947
|
+
const method = metadata["method"];
|
|
948
|
+
const path = metadata["path"];
|
|
949
|
+
let description;
|
|
950
|
+
switch (mode) {
|
|
951
|
+
case "descriptionOnly":
|
|
952
|
+
description = opDescription || summary || `${method.toUpperCase()} ${path}`;
|
|
953
|
+
break;
|
|
954
|
+
case "combined":
|
|
955
|
+
if (summary && opDescription) {
|
|
956
|
+
description = `${summary}
|
|
957
|
+
|
|
958
|
+
${opDescription}`;
|
|
959
|
+
} else {
|
|
960
|
+
description = summary || opDescription || `${method.toUpperCase()} ${path}`;
|
|
961
|
+
}
|
|
962
|
+
break;
|
|
963
|
+
case "full": {
|
|
964
|
+
const parts = [];
|
|
965
|
+
if (summary) parts.push(summary);
|
|
966
|
+
if (opDescription && opDescription !== summary) parts.push(opDescription);
|
|
967
|
+
if (operationId) parts.push(`Operation: ${operationId}`);
|
|
968
|
+
parts.push(`${method.toUpperCase()} ${path}`);
|
|
969
|
+
description = parts.join("\n\n");
|
|
970
|
+
break;
|
|
971
|
+
}
|
|
972
|
+
default:
|
|
973
|
+
return tool2;
|
|
974
|
+
}
|
|
975
|
+
return {
|
|
976
|
+
...tool2,
|
|
977
|
+
description
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Collect tool transforms for a specific tool
|
|
982
|
+
* @private
|
|
983
|
+
*/
|
|
984
|
+
collectToolTransforms(tool2) {
|
|
985
|
+
const result = {};
|
|
986
|
+
const opts = this.options.toolTransforms;
|
|
987
|
+
if (!opts) return result;
|
|
988
|
+
if (opts.global) {
|
|
989
|
+
Object.assign(result, opts.global);
|
|
990
|
+
if (opts.global.annotations) {
|
|
991
|
+
result.annotations = { ...opts.global.annotations };
|
|
992
|
+
}
|
|
993
|
+
if (opts.global.tags) {
|
|
994
|
+
result.tags = [...opts.global.tags];
|
|
995
|
+
}
|
|
996
|
+
if (opts.global.examples) {
|
|
997
|
+
result.examples = [...opts.global.examples];
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
if (opts.perTool?.[tool2.name]) {
|
|
1001
|
+
const perTool = opts.perTool[tool2.name];
|
|
1002
|
+
if (perTool.name) result.name = perTool.name;
|
|
1003
|
+
if (perTool.description) result.description = perTool.description;
|
|
1004
|
+
if (perTool.hideFromDiscovery !== void 0) result.hideFromDiscovery = perTool.hideFromDiscovery;
|
|
1005
|
+
if (perTool.ui) result.ui = perTool.ui;
|
|
1006
|
+
if (perTool.annotations) {
|
|
1007
|
+
result.annotations = { ...result.annotations, ...perTool.annotations };
|
|
1008
|
+
}
|
|
1009
|
+
if (perTool.tags) {
|
|
1010
|
+
result.tags = [...result.tags || [], ...perTool.tags];
|
|
1011
|
+
}
|
|
1012
|
+
if (perTool.examples) {
|
|
1013
|
+
result.examples = [...result.examples || [], ...perTool.examples];
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
if (opts.generator) {
|
|
1017
|
+
const generated = opts.generator(tool2);
|
|
1018
|
+
if (generated) {
|
|
1019
|
+
if (generated.name) result.name = generated.name;
|
|
1020
|
+
if (generated.description) result.description = generated.description;
|
|
1021
|
+
if (generated.hideFromDiscovery !== void 0) result.hideFromDiscovery = generated.hideFromDiscovery;
|
|
1022
|
+
if (generated.ui) result.ui = generated.ui;
|
|
1023
|
+
if (generated.annotations) {
|
|
1024
|
+
result.annotations = { ...result.annotations, ...generated.annotations };
|
|
1025
|
+
}
|
|
1026
|
+
if (generated.tags) {
|
|
1027
|
+
result.tags = [...result.tags || [], ...generated.tags];
|
|
1028
|
+
}
|
|
1029
|
+
if (generated.examples) {
|
|
1030
|
+
result.examples = [...result.examples || [], ...generated.examples];
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
return result;
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Apply tool transforms to an OpenAPI tool
|
|
1038
|
+
* @private
|
|
1039
|
+
*/
|
|
1040
|
+
applyToolTransforms(tool2) {
|
|
1041
|
+
const transforms = this.collectToolTransforms(tool2);
|
|
1042
|
+
if (Object.keys(transforms).length === 0) return tool2;
|
|
1043
|
+
let newName = tool2.name;
|
|
1044
|
+
let newDescription = tool2.description;
|
|
1045
|
+
if (transforms.name) {
|
|
1046
|
+
newName = typeof transforms.name === "function" ? transforms.name(tool2.name, tool2) : transforms.name;
|
|
1047
|
+
}
|
|
1048
|
+
if (transforms.description) {
|
|
1049
|
+
newDescription = typeof transforms.description === "function" ? transforms.description(tool2.description, tool2) : transforms.description;
|
|
1050
|
+
}
|
|
1051
|
+
this.logger.debug(`Applied tool transforms to '${tool2.name}'`);
|
|
1052
|
+
const metadataRecord = tool2.metadata;
|
|
1053
|
+
const existingAdapter = metadataRecord["adapter"];
|
|
1054
|
+
return {
|
|
1055
|
+
...tool2,
|
|
1056
|
+
name: newName,
|
|
1057
|
+
description: newDescription,
|
|
1058
|
+
metadata: {
|
|
1059
|
+
...tool2.metadata,
|
|
1060
|
+
adapter: {
|
|
1061
|
+
...existingAdapter || {},
|
|
1062
|
+
toolTransform: transforms
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Collect all input transforms for a specific tool
|
|
1069
|
+
* @private
|
|
1070
|
+
*/
|
|
1071
|
+
collectTransformsForTool(tool2) {
|
|
1072
|
+
const transforms = [];
|
|
1073
|
+
const opts = this.options.inputTransforms;
|
|
1074
|
+
if (!opts) return transforms;
|
|
1075
|
+
if (opts.global) {
|
|
1076
|
+
transforms.push(...opts.global);
|
|
1077
|
+
}
|
|
1078
|
+
if (opts.perTool?.[tool2.name]) {
|
|
1079
|
+
transforms.push(...opts.perTool[tool2.name]);
|
|
1080
|
+
}
|
|
1081
|
+
if (opts.generator) {
|
|
1082
|
+
transforms.push(...opts.generator(tool2));
|
|
1083
|
+
}
|
|
1084
|
+
return transforms;
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Apply input transforms to an OpenAPI tool
|
|
1088
|
+
* - Removes transformed inputKeys from the inputSchema
|
|
1089
|
+
* - Stores transform metadata for runtime injection
|
|
1090
|
+
* @private
|
|
1091
|
+
*/
|
|
1092
|
+
applyInputTransforms(tool2) {
|
|
1093
|
+
const transforms = this.collectTransformsForTool(tool2);
|
|
1094
|
+
if (transforms.length === 0) return tool2;
|
|
1095
|
+
for (const transform of transforms) {
|
|
1096
|
+
if (RESERVED_KEYS2.includes(transform.inputKey)) {
|
|
1097
|
+
throw new Error(
|
|
1098
|
+
`Invalid inputKey '${transform.inputKey}' in tool '${tool2.name}': reserved keys (${RESERVED_KEYS2.join(", ")}) cannot be used`
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
const transformedInputKeys = new Set(transforms.map((t) => t.inputKey));
|
|
1103
|
+
const inputSchema = tool2.inputSchema;
|
|
1104
|
+
const properties = inputSchema?.["properties"] || {};
|
|
1105
|
+
const required = inputSchema?.["required"] || [];
|
|
1106
|
+
const newProperties = { ...properties };
|
|
1107
|
+
for (const key of transformedInputKeys) {
|
|
1108
|
+
delete newProperties[key];
|
|
1109
|
+
}
|
|
1110
|
+
const newRequired = required.filter((key) => !transformedInputKeys.has(key));
|
|
1111
|
+
this.logger.debug(`Applied ${transforms.length} input transforms to tool '${tool2.name}'`);
|
|
1112
|
+
const metadataRecord = tool2.metadata;
|
|
1113
|
+
const existingAdapter = metadataRecord["adapter"];
|
|
1114
|
+
return {
|
|
1115
|
+
...tool2,
|
|
1116
|
+
inputSchema: {
|
|
1117
|
+
...inputSchema,
|
|
1118
|
+
properties: newProperties,
|
|
1119
|
+
...newRequired.length > 0 ? { required: newRequired } : {}
|
|
1120
|
+
},
|
|
1121
|
+
// Store transforms in metadata for runtime use
|
|
1122
|
+
metadata: {
|
|
1123
|
+
...tool2.metadata,
|
|
1124
|
+
adapter: {
|
|
1125
|
+
...existingAdapter || {},
|
|
1126
|
+
inputTransforms: transforms
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Filter security schemes in tool input based on securitySchemesInInput option.
|
|
1133
|
+
* Removes security inputs that should be resolved from context instead of user input.
|
|
1134
|
+
* @private
|
|
1135
|
+
*/
|
|
1136
|
+
filterSecuritySchemes(tool2) {
|
|
1137
|
+
const allowedSchemes = new Set(this.options.securitySchemesInInput || []);
|
|
1138
|
+
if (allowedSchemes.size === 0) return tool2;
|
|
1139
|
+
const schemesToRemove = /* @__PURE__ */ new Set();
|
|
1140
|
+
const inputKeysToRemove = /* @__PURE__ */ new Set();
|
|
1141
|
+
for (const mapper of tool2.mapper) {
|
|
1142
|
+
if (mapper.security?.scheme && !allowedSchemes.has(mapper.security.scheme)) {
|
|
1143
|
+
schemesToRemove.add(mapper.security.scheme);
|
|
1144
|
+
inputKeysToRemove.add(mapper.inputKey);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
if (inputKeysToRemove.size === 0) return tool2;
|
|
1148
|
+
const inputSchema = tool2.inputSchema;
|
|
1149
|
+
const properties = inputSchema?.["properties"] || {};
|
|
1150
|
+
const required = inputSchema?.["required"] || [];
|
|
1151
|
+
const newProperties = { ...properties };
|
|
1152
|
+
for (const key of inputKeysToRemove) {
|
|
1153
|
+
delete newProperties[key];
|
|
1154
|
+
}
|
|
1155
|
+
const newRequired = required.filter((key) => !inputKeysToRemove.has(key));
|
|
1156
|
+
this.logger.debug(
|
|
1157
|
+
`[${tool2.name}] Filtered security schemes from input: ${Array.from(schemesToRemove).join(", ")}. Kept in input: ${Array.from(allowedSchemes).filter((s) => !schemesToRemove.has(s)).join(", ") || "none"}`
|
|
1158
|
+
);
|
|
1159
|
+
const metadataRecord = tool2.metadata;
|
|
1160
|
+
const existingAdapter = metadataRecord["adapter"];
|
|
1161
|
+
return {
|
|
1162
|
+
...tool2,
|
|
1163
|
+
inputSchema: {
|
|
1164
|
+
...inputSchema,
|
|
1165
|
+
properties: newProperties,
|
|
1166
|
+
...newRequired.length > 0 ? { required: newRequired } : {}
|
|
1167
|
+
},
|
|
1168
|
+
// Store which security schemes are in input vs context for later resolution
|
|
1169
|
+
metadata: {
|
|
1170
|
+
...tool2.metadata,
|
|
1171
|
+
adapter: {
|
|
1172
|
+
...existingAdapter || {},
|
|
1173
|
+
securitySchemesInInput: Array.from(allowedSchemes),
|
|
1174
|
+
securitySchemesFromContext: Array.from(schemesToRemove)
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
};
|
|
1180
|
+
OpenapiAdapter = __decorateClass([
|
|
1181
|
+
(0, import_sdk2.Adapter)({
|
|
1182
|
+
name: "openapi",
|
|
1183
|
+
description: "OpenAPI adapter for FrontMCP - Automatically generates MCP tools from OpenAPI specifications"
|
|
1184
|
+
})
|
|
1185
|
+
], OpenapiAdapter);
|
|
1186
|
+
|
|
1187
|
+
// libs/adapters/src/openapi/openapi.types.ts
|
|
1188
|
+
var FRONTMCP_EXTENSION_KEY = "x-frontmcp";
|
|
1189
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1190
|
+
0 && (module.exports = {
|
|
1191
|
+
FRONTMCP_EXTENSION_KEY,
|
|
1192
|
+
OpenapiAdapter
|
|
1193
|
+
});
|