@frontmcp/adapters 1.9.1-rc.3 → 1.9.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 CHANGED
@@ -10,7 +10,10 @@ var __decorateClass = (decorators, target, key, kind) => {
10
10
  };
11
11
 
12
12
  // libs/adapters/src/openapi/openapi.adapter.ts
13
- import { normalizeSsrfOptions as normalizeSsrfOptions2, OpenAPIToolGenerator } from "mcp-from-openapi";
13
+ import {
14
+ normalizeSsrfOptions as normalizeSsrfOptions2,
15
+ OpenAPIToolGenerator
16
+ } from "mcp-from-openapi";
14
17
  import { Adapter, DynamicAdapter } from "@frontmcp/sdk";
15
18
 
16
19
  // libs/adapters/src/openapi/openapi-spec-poller.ts
@@ -605,7 +608,7 @@ async function resolveInputSecurity(tool2, input, options, securityResolver) {
605
608
 
606
609
  // libs/adapters/src/openapi/openapi.tool.ts
607
610
  import { z as z2 } from "@frontmcp/lazy-zod";
608
- import { tool } from "@frontmcp/sdk";
611
+ import { InvalidInputError, tool } from "@frontmcp/sdk";
609
612
  import { isRedirectResponse } from "@frontmcp/utils";
610
613
 
611
614
  // libs/adapters/src/openapi/openapi.frontmcp-schema.ts
@@ -1005,6 +1008,7 @@ function createOpenApiTool(openapiTool, options, logger) {
1005
1008
  const frontmcpValidation = validateFrontMcpExtension(metadata.frontmcp, openapiTool.name, logger);
1006
1009
  const frontmcpExt = frontmcpValidation.data;
1007
1010
  const schemaResult = getZodSchemaFromJsonSchema(openapiTool.inputSchema, openapiTool.name, logger);
1011
+ const argumentCheck = schemaResult.conversionFailed ? void 0 : getZodSchemaFromJsonSchema(toArgumentJsonSchema(openapiTool), openapiTool.name, logger);
1008
1012
  let wrappedOutputSchema;
1009
1013
  if (openapiTool.outputSchema !== void 0) {
1010
1014
  const baseOutputSchema = openapiTool.outputSchema ?? { type: "string" };
@@ -1022,11 +1026,15 @@ function createOpenApiTool(openapiTool, options, logger) {
1022
1026
  const toolMetadata = {
1023
1027
  id: openapiTool.name,
1024
1028
  name: openapiTool.name,
1029
+ ...openapiTool.title && { title: openapiTool.title },
1025
1030
  description: openapiTool.description,
1026
1031
  inputSchema: schemaResult.schema.shape || {},
1027
1032
  rawInputSchema: openapiTool.inputSchema,
1028
1033
  // Add output schema for tool/list to expose (only if not moved to description)
1029
- ...wrappedOutputSchema && { rawOutputSchema: wrappedOutputSchema }
1034
+ ...wrappedOutputSchema && { rawOutputSchema: wrappedOutputSchema },
1035
+ ...openapiTool.annotations && { annotations: { ...openapiTool.annotations } },
1036
+ ...openapiTool.icons?.length && { icons: openapiTool.icons.map((icon) => ({ ...icon })) },
1037
+ ...openapiTool._meta && { _meta: { ...openapiTool._meta } }
1030
1038
  };
1031
1039
  if (schemaResult.conversionFailed) {
1032
1040
  toolMetadata["_schemaConversionFailed"] = true;
@@ -1034,7 +1042,10 @@ function createOpenApiTool(openapiTool, options, logger) {
1034
1042
  }
1035
1043
  if (frontmcpExt) {
1036
1044
  if (frontmcpExt.annotations) {
1037
- toolMetadata["annotations"] = { ...frontmcpExt.annotations };
1045
+ toolMetadata["annotations"] = {
1046
+ ...toolMetadata["annotations"] || {},
1047
+ ...frontmcpExt.annotations
1048
+ };
1038
1049
  }
1039
1050
  if (frontmcpExt.tags) {
1040
1051
  toolMetadata["tags"] = [...frontmcpExt.tags];
@@ -1074,6 +1085,13 @@ function createOpenApiTool(openapiTool, options, logger) {
1074
1085
  }
1075
1086
  return tool(toolMetadata)(async (input, toolCtx) => {
1076
1087
  const ctx = toolCtx.context;
1088
+ if (argumentCheck && !argumentCheck.conversionFailed) {
1089
+ const validation = argumentCheck.schema.safeParse(input);
1090
+ if (!validation.success) {
1091
+ const problems = validation.error.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`);
1092
+ throw new InvalidInputError(`Invalid arguments for tool '${openapiTool.name}': ${problems.join("; ")}`);
1093
+ }
1094
+ }
1077
1095
  const transformContext = {
1078
1096
  ctx,
1079
1097
  env: process.env,
@@ -1287,6 +1305,64 @@ async function injectTransformedValues(input, transforms, ctx) {
1287
1305
  }
1288
1306
  return result;
1289
1307
  }
1308
+ var SUBSCHEMA_KEYWORDS = [
1309
+ "items",
1310
+ "additionalProperties",
1311
+ "not",
1312
+ "if",
1313
+ "then",
1314
+ "else",
1315
+ "contains",
1316
+ "propertyNames",
1317
+ "unevaluatedItems",
1318
+ "unevaluatedProperties"
1319
+ ];
1320
+ var SUBSCHEMA_LIST_KEYWORDS = ["allOf", "anyOf", "prefixItems"];
1321
+ var SUBSCHEMA_MAP_KEYWORDS = ["properties", "patternProperties", "dependentSchemas", "$defs", "definitions"];
1322
+ function toArgumentJsonSchema(openapiTool) {
1323
+ const argumentSchema = relaxSchema(openapiTool.inputSchema);
1324
+ const credentialKeys = new Set(
1325
+ openapiTool.mapper.filter((mapper) => mapper.security).map((mapper) => mapper.inputKey)
1326
+ );
1327
+ const required = argumentSchema["required"];
1328
+ if (Array.isArray(required)) {
1329
+ argumentSchema["required"] = required.filter((key) => !credentialKeys.has(key));
1330
+ }
1331
+ return argumentSchema;
1332
+ }
1333
+ function relaxSchema(schema) {
1334
+ if (Array.isArray(schema)) return schema.map(relaxSchema);
1335
+ if (typeof schema !== "object" || schema === null) return schema;
1336
+ const relaxed = { ...schema };
1337
+ delete relaxed["format"];
1338
+ for (const keyword of SUBSCHEMA_KEYWORDS) {
1339
+ if (keyword in relaxed) relaxed[keyword] = relaxSchema(relaxed[keyword]);
1340
+ }
1341
+ for (const keyword of SUBSCHEMA_LIST_KEYWORDS) {
1342
+ const subschemas = relaxed[keyword];
1343
+ if (Array.isArray(subschemas)) relaxed[keyword] = subschemas.map(relaxSchema);
1344
+ }
1345
+ for (const keyword of SUBSCHEMA_MAP_KEYWORDS) {
1346
+ const subschemasByName = relaxed[keyword];
1347
+ if (typeof subschemasByName === "object" && subschemasByName !== null) {
1348
+ relaxed[keyword] = Object.fromEntries(
1349
+ Object.entries(subschemasByName).map(([name, subschema]) => [name, relaxSchema(subschema)])
1350
+ );
1351
+ }
1352
+ }
1353
+ const exclusiveBranches = relaxed["oneOf"];
1354
+ if (Array.isArray(exclusiveBranches)) {
1355
+ delete relaxed["oneOf"];
1356
+ const branches = exclusiveBranches.map(relaxSchema);
1357
+ if (Array.isArray(relaxed["anyOf"])) {
1358
+ const allOf = Array.isArray(relaxed["allOf"]) ? relaxed["allOf"] : [];
1359
+ relaxed["allOf"] = [...allOf, { anyOf: branches }];
1360
+ } else {
1361
+ relaxed["anyOf"] = branches;
1362
+ }
1363
+ }
1364
+ return relaxed;
1365
+ }
1290
1366
  function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
1291
1367
  if (typeof jsonSchema !== "object" || jsonSchema === null) {
1292
1368
  logger.warn(`[${toolName}] No valid JSON schema provided, using permissive schema`);
@@ -1314,6 +1390,18 @@ function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
1314
1390
 
1315
1391
  // libs/adapters/src/openapi/openapi.adapter.ts
1316
1392
  var RESERVED_KEYS2 = ["__proto__", "constructor", "prototype"];
1393
+ var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options", "trace"];
1394
+ function normalizeMethods(adapterName, optionName, methods) {
1395
+ return methods?.map((method) => {
1396
+ const lowerMethod = method.toLowerCase();
1397
+ if (!HTTP_METHODS.includes(lowerMethod)) {
1398
+ throw new Error(
1399
+ `[OpenAPI Adapter: ${adapterName}] generateOptions.${optionName} lists "${method}", which is not an HTTP method; use one of ${HTTP_METHODS.join(", ")}.`
1400
+ );
1401
+ }
1402
+ return lowerMethod;
1403
+ });
1404
+ }
1317
1405
  function createConsoleLogger(prefix) {
1318
1406
  const formatMessage = (level, msg) => `[${prefix}] ${level}: ${msg}`;
1319
1407
  return {
@@ -1332,9 +1420,14 @@ var OpenapiAdapter = class extends DynamicAdapter {
1332
1420
  poller = null;
1333
1421
  updateCallbacks = /* @__PURE__ */ new Set();
1334
1422
  rebuildChain = Promise.resolve();
1423
+ methodFilters;
1335
1424
  constructor(options) {
1336
1425
  super();
1337
1426
  this.options = options;
1427
+ this.methodFilters = {
1428
+ includeMethods: normalizeMethods(options.name, "includeMethods", options.generateOptions?.includeMethods),
1429
+ excludeMethods: normalizeMethods(options.name, "excludeMethods", options.generateOptions?.excludeMethods)
1430
+ };
1338
1431
  this.logger = options.logger ?? createConsoleLogger(`openapi:${options.name}`);
1339
1432
  if (options.polling?.enabled && !("url" in options)) {
1340
1433
  throw new Error(
@@ -1358,6 +1451,8 @@ var OpenapiAdapter = class extends DynamicAdapter {
1358
1451
  const generateOptions = this.options.generateOptions;
1359
1452
  let openapiTools = await this.generator.generateTools({
1360
1453
  ...generateOptions,
1454
+ ...this.methodFilters,
1455
+ filterFn: this.operationFilter(),
1361
1456
  preferredStatusCodes: generateOptions?.preferredStatusCodes ?? [200, 201, 202, 204],
1362
1457
  includeDeprecated: generateOptions?.includeDeprecated ?? false,
1363
1458
  includeAllResponses: generateOptions?.includeAllResponses ?? true,
@@ -1443,28 +1538,31 @@ var OpenapiAdapter = class extends DynamicAdapter {
1443
1538
  resolveRefResolution() {
1444
1539
  return this.options.loadOptions?.refResolution ?? { allowedProtocols: [] };
1445
1540
  }
1541
+ /**
1542
+ * `generateOptions.filterFn`, and with `includeOperations` set, only the operations it names: the
1543
+ * generator lets an operation with no `operationId` through that list.
1544
+ */
1545
+ operationFilter() {
1546
+ const { includeOperations, filterFn } = this.options.generateOptions ?? {};
1547
+ if (!includeOperations) return filterFn;
1548
+ return (operation) => operation.operationId !== void 0 && includeOperations.includes(operation.operationId) && (filterFn?.(operation) ?? true);
1549
+ }
1446
1550
  async initializeGenerator() {
1447
- const refResolution = this.resolveRefResolution();
1551
+ const loadOptions = {
1552
+ ...this.options.loadOptions,
1553
+ baseUrl: this.options.baseUrl,
1554
+ validate: this.options.loadOptions?.validate ?? true,
1555
+ dereference: this.options.loadOptions?.dereference ?? true,
1556
+ // SECURITY: do not follow spec-URL redirects by default — a 3xx to an
1557
+ // internal target would otherwise be fetched. With mcp-from-openapi
1558
+ // ≥ 2.5.0 each hop is re-validated; opt in with loadOptions.followRedirects.
1559
+ followRedirects: this.options.loadOptions?.followRedirects ?? false,
1560
+ refResolution: this.resolveRefResolution()
1561
+ };
1448
1562
  if ("url" in this.options) {
1449
- return await OpenAPIToolGenerator.fromURL(this.options.url, {
1450
- baseUrl: this.options.baseUrl,
1451
- validate: this.options.loadOptions?.validate ?? true,
1452
- dereference: this.options.loadOptions?.dereference ?? true,
1453
- headers: this.options.loadOptions?.headers,
1454
- timeout: this.options.loadOptions?.timeout,
1455
- // SECURITY: do not follow spec-URL redirects by default — a 3xx to an
1456
- // internal target would otherwise be fetched. With mcp-from-openapi
1457
- // ≥ 2.5.0 each hop is re-validated; opt in with loadOptions.followRedirects.
1458
- followRedirects: this.options.loadOptions?.followRedirects ?? false,
1459
- refResolution
1460
- });
1563
+ return await OpenAPIToolGenerator.fromURL(this.options.url, loadOptions);
1461
1564
  } else if ("spec" in this.options) {
1462
- return await OpenAPIToolGenerator.fromJSON(this.options.spec, {
1463
- baseUrl: this.options.baseUrl,
1464
- validate: this.options.loadOptions?.validate ?? true,
1465
- dereference: this.options.loadOptions?.dereference ?? true,
1466
- refResolution
1467
- });
1565
+ return await OpenAPIToolGenerator.fromJSON(this.options.spec, loadOptions);
1468
1566
  } else {
1469
1567
  throw new Error("Either url or spec must be provided in OpenApiAdapterOptions");
1470
1568
  }
@@ -10,7 +10,10 @@ var __decorateClass = (decorators, target, key, kind) => {
10
10
  };
11
11
 
12
12
  // libs/adapters/src/openapi/openapi.adapter.ts
13
- import { normalizeSsrfOptions as normalizeSsrfOptions2, OpenAPIToolGenerator } from "mcp-from-openapi";
13
+ import {
14
+ normalizeSsrfOptions as normalizeSsrfOptions2,
15
+ OpenAPIToolGenerator
16
+ } from "mcp-from-openapi";
14
17
  import { Adapter, DynamicAdapter } from "@frontmcp/sdk";
15
18
 
16
19
  // libs/adapters/src/openapi/openapi-spec-poller.ts
@@ -605,7 +608,7 @@ async function resolveInputSecurity(tool2, input, options, securityResolver) {
605
608
 
606
609
  // libs/adapters/src/openapi/openapi.tool.ts
607
610
  import { z as z2 } from "@frontmcp/lazy-zod";
608
- import { tool } from "@frontmcp/sdk";
611
+ import { InvalidInputError, tool } from "@frontmcp/sdk";
609
612
  import { isRedirectResponse } from "@frontmcp/utils";
610
613
 
611
614
  // libs/adapters/src/openapi/openapi.frontmcp-schema.ts
@@ -1005,6 +1008,7 @@ function createOpenApiTool(openapiTool, options, logger) {
1005
1008
  const frontmcpValidation = validateFrontMcpExtension(metadata.frontmcp, openapiTool.name, logger);
1006
1009
  const frontmcpExt = frontmcpValidation.data;
1007
1010
  const schemaResult = getZodSchemaFromJsonSchema(openapiTool.inputSchema, openapiTool.name, logger);
1011
+ const argumentCheck = schemaResult.conversionFailed ? void 0 : getZodSchemaFromJsonSchema(toArgumentJsonSchema(openapiTool), openapiTool.name, logger);
1008
1012
  let wrappedOutputSchema;
1009
1013
  if (openapiTool.outputSchema !== void 0) {
1010
1014
  const baseOutputSchema = openapiTool.outputSchema ?? { type: "string" };
@@ -1022,11 +1026,15 @@ function createOpenApiTool(openapiTool, options, logger) {
1022
1026
  const toolMetadata = {
1023
1027
  id: openapiTool.name,
1024
1028
  name: openapiTool.name,
1029
+ ...openapiTool.title && { title: openapiTool.title },
1025
1030
  description: openapiTool.description,
1026
1031
  inputSchema: schemaResult.schema.shape || {},
1027
1032
  rawInputSchema: openapiTool.inputSchema,
1028
1033
  // Add output schema for tool/list to expose (only if not moved to description)
1029
- ...wrappedOutputSchema && { rawOutputSchema: wrappedOutputSchema }
1034
+ ...wrappedOutputSchema && { rawOutputSchema: wrappedOutputSchema },
1035
+ ...openapiTool.annotations && { annotations: { ...openapiTool.annotations } },
1036
+ ...openapiTool.icons?.length && { icons: openapiTool.icons.map((icon) => ({ ...icon })) },
1037
+ ...openapiTool._meta && { _meta: { ...openapiTool._meta } }
1030
1038
  };
1031
1039
  if (schemaResult.conversionFailed) {
1032
1040
  toolMetadata["_schemaConversionFailed"] = true;
@@ -1034,7 +1042,10 @@ function createOpenApiTool(openapiTool, options, logger) {
1034
1042
  }
1035
1043
  if (frontmcpExt) {
1036
1044
  if (frontmcpExt.annotations) {
1037
- toolMetadata["annotations"] = { ...frontmcpExt.annotations };
1045
+ toolMetadata["annotations"] = {
1046
+ ...toolMetadata["annotations"] || {},
1047
+ ...frontmcpExt.annotations
1048
+ };
1038
1049
  }
1039
1050
  if (frontmcpExt.tags) {
1040
1051
  toolMetadata["tags"] = [...frontmcpExt.tags];
@@ -1074,6 +1085,13 @@ function createOpenApiTool(openapiTool, options, logger) {
1074
1085
  }
1075
1086
  return tool(toolMetadata)(async (input, toolCtx) => {
1076
1087
  const ctx = toolCtx.context;
1088
+ if (argumentCheck && !argumentCheck.conversionFailed) {
1089
+ const validation = argumentCheck.schema.safeParse(input);
1090
+ if (!validation.success) {
1091
+ const problems = validation.error.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`);
1092
+ throw new InvalidInputError(`Invalid arguments for tool '${openapiTool.name}': ${problems.join("; ")}`);
1093
+ }
1094
+ }
1077
1095
  const transformContext = {
1078
1096
  ctx,
1079
1097
  env: process.env,
@@ -1287,6 +1305,64 @@ async function injectTransformedValues(input, transforms, ctx) {
1287
1305
  }
1288
1306
  return result;
1289
1307
  }
1308
+ var SUBSCHEMA_KEYWORDS = [
1309
+ "items",
1310
+ "additionalProperties",
1311
+ "not",
1312
+ "if",
1313
+ "then",
1314
+ "else",
1315
+ "contains",
1316
+ "propertyNames",
1317
+ "unevaluatedItems",
1318
+ "unevaluatedProperties"
1319
+ ];
1320
+ var SUBSCHEMA_LIST_KEYWORDS = ["allOf", "anyOf", "prefixItems"];
1321
+ var SUBSCHEMA_MAP_KEYWORDS = ["properties", "patternProperties", "dependentSchemas", "$defs", "definitions"];
1322
+ function toArgumentJsonSchema(openapiTool) {
1323
+ const argumentSchema = relaxSchema(openapiTool.inputSchema);
1324
+ const credentialKeys = new Set(
1325
+ openapiTool.mapper.filter((mapper) => mapper.security).map((mapper) => mapper.inputKey)
1326
+ );
1327
+ const required = argumentSchema["required"];
1328
+ if (Array.isArray(required)) {
1329
+ argumentSchema["required"] = required.filter((key) => !credentialKeys.has(key));
1330
+ }
1331
+ return argumentSchema;
1332
+ }
1333
+ function relaxSchema(schema) {
1334
+ if (Array.isArray(schema)) return schema.map(relaxSchema);
1335
+ if (typeof schema !== "object" || schema === null) return schema;
1336
+ const relaxed = { ...schema };
1337
+ delete relaxed["format"];
1338
+ for (const keyword of SUBSCHEMA_KEYWORDS) {
1339
+ if (keyword in relaxed) relaxed[keyword] = relaxSchema(relaxed[keyword]);
1340
+ }
1341
+ for (const keyword of SUBSCHEMA_LIST_KEYWORDS) {
1342
+ const subschemas = relaxed[keyword];
1343
+ if (Array.isArray(subschemas)) relaxed[keyword] = subschemas.map(relaxSchema);
1344
+ }
1345
+ for (const keyword of SUBSCHEMA_MAP_KEYWORDS) {
1346
+ const subschemasByName = relaxed[keyword];
1347
+ if (typeof subschemasByName === "object" && subschemasByName !== null) {
1348
+ relaxed[keyword] = Object.fromEntries(
1349
+ Object.entries(subschemasByName).map(([name, subschema]) => [name, relaxSchema(subschema)])
1350
+ );
1351
+ }
1352
+ }
1353
+ const exclusiveBranches = relaxed["oneOf"];
1354
+ if (Array.isArray(exclusiveBranches)) {
1355
+ delete relaxed["oneOf"];
1356
+ const branches = exclusiveBranches.map(relaxSchema);
1357
+ if (Array.isArray(relaxed["anyOf"])) {
1358
+ const allOf = Array.isArray(relaxed["allOf"]) ? relaxed["allOf"] : [];
1359
+ relaxed["allOf"] = [...allOf, { anyOf: branches }];
1360
+ } else {
1361
+ relaxed["anyOf"] = branches;
1362
+ }
1363
+ }
1364
+ return relaxed;
1365
+ }
1290
1366
  function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
1291
1367
  if (typeof jsonSchema !== "object" || jsonSchema === null) {
1292
1368
  logger.warn(`[${toolName}] No valid JSON schema provided, using permissive schema`);
@@ -1314,6 +1390,18 @@ function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
1314
1390
 
1315
1391
  // libs/adapters/src/openapi/openapi.adapter.ts
1316
1392
  var RESERVED_KEYS2 = ["__proto__", "constructor", "prototype"];
1393
+ var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options", "trace"];
1394
+ function normalizeMethods(adapterName, optionName, methods) {
1395
+ return methods?.map((method) => {
1396
+ const lowerMethod = method.toLowerCase();
1397
+ if (!HTTP_METHODS.includes(lowerMethod)) {
1398
+ throw new Error(
1399
+ `[OpenAPI Adapter: ${adapterName}] generateOptions.${optionName} lists "${method}", which is not an HTTP method; use one of ${HTTP_METHODS.join(", ")}.`
1400
+ );
1401
+ }
1402
+ return lowerMethod;
1403
+ });
1404
+ }
1317
1405
  function createConsoleLogger(prefix) {
1318
1406
  const formatMessage = (level, msg) => `[${prefix}] ${level}: ${msg}`;
1319
1407
  return {
@@ -1332,9 +1420,14 @@ var OpenapiAdapter = class extends DynamicAdapter {
1332
1420
  poller = null;
1333
1421
  updateCallbacks = /* @__PURE__ */ new Set();
1334
1422
  rebuildChain = Promise.resolve();
1423
+ methodFilters;
1335
1424
  constructor(options) {
1336
1425
  super();
1337
1426
  this.options = options;
1427
+ this.methodFilters = {
1428
+ includeMethods: normalizeMethods(options.name, "includeMethods", options.generateOptions?.includeMethods),
1429
+ excludeMethods: normalizeMethods(options.name, "excludeMethods", options.generateOptions?.excludeMethods)
1430
+ };
1338
1431
  this.logger = options.logger ?? createConsoleLogger(`openapi:${options.name}`);
1339
1432
  if (options.polling?.enabled && !("url" in options)) {
1340
1433
  throw new Error(
@@ -1358,6 +1451,8 @@ var OpenapiAdapter = class extends DynamicAdapter {
1358
1451
  const generateOptions = this.options.generateOptions;
1359
1452
  let openapiTools = await this.generator.generateTools({
1360
1453
  ...generateOptions,
1454
+ ...this.methodFilters,
1455
+ filterFn: this.operationFilter(),
1361
1456
  preferredStatusCodes: generateOptions?.preferredStatusCodes ?? [200, 201, 202, 204],
1362
1457
  includeDeprecated: generateOptions?.includeDeprecated ?? false,
1363
1458
  includeAllResponses: generateOptions?.includeAllResponses ?? true,
@@ -1443,28 +1538,31 @@ var OpenapiAdapter = class extends DynamicAdapter {
1443
1538
  resolveRefResolution() {
1444
1539
  return this.options.loadOptions?.refResolution ?? { allowedProtocols: [] };
1445
1540
  }
1541
+ /**
1542
+ * `generateOptions.filterFn`, and with `includeOperations` set, only the operations it names: the
1543
+ * generator lets an operation with no `operationId` through that list.
1544
+ */
1545
+ operationFilter() {
1546
+ const { includeOperations, filterFn } = this.options.generateOptions ?? {};
1547
+ if (!includeOperations) return filterFn;
1548
+ return (operation) => operation.operationId !== void 0 && includeOperations.includes(operation.operationId) && (filterFn?.(operation) ?? true);
1549
+ }
1446
1550
  async initializeGenerator() {
1447
- const refResolution = this.resolveRefResolution();
1551
+ const loadOptions = {
1552
+ ...this.options.loadOptions,
1553
+ baseUrl: this.options.baseUrl,
1554
+ validate: this.options.loadOptions?.validate ?? true,
1555
+ dereference: this.options.loadOptions?.dereference ?? true,
1556
+ // SECURITY: do not follow spec-URL redirects by default — a 3xx to an
1557
+ // internal target would otherwise be fetched. With mcp-from-openapi
1558
+ // ≥ 2.5.0 each hop is re-validated; opt in with loadOptions.followRedirects.
1559
+ followRedirects: this.options.loadOptions?.followRedirects ?? false,
1560
+ refResolution: this.resolveRefResolution()
1561
+ };
1448
1562
  if ("url" in this.options) {
1449
- return await OpenAPIToolGenerator.fromURL(this.options.url, {
1450
- baseUrl: this.options.baseUrl,
1451
- validate: this.options.loadOptions?.validate ?? true,
1452
- dereference: this.options.loadOptions?.dereference ?? true,
1453
- headers: this.options.loadOptions?.headers,
1454
- timeout: this.options.loadOptions?.timeout,
1455
- // SECURITY: do not follow spec-URL redirects by default — a 3xx to an
1456
- // internal target would otherwise be fetched. With mcp-from-openapi
1457
- // ≥ 2.5.0 each hop is re-validated; opt in with loadOptions.followRedirects.
1458
- followRedirects: this.options.loadOptions?.followRedirects ?? false,
1459
- refResolution
1460
- });
1563
+ return await OpenAPIToolGenerator.fromURL(this.options.url, loadOptions);
1461
1564
  } else if ("spec" in this.options) {
1462
- return await OpenAPIToolGenerator.fromJSON(this.options.spec, {
1463
- baseUrl: this.options.baseUrl,
1464
- validate: this.options.loadOptions?.validate ?? true,
1465
- dereference: this.options.loadOptions?.dereference ?? true,
1466
- refResolution
1467
- });
1565
+ return await OpenAPIToolGenerator.fromJSON(this.options.spec, loadOptions);
1468
1566
  } else {
1469
1567
  throw new Error("Either url or spec must be provided in OpenApiAdapterOptions");
1470
1568
  }
package/esm/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/adapters",
3
- "version": "1.9.1-rc.3",
3
+ "version": "1.9.2",
4
4
  "description": "Adapters for the FrontMCP framework",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -64,15 +64,15 @@
64
64
  "node": ">=24.0.0"
65
65
  },
66
66
  "dependencies": {
67
- "@frontmcp/auth": "1.9.1-rc.3",
68
- "@frontmcp/di": "1.9.1-rc.3",
69
- "@frontmcp/sdk": "1.9.1-rc.3",
70
- "@frontmcp/utils": "1.9.1-rc.3",
67
+ "@frontmcp/auth": "1.9.2",
68
+ "@frontmcp/di": "1.9.2",
69
+ "@frontmcp/sdk": "1.9.2",
70
+ "@frontmcp/utils": "1.9.2",
71
71
  "js-yaml": "^4.1.0",
72
72
  "mcp-from-openapi": "2.8.0",
73
73
  "openapi-types": "^12.1.3"
74
74
  },
75
75
  "peerDependencies": {
76
- "@frontmcp/lazy-zod": "1.9.1-rc.3"
76
+ "@frontmcp/lazy-zod": "1.9.2"
77
77
  }
78
78
  }
@@ -843,7 +843,7 @@ var SaasPullSource = class {
843
843
  bundle = await this.loadCache();
844
844
  if (!bundle) {
845
845
  throw new Error(
846
- `[saas-source] initial pull failed and no cached bundle is available at ${this.cachePath()}: ${e.message}`,
846
+ `[saas-source] initial pull failed and no cached bundle is available ${this.cacheLocation()}: ${e.message}`,
847
847
  { cause: e }
848
848
  );
849
849
  }
@@ -989,6 +989,10 @@ var SaasPullSource = class {
989
989
  trimmed.startsWith("{") || trimmed.startsWith("[") ? { kind: "json", content: body } : { kind: "yaml", content: body }
990
990
  );
991
991
  }
992
+ /** Where the last-good bundle is kept, for messages: an injected store (a Worker's KV) has no file path. */
993
+ cacheLocation() {
994
+ return this.deps.cache ? "in the injected bundle cache" : `at ${this.cachePath()}`;
995
+ }
992
996
  cachePath() {
993
997
  const dir = this.cacheDir ?? DEFAULT_CACHE_DIR;
994
998
  return pathResolve(dir, `${this.options.expectedAudience.replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`);
package/index.js CHANGED
@@ -1037,6 +1037,7 @@ function createOpenApiTool(openapiTool, options, logger) {
1037
1037
  const frontmcpValidation = validateFrontMcpExtension(metadata.frontmcp, openapiTool.name, logger);
1038
1038
  const frontmcpExt = frontmcpValidation.data;
1039
1039
  const schemaResult = getZodSchemaFromJsonSchema(openapiTool.inputSchema, openapiTool.name, logger);
1040
+ const argumentCheck = schemaResult.conversionFailed ? void 0 : getZodSchemaFromJsonSchema(toArgumentJsonSchema(openapiTool), openapiTool.name, logger);
1040
1041
  let wrappedOutputSchema;
1041
1042
  if (openapiTool.outputSchema !== void 0) {
1042
1043
  const baseOutputSchema = openapiTool.outputSchema ?? { type: "string" };
@@ -1054,11 +1055,15 @@ function createOpenApiTool(openapiTool, options, logger) {
1054
1055
  const toolMetadata = {
1055
1056
  id: openapiTool.name,
1056
1057
  name: openapiTool.name,
1058
+ ...openapiTool.title && { title: openapiTool.title },
1057
1059
  description: openapiTool.description,
1058
1060
  inputSchema: schemaResult.schema.shape || {},
1059
1061
  rawInputSchema: openapiTool.inputSchema,
1060
1062
  // Add output schema for tool/list to expose (only if not moved to description)
1061
- ...wrappedOutputSchema && { rawOutputSchema: wrappedOutputSchema }
1063
+ ...wrappedOutputSchema && { rawOutputSchema: wrappedOutputSchema },
1064
+ ...openapiTool.annotations && { annotations: { ...openapiTool.annotations } },
1065
+ ...openapiTool.icons?.length && { icons: openapiTool.icons.map((icon) => ({ ...icon })) },
1066
+ ...openapiTool._meta && { _meta: { ...openapiTool._meta } }
1062
1067
  };
1063
1068
  if (schemaResult.conversionFailed) {
1064
1069
  toolMetadata["_schemaConversionFailed"] = true;
@@ -1066,7 +1071,10 @@ function createOpenApiTool(openapiTool, options, logger) {
1066
1071
  }
1067
1072
  if (frontmcpExt) {
1068
1073
  if (frontmcpExt.annotations) {
1069
- toolMetadata["annotations"] = { ...frontmcpExt.annotations };
1074
+ toolMetadata["annotations"] = {
1075
+ ...toolMetadata["annotations"] || {},
1076
+ ...frontmcpExt.annotations
1077
+ };
1070
1078
  }
1071
1079
  if (frontmcpExt.tags) {
1072
1080
  toolMetadata["tags"] = [...frontmcpExt.tags];
@@ -1106,6 +1114,13 @@ function createOpenApiTool(openapiTool, options, logger) {
1106
1114
  }
1107
1115
  return (0, import_sdk4.tool)(toolMetadata)(async (input, toolCtx) => {
1108
1116
  const ctx = toolCtx.context;
1117
+ if (argumentCheck && !argumentCheck.conversionFailed) {
1118
+ const validation = argumentCheck.schema.safeParse(input);
1119
+ if (!validation.success) {
1120
+ const problems = validation.error.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`);
1121
+ throw new import_sdk4.InvalidInputError(`Invalid arguments for tool '${openapiTool.name}': ${problems.join("; ")}`);
1122
+ }
1123
+ }
1109
1124
  const transformContext = {
1110
1125
  ctx,
1111
1126
  env: process.env,
@@ -1319,6 +1334,64 @@ async function injectTransformedValues(input, transforms, ctx) {
1319
1334
  }
1320
1335
  return result;
1321
1336
  }
1337
+ var SUBSCHEMA_KEYWORDS = [
1338
+ "items",
1339
+ "additionalProperties",
1340
+ "not",
1341
+ "if",
1342
+ "then",
1343
+ "else",
1344
+ "contains",
1345
+ "propertyNames",
1346
+ "unevaluatedItems",
1347
+ "unevaluatedProperties"
1348
+ ];
1349
+ var SUBSCHEMA_LIST_KEYWORDS = ["allOf", "anyOf", "prefixItems"];
1350
+ var SUBSCHEMA_MAP_KEYWORDS = ["properties", "patternProperties", "dependentSchemas", "$defs", "definitions"];
1351
+ function toArgumentJsonSchema(openapiTool) {
1352
+ const argumentSchema = relaxSchema(openapiTool.inputSchema);
1353
+ const credentialKeys = new Set(
1354
+ openapiTool.mapper.filter((mapper) => mapper.security).map((mapper) => mapper.inputKey)
1355
+ );
1356
+ const required = argumentSchema["required"];
1357
+ if (Array.isArray(required)) {
1358
+ argumentSchema["required"] = required.filter((key) => !credentialKeys.has(key));
1359
+ }
1360
+ return argumentSchema;
1361
+ }
1362
+ function relaxSchema(schema) {
1363
+ if (Array.isArray(schema)) return schema.map(relaxSchema);
1364
+ if (typeof schema !== "object" || schema === null) return schema;
1365
+ const relaxed = { ...schema };
1366
+ delete relaxed["format"];
1367
+ for (const keyword of SUBSCHEMA_KEYWORDS) {
1368
+ if (keyword in relaxed) relaxed[keyword] = relaxSchema(relaxed[keyword]);
1369
+ }
1370
+ for (const keyword of SUBSCHEMA_LIST_KEYWORDS) {
1371
+ const subschemas = relaxed[keyword];
1372
+ if (Array.isArray(subschemas)) relaxed[keyword] = subschemas.map(relaxSchema);
1373
+ }
1374
+ for (const keyword of SUBSCHEMA_MAP_KEYWORDS) {
1375
+ const subschemasByName = relaxed[keyword];
1376
+ if (typeof subschemasByName === "object" && subschemasByName !== null) {
1377
+ relaxed[keyword] = Object.fromEntries(
1378
+ Object.entries(subschemasByName).map(([name, subschema]) => [name, relaxSchema(subschema)])
1379
+ );
1380
+ }
1381
+ }
1382
+ const exclusiveBranches = relaxed["oneOf"];
1383
+ if (Array.isArray(exclusiveBranches)) {
1384
+ delete relaxed["oneOf"];
1385
+ const branches = exclusiveBranches.map(relaxSchema);
1386
+ if (Array.isArray(relaxed["anyOf"])) {
1387
+ const allOf = Array.isArray(relaxed["allOf"]) ? relaxed["allOf"] : [];
1388
+ relaxed["allOf"] = [...allOf, { anyOf: branches }];
1389
+ } else {
1390
+ relaxed["anyOf"] = branches;
1391
+ }
1392
+ }
1393
+ return relaxed;
1394
+ }
1322
1395
  function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
1323
1396
  if (typeof jsonSchema !== "object" || jsonSchema === null) {
1324
1397
  logger.warn(`[${toolName}] No valid JSON schema provided, using permissive schema`);
@@ -1346,6 +1419,18 @@ function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
1346
1419
 
1347
1420
  // libs/adapters/src/openapi/openapi.adapter.ts
1348
1421
  var RESERVED_KEYS2 = ["__proto__", "constructor", "prototype"];
1422
+ var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options", "trace"];
1423
+ function normalizeMethods(adapterName, optionName, methods) {
1424
+ return methods?.map((method) => {
1425
+ const lowerMethod = method.toLowerCase();
1426
+ if (!HTTP_METHODS.includes(lowerMethod)) {
1427
+ throw new Error(
1428
+ `[OpenAPI Adapter: ${adapterName}] generateOptions.${optionName} lists "${method}", which is not an HTTP method; use one of ${HTTP_METHODS.join(", ")}.`
1429
+ );
1430
+ }
1431
+ return lowerMethod;
1432
+ });
1433
+ }
1349
1434
  function createConsoleLogger(prefix) {
1350
1435
  const formatMessage = (level, msg) => `[${prefix}] ${level}: ${msg}`;
1351
1436
  return {
@@ -1364,9 +1449,14 @@ var OpenapiAdapter = class extends import_sdk5.DynamicAdapter {
1364
1449
  poller = null;
1365
1450
  updateCallbacks = /* @__PURE__ */ new Set();
1366
1451
  rebuildChain = Promise.resolve();
1452
+ methodFilters;
1367
1453
  constructor(options) {
1368
1454
  super();
1369
1455
  this.options = options;
1456
+ this.methodFilters = {
1457
+ includeMethods: normalizeMethods(options.name, "includeMethods", options.generateOptions?.includeMethods),
1458
+ excludeMethods: normalizeMethods(options.name, "excludeMethods", options.generateOptions?.excludeMethods)
1459
+ };
1370
1460
  this.logger = options.logger ?? createConsoleLogger(`openapi:${options.name}`);
1371
1461
  if (options.polling?.enabled && !("url" in options)) {
1372
1462
  throw new Error(
@@ -1390,6 +1480,8 @@ var OpenapiAdapter = class extends import_sdk5.DynamicAdapter {
1390
1480
  const generateOptions = this.options.generateOptions;
1391
1481
  let openapiTools = await this.generator.generateTools({
1392
1482
  ...generateOptions,
1483
+ ...this.methodFilters,
1484
+ filterFn: this.operationFilter(),
1393
1485
  preferredStatusCodes: generateOptions?.preferredStatusCodes ?? [200, 201, 202, 204],
1394
1486
  includeDeprecated: generateOptions?.includeDeprecated ?? false,
1395
1487
  includeAllResponses: generateOptions?.includeAllResponses ?? true,
@@ -1475,28 +1567,31 @@ var OpenapiAdapter = class extends import_sdk5.DynamicAdapter {
1475
1567
  resolveRefResolution() {
1476
1568
  return this.options.loadOptions?.refResolution ?? { allowedProtocols: [] };
1477
1569
  }
1570
+ /**
1571
+ * `generateOptions.filterFn`, and with `includeOperations` set, only the operations it names: the
1572
+ * generator lets an operation with no `operationId` through that list.
1573
+ */
1574
+ operationFilter() {
1575
+ const { includeOperations, filterFn } = this.options.generateOptions ?? {};
1576
+ if (!includeOperations) return filterFn;
1577
+ return (operation) => operation.operationId !== void 0 && includeOperations.includes(operation.operationId) && (filterFn?.(operation) ?? true);
1578
+ }
1478
1579
  async initializeGenerator() {
1479
- const refResolution = this.resolveRefResolution();
1580
+ const loadOptions = {
1581
+ ...this.options.loadOptions,
1582
+ baseUrl: this.options.baseUrl,
1583
+ validate: this.options.loadOptions?.validate ?? true,
1584
+ dereference: this.options.loadOptions?.dereference ?? true,
1585
+ // SECURITY: do not follow spec-URL redirects by default — a 3xx to an
1586
+ // internal target would otherwise be fetched. With mcp-from-openapi
1587
+ // ≥ 2.5.0 each hop is re-validated; opt in with loadOptions.followRedirects.
1588
+ followRedirects: this.options.loadOptions?.followRedirects ?? false,
1589
+ refResolution: this.resolveRefResolution()
1590
+ };
1480
1591
  if ("url" in this.options) {
1481
- return await import_mcp_from_openapi3.OpenAPIToolGenerator.fromURL(this.options.url, {
1482
- baseUrl: this.options.baseUrl,
1483
- validate: this.options.loadOptions?.validate ?? true,
1484
- dereference: this.options.loadOptions?.dereference ?? true,
1485
- headers: this.options.loadOptions?.headers,
1486
- timeout: this.options.loadOptions?.timeout,
1487
- // SECURITY: do not follow spec-URL redirects by default — a 3xx to an
1488
- // internal target would otherwise be fetched. With mcp-from-openapi
1489
- // ≥ 2.5.0 each hop is re-validated; opt in with loadOptions.followRedirects.
1490
- followRedirects: this.options.loadOptions?.followRedirects ?? false,
1491
- refResolution
1492
- });
1592
+ return await import_mcp_from_openapi3.OpenAPIToolGenerator.fromURL(this.options.url, loadOptions);
1493
1593
  } else if ("spec" in this.options) {
1494
- return await import_mcp_from_openapi3.OpenAPIToolGenerator.fromJSON(this.options.spec, {
1495
- baseUrl: this.options.baseUrl,
1496
- validate: this.options.loadOptions?.validate ?? true,
1497
- dereference: this.options.loadOptions?.dereference ?? true,
1498
- refResolution
1499
- });
1594
+ return await import_mcp_from_openapi3.OpenAPIToolGenerator.fromJSON(this.options.spec, loadOptions);
1500
1595
  } else {
1501
1596
  throw new Error("Either url or spec must be provided in OpenApiAdapterOptions");
1502
1597
  }
package/openapi/index.js CHANGED
@@ -1037,6 +1037,7 @@ function createOpenApiTool(openapiTool, options, logger) {
1037
1037
  const frontmcpValidation = validateFrontMcpExtension(metadata.frontmcp, openapiTool.name, logger);
1038
1038
  const frontmcpExt = frontmcpValidation.data;
1039
1039
  const schemaResult = getZodSchemaFromJsonSchema(openapiTool.inputSchema, openapiTool.name, logger);
1040
+ const argumentCheck = schemaResult.conversionFailed ? void 0 : getZodSchemaFromJsonSchema(toArgumentJsonSchema(openapiTool), openapiTool.name, logger);
1040
1041
  let wrappedOutputSchema;
1041
1042
  if (openapiTool.outputSchema !== void 0) {
1042
1043
  const baseOutputSchema = openapiTool.outputSchema ?? { type: "string" };
@@ -1054,11 +1055,15 @@ function createOpenApiTool(openapiTool, options, logger) {
1054
1055
  const toolMetadata = {
1055
1056
  id: openapiTool.name,
1056
1057
  name: openapiTool.name,
1058
+ ...openapiTool.title && { title: openapiTool.title },
1057
1059
  description: openapiTool.description,
1058
1060
  inputSchema: schemaResult.schema.shape || {},
1059
1061
  rawInputSchema: openapiTool.inputSchema,
1060
1062
  // Add output schema for tool/list to expose (only if not moved to description)
1061
- ...wrappedOutputSchema && { rawOutputSchema: wrappedOutputSchema }
1063
+ ...wrappedOutputSchema && { rawOutputSchema: wrappedOutputSchema },
1064
+ ...openapiTool.annotations && { annotations: { ...openapiTool.annotations } },
1065
+ ...openapiTool.icons?.length && { icons: openapiTool.icons.map((icon) => ({ ...icon })) },
1066
+ ...openapiTool._meta && { _meta: { ...openapiTool._meta } }
1062
1067
  };
1063
1068
  if (schemaResult.conversionFailed) {
1064
1069
  toolMetadata["_schemaConversionFailed"] = true;
@@ -1066,7 +1071,10 @@ function createOpenApiTool(openapiTool, options, logger) {
1066
1071
  }
1067
1072
  if (frontmcpExt) {
1068
1073
  if (frontmcpExt.annotations) {
1069
- toolMetadata["annotations"] = { ...frontmcpExt.annotations };
1074
+ toolMetadata["annotations"] = {
1075
+ ...toolMetadata["annotations"] || {},
1076
+ ...frontmcpExt.annotations
1077
+ };
1070
1078
  }
1071
1079
  if (frontmcpExt.tags) {
1072
1080
  toolMetadata["tags"] = [...frontmcpExt.tags];
@@ -1106,6 +1114,13 @@ function createOpenApiTool(openapiTool, options, logger) {
1106
1114
  }
1107
1115
  return (0, import_sdk4.tool)(toolMetadata)(async (input, toolCtx) => {
1108
1116
  const ctx = toolCtx.context;
1117
+ if (argumentCheck && !argumentCheck.conversionFailed) {
1118
+ const validation = argumentCheck.schema.safeParse(input);
1119
+ if (!validation.success) {
1120
+ const problems = validation.error.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`);
1121
+ throw new import_sdk4.InvalidInputError(`Invalid arguments for tool '${openapiTool.name}': ${problems.join("; ")}`);
1122
+ }
1123
+ }
1109
1124
  const transformContext = {
1110
1125
  ctx,
1111
1126
  env: process.env,
@@ -1319,6 +1334,64 @@ async function injectTransformedValues(input, transforms, ctx) {
1319
1334
  }
1320
1335
  return result;
1321
1336
  }
1337
+ var SUBSCHEMA_KEYWORDS = [
1338
+ "items",
1339
+ "additionalProperties",
1340
+ "not",
1341
+ "if",
1342
+ "then",
1343
+ "else",
1344
+ "contains",
1345
+ "propertyNames",
1346
+ "unevaluatedItems",
1347
+ "unevaluatedProperties"
1348
+ ];
1349
+ var SUBSCHEMA_LIST_KEYWORDS = ["allOf", "anyOf", "prefixItems"];
1350
+ var SUBSCHEMA_MAP_KEYWORDS = ["properties", "patternProperties", "dependentSchemas", "$defs", "definitions"];
1351
+ function toArgumentJsonSchema(openapiTool) {
1352
+ const argumentSchema = relaxSchema(openapiTool.inputSchema);
1353
+ const credentialKeys = new Set(
1354
+ openapiTool.mapper.filter((mapper) => mapper.security).map((mapper) => mapper.inputKey)
1355
+ );
1356
+ const required = argumentSchema["required"];
1357
+ if (Array.isArray(required)) {
1358
+ argumentSchema["required"] = required.filter((key) => !credentialKeys.has(key));
1359
+ }
1360
+ return argumentSchema;
1361
+ }
1362
+ function relaxSchema(schema) {
1363
+ if (Array.isArray(schema)) return schema.map(relaxSchema);
1364
+ if (typeof schema !== "object" || schema === null) return schema;
1365
+ const relaxed = { ...schema };
1366
+ delete relaxed["format"];
1367
+ for (const keyword of SUBSCHEMA_KEYWORDS) {
1368
+ if (keyword in relaxed) relaxed[keyword] = relaxSchema(relaxed[keyword]);
1369
+ }
1370
+ for (const keyword of SUBSCHEMA_LIST_KEYWORDS) {
1371
+ const subschemas = relaxed[keyword];
1372
+ if (Array.isArray(subschemas)) relaxed[keyword] = subschemas.map(relaxSchema);
1373
+ }
1374
+ for (const keyword of SUBSCHEMA_MAP_KEYWORDS) {
1375
+ const subschemasByName = relaxed[keyword];
1376
+ if (typeof subschemasByName === "object" && subschemasByName !== null) {
1377
+ relaxed[keyword] = Object.fromEntries(
1378
+ Object.entries(subschemasByName).map(([name, subschema]) => [name, relaxSchema(subschema)])
1379
+ );
1380
+ }
1381
+ }
1382
+ const exclusiveBranches = relaxed["oneOf"];
1383
+ if (Array.isArray(exclusiveBranches)) {
1384
+ delete relaxed["oneOf"];
1385
+ const branches = exclusiveBranches.map(relaxSchema);
1386
+ if (Array.isArray(relaxed["anyOf"])) {
1387
+ const allOf = Array.isArray(relaxed["allOf"]) ? relaxed["allOf"] : [];
1388
+ relaxed["allOf"] = [...allOf, { anyOf: branches }];
1389
+ } else {
1390
+ relaxed["anyOf"] = branches;
1391
+ }
1392
+ }
1393
+ return relaxed;
1394
+ }
1322
1395
  function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
1323
1396
  if (typeof jsonSchema !== "object" || jsonSchema === null) {
1324
1397
  logger.warn(`[${toolName}] No valid JSON schema provided, using permissive schema`);
@@ -1346,6 +1419,18 @@ function getZodSchemaFromJsonSchema(jsonSchema, toolName, logger) {
1346
1419
 
1347
1420
  // libs/adapters/src/openapi/openapi.adapter.ts
1348
1421
  var RESERVED_KEYS2 = ["__proto__", "constructor", "prototype"];
1422
+ var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options", "trace"];
1423
+ function normalizeMethods(adapterName, optionName, methods) {
1424
+ return methods?.map((method) => {
1425
+ const lowerMethod = method.toLowerCase();
1426
+ if (!HTTP_METHODS.includes(lowerMethod)) {
1427
+ throw new Error(
1428
+ `[OpenAPI Adapter: ${adapterName}] generateOptions.${optionName} lists "${method}", which is not an HTTP method; use one of ${HTTP_METHODS.join(", ")}.`
1429
+ );
1430
+ }
1431
+ return lowerMethod;
1432
+ });
1433
+ }
1349
1434
  function createConsoleLogger(prefix) {
1350
1435
  const formatMessage = (level, msg) => `[${prefix}] ${level}: ${msg}`;
1351
1436
  return {
@@ -1364,9 +1449,14 @@ var OpenapiAdapter = class extends import_sdk5.DynamicAdapter {
1364
1449
  poller = null;
1365
1450
  updateCallbacks = /* @__PURE__ */ new Set();
1366
1451
  rebuildChain = Promise.resolve();
1452
+ methodFilters;
1367
1453
  constructor(options) {
1368
1454
  super();
1369
1455
  this.options = options;
1456
+ this.methodFilters = {
1457
+ includeMethods: normalizeMethods(options.name, "includeMethods", options.generateOptions?.includeMethods),
1458
+ excludeMethods: normalizeMethods(options.name, "excludeMethods", options.generateOptions?.excludeMethods)
1459
+ };
1370
1460
  this.logger = options.logger ?? createConsoleLogger(`openapi:${options.name}`);
1371
1461
  if (options.polling?.enabled && !("url" in options)) {
1372
1462
  throw new Error(
@@ -1390,6 +1480,8 @@ var OpenapiAdapter = class extends import_sdk5.DynamicAdapter {
1390
1480
  const generateOptions = this.options.generateOptions;
1391
1481
  let openapiTools = await this.generator.generateTools({
1392
1482
  ...generateOptions,
1483
+ ...this.methodFilters,
1484
+ filterFn: this.operationFilter(),
1393
1485
  preferredStatusCodes: generateOptions?.preferredStatusCodes ?? [200, 201, 202, 204],
1394
1486
  includeDeprecated: generateOptions?.includeDeprecated ?? false,
1395
1487
  includeAllResponses: generateOptions?.includeAllResponses ?? true,
@@ -1475,28 +1567,31 @@ var OpenapiAdapter = class extends import_sdk5.DynamicAdapter {
1475
1567
  resolveRefResolution() {
1476
1568
  return this.options.loadOptions?.refResolution ?? { allowedProtocols: [] };
1477
1569
  }
1570
+ /**
1571
+ * `generateOptions.filterFn`, and with `includeOperations` set, only the operations it names: the
1572
+ * generator lets an operation with no `operationId` through that list.
1573
+ */
1574
+ operationFilter() {
1575
+ const { includeOperations, filterFn } = this.options.generateOptions ?? {};
1576
+ if (!includeOperations) return filterFn;
1577
+ return (operation) => operation.operationId !== void 0 && includeOperations.includes(operation.operationId) && (filterFn?.(operation) ?? true);
1578
+ }
1478
1579
  async initializeGenerator() {
1479
- const refResolution = this.resolveRefResolution();
1580
+ const loadOptions = {
1581
+ ...this.options.loadOptions,
1582
+ baseUrl: this.options.baseUrl,
1583
+ validate: this.options.loadOptions?.validate ?? true,
1584
+ dereference: this.options.loadOptions?.dereference ?? true,
1585
+ // SECURITY: do not follow spec-URL redirects by default — a 3xx to an
1586
+ // internal target would otherwise be fetched. With mcp-from-openapi
1587
+ // ≥ 2.5.0 each hop is re-validated; opt in with loadOptions.followRedirects.
1588
+ followRedirects: this.options.loadOptions?.followRedirects ?? false,
1589
+ refResolution: this.resolveRefResolution()
1590
+ };
1480
1591
  if ("url" in this.options) {
1481
- return await import_mcp_from_openapi3.OpenAPIToolGenerator.fromURL(this.options.url, {
1482
- baseUrl: this.options.baseUrl,
1483
- validate: this.options.loadOptions?.validate ?? true,
1484
- dereference: this.options.loadOptions?.dereference ?? true,
1485
- headers: this.options.loadOptions?.headers,
1486
- timeout: this.options.loadOptions?.timeout,
1487
- // SECURITY: do not follow spec-URL redirects by default — a 3xx to an
1488
- // internal target would otherwise be fetched. With mcp-from-openapi
1489
- // ≥ 2.5.0 each hop is re-validated; opt in with loadOptions.followRedirects.
1490
- followRedirects: this.options.loadOptions?.followRedirects ?? false,
1491
- refResolution
1492
- });
1592
+ return await import_mcp_from_openapi3.OpenAPIToolGenerator.fromURL(this.options.url, loadOptions);
1493
1593
  } else if ("spec" in this.options) {
1494
- return await import_mcp_from_openapi3.OpenAPIToolGenerator.fromJSON(this.options.spec, {
1495
- baseUrl: this.options.baseUrl,
1496
- validate: this.options.loadOptions?.validate ?? true,
1497
- dereference: this.options.loadOptions?.dereference ?? true,
1498
- refResolution
1499
- });
1594
+ return await import_mcp_from_openapi3.OpenAPIToolGenerator.fromJSON(this.options.spec, loadOptions);
1500
1595
  } else {
1501
1596
  throw new Error("Either url or spec must be provided in OpenApiAdapterOptions");
1502
1597
  }
@@ -7,6 +7,7 @@ export default class OpenapiAdapter extends DynamicAdapter<OpenApiAdapterOptions
7
7
  private poller;
8
8
  private updateCallbacks;
9
9
  private rebuildChain;
10
+ private readonly methodFilters;
10
11
  constructor(options: OpenApiAdapterOptions);
11
12
  /**
12
13
  * Receive the SDK logger. Called by the SDK before fetch().
@@ -29,6 +30,11 @@ export default class OpenapiAdapter extends DynamicAdapter<OpenApiAdapterOptions
29
30
  * @private
30
31
  */
31
32
  private resolveRefResolution;
33
+ /**
34
+ * `generateOptions.filterFn`, and with `includeOperations` set, only the operations it names: the
35
+ * generator lets an operation with no `operationId` through that list.
36
+ */
37
+ private operationFilter;
32
38
  private initializeGenerator;
33
39
  /**
34
40
  * Apply description mode to generate description from summary/description
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.adapter.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.adapter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,cAAc,EAAE,KAAK,uBAAuB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAS3G,OAAO,EAIL,KAAK,qBAAqB,EAQ3B,MAAM,iBAAiB,CAAC;AAwBzB,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,cAAc,CAAC,qBAAqB,CAAC;IAC/E,OAAO,CAAC,SAAS,CAAC,CAAuB;IACzC,OAAO,CAAC,MAAM,CAAiB;IACxB,OAAO,EAAE,qBAAqB,CAAC;IACtC,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,eAAe,CAA0D;IACjF,OAAO,CAAC,YAAY,CAAoC;gBAE5C,OAAO,EAAE,qBAAqB;IAc1C;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAIjC,KAAK,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAoH/C;;;OAGG;IACH;;;;;;;;;;OAUG;IACH,OAAO,CAAC,oBAAoB;YAId,mBAAmB;IA4BjC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IA0C5B;;;OAGG;IACH,OAAO,CAAC,cAAc;IAMtB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IA6D7B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAsC3B;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAwBhC;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAoD5B;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IA0D7B;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IA0C7B;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IAkBnC;;;OAGG;IACH,OAAO,CAAC,4BAA4B;IAoBpC;;;;OAIG;YACW,wBAAwB;IAiFtC;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IAgBlC;;;OAGG;IACH,OAAO,CAAC,0CAA0C;IA+BlD;;;OAGG;IACH,OAAO,CAAC,2CAA2C;IAyCnD;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAuB7B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,GAAG,MAAM,IAAI;IAO3E;;;;OAIG;IACH,YAAY,IAAI,IAAI;IA6CpB;;OAEG;IACH,WAAW,IAAI,IAAI;CAOpB"}
1
+ {"version":3,"file":"openapi.adapter.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.adapter.ts"],"names":[],"mappings":"AAOA,OAAO,EAAW,cAAc,EAAE,KAAK,uBAAuB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAS3G,OAAO,EAIL,KAAK,qBAAqB,EAQ3B,MAAM,iBAAiB,CAAC;AA6CzB,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,cAAc,CAAC,qBAAqB,CAAC;IAC/E,OAAO,CAAC,SAAS,CAAC,CAAuB;IACzC,OAAO,CAAC,MAAM,CAAiB;IACxB,OAAO,EAAE,qBAAqB,CAAC;IACtC,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,eAAe,CAA0D;IACjF,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmE;gBAErF,OAAO,EAAE,qBAAqB;IAkB1C;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAIjC,KAAK,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAsH/C;;;OAGG;IACH;;;;;;;;;;OAUG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;;OAGG;IACH,OAAO,CAAC,eAAe;YAST,mBAAmB;IAuBjC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IA0C5B;;;OAGG;IACH,OAAO,CAAC,cAAc;IAMtB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IA6D7B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAsC3B;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAwBhC;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAoD5B;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IA0D7B;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IA0C7B;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IAkBnC;;;OAGG;IACH,OAAO,CAAC,4BAA4B;IAoBpC;;;;OAIG;YACW,wBAAwB;IAiFtC;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IAgBlC;;;OAGG;IACH,OAAO,CAAC,0CAA0C;IA+BlD;;;OAGG;IACH,OAAO,CAAC,2CAA2C;IAyCnD;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAuB7B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,GAAG,MAAM,IAAI;IAO3E;;;;OAIG;IACH,YAAY,IAAI,IAAI;IA6CpB;;OAEG;IACH,WAAW,IAAI,IAAI;CAOpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.tool.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAK1D,OAAO,KAAK,EAIV,qBAAqB,EAEtB,MAAM,iBAAiB,CAAC;AAMzB;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,cAAc,cAwTpH"}
1
+ {"version":3,"file":"openapi.tool.d.ts","sourceRoot":"","sources":["../../src/openapi/openapi.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAA2B,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAK7E,OAAO,KAAK,EAIV,qBAAqB,EAEtB,MAAM,iBAAiB,CAAC;AAMzB;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,cAAc,cA2UpH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/adapters",
3
- "version": "1.9.1-rc.3",
3
+ "version": "1.9.2",
4
4
  "description": "Adapters for the FrontMCP framework",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -64,15 +64,15 @@
64
64
  "node": ">=24.0.0"
65
65
  },
66
66
  "dependencies": {
67
- "@frontmcp/auth": "1.9.1-rc.3",
68
- "@frontmcp/di": "1.9.1-rc.3",
69
- "@frontmcp/sdk": "1.9.1-rc.3",
70
- "@frontmcp/utils": "1.9.1-rc.3",
67
+ "@frontmcp/auth": "1.9.2",
68
+ "@frontmcp/di": "1.9.2",
69
+ "@frontmcp/sdk": "1.9.2",
70
+ "@frontmcp/utils": "1.9.2",
71
71
  "js-yaml": "^4.1.0",
72
72
  "mcp-from-openapi": "2.8.0",
73
73
  "openapi-types": "^12.1.3"
74
74
  },
75
75
  "peerDependencies": {
76
- "@frontmcp/lazy-zod": "1.9.1-rc.3"
76
+ "@frontmcp/lazy-zod": "1.9.2"
77
77
  }
78
78
  }
package/skills/index.js CHANGED
@@ -942,7 +942,7 @@ var SaasPullSource = class {
942
942
  bundle = await this.loadCache();
943
943
  if (!bundle) {
944
944
  throw new Error(
945
- `[saas-source] initial pull failed and no cached bundle is available at ${this.cachePath()}: ${e.message}`,
945
+ `[saas-source] initial pull failed and no cached bundle is available ${this.cacheLocation()}: ${e.message}`,
946
946
  { cause: e }
947
947
  );
948
948
  }
@@ -1088,6 +1088,10 @@ var SaasPullSource = class {
1088
1088
  trimmed.startsWith("{") || trimmed.startsWith("[") ? { kind: "json", content: body } : { kind: "yaml", content: body }
1089
1089
  );
1090
1090
  }
1091
+ /** Where the last-good bundle is kept, for messages: an injected store (a Worker's KV) has no file path. */
1092
+ cacheLocation() {
1093
+ return this.deps.cache ? "in the injected bundle cache" : `at ${this.cachePath()}`;
1094
+ }
1091
1095
  cachePath() {
1092
1096
  const dir = this.cacheDir ?? DEFAULT_CACHE_DIR;
1093
1097
  return (0, import_utils.pathResolve)(dir, `${this.options.expectedAudience.replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`);
@@ -72,6 +72,8 @@ export declare class SaasPullSource implements SkillBundleSource {
72
72
  */
73
73
  private verifyPullToken;
74
74
  private fetchOnce;
75
+ /** Where the last-good bundle is kept, for messages: an injected store (a Worker's KV) has no file path. */
76
+ private cacheLocation;
75
77
  private cachePath;
76
78
  private persistCache;
77
79
  private loadCache;
@@ -1 +1 @@
1
- {"version":3,"file":"saas-pull.source.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/saas-pull.source.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAMjH;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;gBACvC,MAAM,EAAE,MAAM;CAI3B;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,cAAe,YAAW,iBAAiB;IAUpD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuB;gBAGhC,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,cAAc,EACtB,IAAI,GAAE,gBAAqB;IAKxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC5B,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI;IAO9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,OAAO,CAAC,YAAY;IAcpB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;YAatC,QAAQ;cAyBN,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAoBhH;;;;OAIG;YACW,eAAe;YA2Cf,SAAS;IAkBvB,OAAO,CAAC,SAAS;YAKH,YAAY;YAeZ,SAAS;IAkBvB,OAAO,CAAC,MAAM;CASf"}
1
+ {"version":3,"file":"saas-pull.source.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/saas-pull.source.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAMjH;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;gBACvC,MAAM,EAAE,MAAM;CAI3B;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,cAAe,YAAW,iBAAiB;IAUpD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuB;gBAGhC,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,cAAc,EACtB,IAAI,GAAE,gBAAqB;IAKxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC5B,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI;IAO9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,OAAO,CAAC,YAAY;IAcpB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;YAatC,QAAQ;cAyBN,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAoBhH;;;;OAIG;YACW,eAAe;YA2Cf,SAAS;IAkBvB,4GAA4G;IAC5G,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,SAAS;YAKH,YAAY;YAeZ,SAAS;IAkBvB,OAAO,CAAC,MAAM;CASf"}