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