@frontmcp/adapters 1.8.2 → 1.8.4

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
@@ -183,13 +183,17 @@ var OpenApiSpecPoller = class {
183
183
  };
184
184
 
185
185
  // libs/adapters/src/openapi/openapi.security.ts
186
- import { SecurityResolver, createSecurityContext } from "mcp-from-openapi";
186
+ import {
187
+ createSecurityContext,
188
+ SecurityResolver
189
+ } from "mcp-from-openapi";
190
+ import { PublicMcpError } from "@frontmcp/sdk";
187
191
  async function createSecurityContextFromAuth(tool2, ctx, options) {
188
192
  if (options.securityResolver) {
189
193
  return await options.securityResolver(tool2, ctx);
190
194
  }
191
195
  if (options.authProviderMapper) {
192
- const context = createSecurityContext({});
196
+ const context = {};
193
197
  const securitySchemes = /* @__PURE__ */ new Set();
194
198
  for (const mapper of tool2.mapper) {
195
199
  if (mapper.security?.scheme) {
@@ -238,26 +242,42 @@ async function createSecurityContextFromAuth(tool2, ctx, options) {
238
242
  throw err;
239
243
  }
240
244
  const errorMessage = err instanceof Error ? err.message : String(err);
241
- throw new Error(`authProviderMapper['${scheme}'] threw an error: ${errorMessage}`);
245
+ throw new Error(`authProviderMapper['${scheme}'] threw an error: ${errorMessage}`, { cause: err });
242
246
  }
243
247
  }
244
248
  }
249
+ if (hasStaticAuth(options)) {
250
+ return createSecurityContext({ ...options.staticAuth, ...context });
251
+ }
245
252
  const hasAnyAuth = context.jwt || context.apiKey || context.basic || context.oauth2Token;
246
- const authToken = ctx.authInfo?.token;
247
- if (!hasAnyAuth && authToken) {
248
- if (typeof authToken !== "string") {
249
- throw new Error(`authInfo.token must be a string, but got: ${typeof authToken}`);
253
+ if (!hasAnyAuth && options.passthroughCallerToken === true) {
254
+ const callerToken = getCallerToken(ctx);
255
+ if (callerToken) {
256
+ context.jwt = callerToken;
250
257
  }
251
- context.jwt = authToken;
252
258
  }
253
- return context;
259
+ return createSecurityContext(context);
254
260
  }
255
- if (options.staticAuth) {
261
+ if (hasStaticAuth(options)) {
256
262
  return createSecurityContext(options.staticAuth);
257
263
  }
258
- return createSecurityContext({
259
- jwt: ctx.authInfo?.token
260
- });
264
+ if (options.passthroughCallerToken === true) {
265
+ return createSecurityContext({ jwt: getCallerToken(ctx) });
266
+ }
267
+ return createSecurityContext({});
268
+ }
269
+ function hasStaticAuth(options) {
270
+ return !!options.staticAuth && Object.keys(options.staticAuth).length > 0;
271
+ }
272
+ function getCallerToken(ctx) {
273
+ const authToken = ctx.authInfo?.token;
274
+ if (authToken === void 0 || authToken === null || authToken === "") {
275
+ return void 0;
276
+ }
277
+ if (typeof authToken !== "string") {
278
+ throw new Error(`authInfo.token must be a string, but got: ${typeof authToken}`);
279
+ }
280
+ return authToken;
261
281
  }
262
282
  function extractSecuritySchemes(tools) {
263
283
  const schemes = /* @__PURE__ */ new Set();
@@ -270,19 +290,158 @@ function extractSecuritySchemes(tools) {
270
290
  }
271
291
  return schemes;
272
292
  }
293
+ function describeSecuritySchemes(tools) {
294
+ const schemes = /* @__PURE__ */ new Map();
295
+ for (const tool2 of tools) {
296
+ for (const mapper of tool2.mapper) {
297
+ if (mapper.security?.scheme && !schemes.has(mapper.security.scheme)) {
298
+ schemes.set(mapper.security.scheme, mapper.security);
299
+ }
300
+ }
301
+ }
302
+ return schemes;
303
+ }
304
+ function schemeParametersOf(tools) {
305
+ const parameters = /* @__PURE__ */ new Map();
306
+ for (const tool2 of tools) {
307
+ for (const mapper of tool2.mapper) {
308
+ const scheme = mapper.security?.scheme;
309
+ if (scheme && !parameters.has(scheme)) parameters.set(scheme, mapper);
310
+ }
311
+ }
312
+ return parameters;
313
+ }
314
+ function headerCredentialSource(parameter, options) {
315
+ if (!parameter) return void 0;
316
+ const staticHeaders = new Headers(options.additionalHeaders ?? {});
317
+ if (carriesSchemeParameter(parameter, new URL("http://startup.invalid/"), staticHeaders)) return "static";
318
+ if (options.headersMapper && (parameter.type === "header" || parameter.type === "cookie")) return "per-request";
319
+ return void 0;
320
+ }
321
+ function isBearerScheme(security) {
322
+ if (!security || security.type !== "http") return false;
323
+ return (security.httpScheme ?? "bearer").toLowerCase() === "bearer";
324
+ }
325
+ function credentialFieldOf(security) {
326
+ if (security?.type === "apiKey") return "apiKey";
327
+ if (security?.type === "oauth2" || security?.type === "openIdConnect") return "oauth2Token";
328
+ if (security?.type === "http" && security.httpScheme?.toLowerCase() === "basic") return "basic";
329
+ return "jwt";
330
+ }
331
+ function formatMissingSecurityMappingsError(adapterName, tools, missingMappings) {
332
+ const schemes = describeSecuritySchemes(tools);
333
+ const fieldOf = (scheme) => credentialFieldOf(schemes.get(scheme));
334
+ const firstField = fieldOf(missingMappings[0] ?? "");
335
+ const bearerSchemes = missingMappings.filter((scheme) => isBearerScheme(schemes.get(scheme)));
336
+ const lines = [
337
+ `[OpenAPI Adapter: ${adapterName}] Invalid security configuration.`,
338
+ `Missing auth provider mappings for security schemes: ${missingMappings.join(", ")}`,
339
+ "",
340
+ "Your OpenAPI spec requires these security schemes, but no credential option covers them.",
341
+ "",
342
+ "Add one of the following to your adapter configuration:",
343
+ "",
344
+ "1. authProviderMapper (recommended): a function per scheme that returns the credential issued for the API:",
345
+ " authProviderMapper: {",
346
+ ...missingMappings.map((scheme) => ` '${scheme}': (ctx) => getApiCredential(ctx), // the ${fieldOf(scheme)}`),
347
+ " }",
348
+ "",
349
+ "2. securityResolver:",
350
+ ` securityResolver: async (tool, ctx) => ({ ${firstField}: await getApiCredential(ctx) })`,
351
+ "",
352
+ "3. staticAuth (one credential for every caller):",
353
+ ` staticAuth: { ${firstField}: process.env.API_CREDENTIAL }`
354
+ ];
355
+ if (bearerSchemes.length > 0) {
356
+ lines.push(
357
+ "",
358
+ `4. passthroughCallerToken: true, for the HTTP bearer schemes (${bearerSchemes.join(", ")}) only, and only if the API`,
359
+ " accepts the MCP client's own token (same issuer and audience). It sends nothing for other schemes."
360
+ );
361
+ }
362
+ return lines.join("\n");
363
+ }
364
+ function authenticationRequiredError(tool2) {
365
+ const required = requiredSecurityOf(tool2);
366
+ const schemes = [...new Map(required.map((security) => [security.scheme, security])).values()];
367
+ const schemesStr = schemes.map((security) => `${security.scheme} (${security.type})`).join(", ") || "unknown";
368
+ const first = schemes[0];
369
+ const firstScheme = first?.scheme ?? "BearerAuth";
370
+ const field = credentialFieldOf(first);
371
+ const bearerSchemes = schemes.filter(isBearerScheme).map((security) => security.scheme);
372
+ const solutions = [
373
+ ` 1. Add authProviderMapper: { '${firstScheme}': (ctx) => getApiCredential(ctx) }, returning the ${field} issued for the API`,
374
+ ` 2. Add securityResolver: async (tool, ctx) => ({ ${field}: await getApiCredential(ctx) })`,
375
+ ` 3. Add staticAuth: { ${field}: process.env.API_CREDENTIAL }`
376
+ ];
377
+ if (bearerSchemes.length > 0) {
378
+ solutions.push(
379
+ ` 4. Set passthroughCallerToken: true, only if the API accepts the MCP client's own token (it fills ${bearerSchemes.join(", ")} only)`
380
+ );
381
+ }
382
+ return new Error(
383
+ `Authentication required for tool '${tool2.name}': no credential for its security schemes.
384
+ Required security schemes: ${schemesStr}
385
+ Solutions:
386
+ ` + solutions.join("\n")
387
+ );
388
+ }
389
+ function requiredSecurityOf(tool2) {
390
+ return tool2.mapper.flatMap((mapper) => mapper.security && mapper.required === true ? [mapper.security] : []);
391
+ }
392
+ function authorizationSchemeOf(security) {
393
+ if (security?.type === "http") return (security.httpScheme ?? "bearer").toLowerCase();
394
+ if (security?.type === "oauth2" || security?.type === "openIdConnect") return "bearer";
395
+ return void 0;
396
+ }
397
+ function carriesSchemeParameter(mapper, url, headers) {
398
+ if (mapper.type === "query") return !!url.searchParams.get(mapper.key);
399
+ if (mapper.type === "cookie") {
400
+ return (headers.get("cookie") ?? "").split(";").some((pair) => {
401
+ const [name, ...value2] = pair.trim().split("=");
402
+ return name === mapper.key && value2.join("=") !== "";
403
+ });
404
+ }
405
+ if (mapper.type !== "header") return false;
406
+ const value = headers.get(mapper.key)?.trim();
407
+ if (!value) return false;
408
+ const scheme = authorizationSchemeOf(mapper.security);
409
+ if (!scheme) return true;
410
+ const space = value.indexOf(" ");
411
+ return space > 0 && value.slice(0, space).toLowerCase() === scheme && value.slice(space + 1).trim() !== "";
412
+ }
413
+ function assertRequestHasCredential(tool2, url, headers) {
414
+ const securityMappers = tool2.mapper.filter((mapper) => mapper.security && mapper.required === true);
415
+ if (securityMappers.length === 0) return;
416
+ const parsedUrl = new URL(url);
417
+ if (!securityMappers.some((mapper) => carriesSchemeParameter(mapper, parsedUrl, headers))) {
418
+ throw authenticationRequiredError(tool2);
419
+ }
420
+ }
273
421
  function validateSecurityConfiguration(tools, options) {
422
+ const result = validateCredentialSources(tools, options);
423
+ const schemesInInput = options.securitySchemesInInput ?? [];
424
+ if (options.generateOptions?.includeSecurityInInput !== true && schemesInInput.length > 0) {
425
+ result.securityRiskScore = "high";
426
+ result.warnings.push(
427
+ `SECURITY WARNING: securitySchemesInInput is enabled. The model provides the credential for security schemes ${schemesInInput.join(", ")} in tool inputs, used when the server supplies none. Credentials may be logged or exposed, and the model chooses whose account a call uses.`
428
+ );
429
+ }
430
+ return result;
431
+ }
432
+ function validateCredentialSources(tools, options) {
274
433
  const result = {
275
434
  valid: true,
276
435
  missingMappings: [],
277
436
  warnings: [],
278
437
  securityRiskScore: "low"
279
438
  };
280
- const securitySchemes = extractSecuritySchemes(tools);
439
+ const securitySchemes = describeSecuritySchemes(tools);
281
440
  const includeSecurityInInput = options.generateOptions?.includeSecurityInInput ?? false;
282
441
  if (includeSecurityInInput) {
283
442
  result.securityRiskScore = "high";
284
443
  result.warnings.push(
285
- "SECURITY WARNING: includeSecurityInInput is enabled. Users will provide authentication directly in tool inputs. This increases security risk as credentials may be logged or exposed."
444
+ "SECURITY WARNING: includeSecurityInInput is enabled. Users will provide authentication directly in tool inputs (used for a scheme when the server supplies none). This increases security risk as credentials may be logged or exposed."
286
445
  );
287
446
  return result;
288
447
  }
@@ -293,28 +452,35 @@ function validateSecurityConfiguration(tools, options) {
293
452
  );
294
453
  return result;
295
454
  }
296
- if (options.staticAuth && Object.keys(options.staticAuth).length > 0) {
455
+ if (hasStaticAuth(options)) {
297
456
  result.securityRiskScore = "medium";
298
457
  result.warnings.push(
299
458
  "SECURITY INFO: Using staticAuth with hardcoded credentials. Ensure credentials are stored securely (environment variables, secrets manager)."
300
459
  );
301
- return result;
460
+ return withPassthroughRisk(result, securitySchemes, options);
302
461
  }
303
462
  const schemesInInput = new Set(options.securitySchemesInInput || []);
304
463
  if (options.authProviderMapper || schemesInInput.size > 0) {
305
- result.securityRiskScore = schemesInInput.size > 0 ? "medium" : "low";
306
- if (schemesInInput.size > 0) {
307
- result.warnings.push(
308
- `INFO: Per-scheme security control enabled. Schemes in input: ${Array.from(schemesInInput).join(", ")}`
309
- );
310
- }
311
- for (const scheme of securitySchemes) {
464
+ result.securityRiskScore = "low";
465
+ const passthroughFallback = [];
466
+ const perRequestHeaders = [];
467
+ const schemeParameters = schemeParametersOf(tools);
468
+ for (const [scheme, security] of securitySchemes) {
312
469
  if (schemesInInput.has(scheme)) {
313
470
  continue;
314
471
  }
315
472
  if (!options.authProviderMapper?.[scheme]) {
316
- result.valid = false;
317
- result.missingMappings.push(scheme);
473
+ const headerSource = headerCredentialSource(schemeParameters.get(scheme), options);
474
+ if (headerSource === "static") {
475
+ continue;
476
+ } else if (headerSource === "per-request") {
477
+ perRequestHeaders.push(scheme);
478
+ } else if (options.passthroughCallerToken === true && isBearerScheme(security)) {
479
+ passthroughFallback.push(scheme);
480
+ } else {
481
+ result.valid = false;
482
+ result.missingMappings.push(scheme);
483
+ }
318
484
  }
319
485
  }
320
486
  if (!result.valid) {
@@ -322,42 +488,108 @@ function validateSecurityConfiguration(tools, options) {
322
488
  `ERROR: Missing auth provider mappings for security schemes: ${result.missingMappings.join(", ")}`
323
489
  );
324
490
  }
491
+ if (perRequestHeaders.length > 0) {
492
+ result.warnings.push(
493
+ `INFO: Security schemes with no authProviderMapper entry (${perRequestHeaders.join(", ")}) take their credential from headersMapper. An operation that requires only them is refused when headersMapper sets none.`
494
+ );
495
+ }
496
+ withPassthroughRisk(result, securitySchemes, options);
497
+ if (passthroughFallback.length > 0) {
498
+ result.warnings.push(
499
+ `SECURITY WARNING: Security schemes with no authProviderMapper entry (${passthroughFallback.join(", ")}) get the MCP client's own token, because passthroughCallerToken is enabled.`
500
+ );
501
+ }
325
502
  return result;
326
503
  }
327
- if (securitySchemes.size > 0) {
504
+ if (securitySchemes.size > 0 && options.passthroughCallerToken !== true) {
505
+ const schemesStr = Array.from(securitySchemes.keys()).join(", ");
328
506
  result.securityRiskScore = "medium";
329
507
  result.warnings.push(
330
- `INFO: No auth configuration provided. Using default ctx.authInfo.token for all security schemes: ${Array.from(
331
- securitySchemes
332
- ).join(", ")}`
508
+ `SECURITY WARNING: No auth configuration provided, so the adapter has no credentials for the API (security schemes: ${schemesStr}). Operations that require authentication fail unless additionalHeaders or headersMapper sets their credential. The MCP client's own token is not forwarded: configure authProviderMapper, securityResolver or staticAuth, or set passthroughCallerToken: true if the API is meant to receive the caller's MCP token.`
333
509
  );
510
+ }
511
+ const unfilled = [...securitySchemes].filter(([, security]) => !isBearerScheme(security)).map(([scheme]) => scheme);
512
+ if (options.passthroughCallerToken === true && unfilled.length > 0) {
513
+ result.securityRiskScore = "medium";
334
514
  result.warnings.push(
335
- "RECOMMENDATION: For multiple auth providers, use authProviderMapper or securityResolver to map each security scheme to the correct auth provider."
515
+ `SECURITY WARNING: The adapter has no credentials for the API for security schemes: ${unfilled.join(", ")}. passthroughCallerToken sends only a bearer token, which these schemes don't use, so operations that require only them fail unless additionalHeaders or headersMapper sets their credential. Configure authProviderMapper, securityResolver or staticAuth for them.`
336
516
  );
337
517
  }
518
+ return withPassthroughRisk(result, securitySchemes, options);
519
+ }
520
+ function canPassThroughCallerToken(options) {
521
+ return options.passthroughCallerToken === true && !options.securityResolver && !hasStaticAuth(options);
522
+ }
523
+ function withPassthroughRisk(result, securitySchemes, options) {
524
+ const bearerSchemes = [...securitySchemes].filter(([, security]) => isBearerScheme(security));
525
+ if (bearerSchemes.length === 0 || !canPassThroughCallerToken(options)) {
526
+ return result;
527
+ }
528
+ const schemesStr = bearerSchemes.map(([scheme]) => scheme).join(", ");
529
+ const when = options.authProviderMapper ? " whenever no authProviderMapper function returns a credential" : "";
530
+ result.securityRiskScore = "high";
531
+ result.warnings.push(
532
+ `SECURITY WARNING: passthroughCallerToken is enabled. The MCP client's own token (ctx.authInfo.token) is sent to the API${when} for security schemes: ${schemesStr}. Only use this when the API accepts tokens issued for this MCP server.`
533
+ );
338
534
  return result;
339
535
  }
340
- async function resolveToolSecurity(tool2, ctx, options) {
536
+ async function resolveToolSecurity(tool2, ctx, options, request = {}) {
341
537
  const securityResolver = new SecurityResolver();
342
538
  const securityContext = await createSecurityContextFromAuth(tool2, ctx, options);
343
539
  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;
344
540
  const requiresSecurity = tool2.mapper.some((m) => m.security && m.required === true);
345
- if (requiresSecurity && !hasAuth) {
346
- const requiredSchemes = tool2.mapper.filter((m) => m.security && m.required === true).map((m) => m.security?.scheme ?? "unknown");
347
- const uniqueSchemes = [...new Set(requiredSchemes)];
348
- const schemesStr = uniqueSchemes.join(", ") || "unknown";
349
- const firstScheme = uniqueSchemes[0] || "BearerAuth";
350
- throw new Error(
351
- `Authentication required for tool '${tool2.name}' but no auth configuration found.
352
- Required security schemes: ${schemesStr}
353
- Solutions:
354
- 1. Add authProviderMapper: { '${firstScheme}': (ctx) => ctx.authInfo.user?.token }
355
- 2. Add securityResolver: (tool, ctx) => ({ jwt: ctx.authInfo.token })
356
- 3. Add staticAuth: { jwt: process.env.API_TOKEN }
357
- 4. Set generateOptions.includeSecurityInInput: true (not recommended for production)`
358
- );
541
+ const fromInput = request.input ? await resolveInputSecurity(tool2, request.input, options, securityResolver) : void 0;
542
+ const hasInputCredential = !!fromInput && Object.keys(fromInput.headers).length + Object.keys(fromInput.query).length + Object.keys(fromInput.cookies).length > 0;
543
+ if (requiresSecurity && !hasAuth && !hasInputCredential && !request.deferCredentialCheck) {
544
+ throw authenticationRequiredError(tool2);
545
+ }
546
+ const resolved = await securityResolver.resolve(tool2.mapper, securityContext);
547
+ if (!fromInput) return resolved;
548
+ return {
549
+ ...resolved,
550
+ headers: { ...fromInput.headers, ...resolved.headers },
551
+ query: { ...fromInput.query, ...resolved.query },
552
+ cookies: { ...fromInput.cookies, ...resolved.cookies }
553
+ };
554
+ }
555
+ function isInputScheme(scheme, options) {
556
+ return options.generateOptions?.includeSecurityInInput === true || (options.securitySchemesInInput ?? []).includes(scheme);
557
+ }
558
+ function inputCredentialContext(security, value) {
559
+ const bare = value.replace(/^(?:bearer|basic)\s+/i, "");
560
+ if (security.type === "apiKey") {
561
+ return security.apiKeyName ? { apiKeys: { [security.apiKeyName]: value } } : { apiKey: value };
562
+ }
563
+ if (security.type === "oauth2" || security.type === "openIdConnect") return { oauth2Token: bare };
564
+ if (security.type === "http") {
565
+ const httpScheme = (security.httpScheme ?? "bearer").toLowerCase();
566
+ if (httpScheme === "bearer") return { jwt: bare };
567
+ if (httpScheme === "basic") return { basic: bare };
568
+ }
569
+ return void 0;
570
+ }
571
+ async function resolveInputSecurity(tool2, input, options, securityResolver) {
572
+ const resolved = { headers: {}, query: {}, cookies: {} };
573
+ for (const mapper of tool2.mapper) {
574
+ const security = mapper.security;
575
+ if (!security || !isInputScheme(security.scheme, options)) continue;
576
+ const value = input[mapper.inputKey];
577
+ if (typeof value !== "string" || value === "") continue;
578
+ if (/[\r\n\x00\f\v]/.test(value)) {
579
+ throw new PublicMcpError(
580
+ `Invalid value for '${mapper.inputKey}': contains control characters (possible header injection attack)`,
581
+ "INVALID_HEADER_VALUE",
582
+ 400
583
+ );
584
+ }
585
+ const context = inputCredentialContext(security, value);
586
+ if (!context) continue;
587
+ const one = await securityResolver.resolve([mapper], createSecurityContext(context));
588
+ Object.assign(resolved.headers, one.headers);
589
+ Object.assign(resolved.query, one.query);
590
+ Object.assign(resolved.cookies, one.cookies);
359
591
  }
360
- return await securityResolver.resolve(tool2.mapper, securityContext);
592
+ return resolved;
361
593
  }
362
594
 
363
595
  // libs/adapters/src/openapi/openapi.tool.ts
@@ -606,7 +838,7 @@ function formatZodIssues(issues, prefix) {
606
838
  }
607
839
 
608
840
  // libs/adapters/src/openapi/openapi.utils.ts
609
- import { PublicMcpError } from "@frontmcp/sdk";
841
+ import { PublicMcpError as PublicMcpError2 } from "@frontmcp/sdk";
610
842
  import { trimTrailing, validateBaseUrl } from "@frontmcp/utils";
611
843
  function coerceToString(value, paramName, location) {
612
844
  if (value === null || value === void 0) {
@@ -664,7 +896,7 @@ function buildRequest(tool2, input, security, baseUrl) {
664
896
  case "header": {
665
897
  const headerValue = coerceToString(value, mapper.key, "header");
666
898
  if (/[\r\n\x00\f\v]/.test(headerValue)) {
667
- throw new PublicMcpError(
899
+ throw new PublicMcpError2(
668
900
  `Invalid header value for '${mapper.key}': contains control characters (possible header injection attack)`,
669
901
  "INVALID_HEADER_VALUE",
670
902
  400
@@ -841,7 +1073,10 @@ function createOpenApiTool(openapiTool, options, logger) {
841
1073
  inputTransforms,
842
1074
  transformContext
843
1075
  );
844
- const security = await resolveToolSecurity(openapiTool, ctx, options);
1076
+ const security = await resolveToolSecurity(openapiTool, ctx, options, {
1077
+ input: injectedInput,
1078
+ deferCredentialCheck: true
1079
+ });
845
1080
  const { url, headers, body: requestBody } = buildRequest(openapiTool, injectedInput, security, options.baseUrl);
846
1081
  applyAdditionalHeaders(headers, options.additionalHeaders);
847
1082
  if (options.headersMapper) {
@@ -857,6 +1092,7 @@ function createOpenApiTool(openapiTool, options, logger) {
857
1092
  throw new Error(`headersMapper failed for tool '${openapiTool.name}': ${errorMessage}`, { cause: err });
858
1093
  }
859
1094
  }
1095
+ assertRequestHasCredential(openapiTool, url, headers);
860
1096
  let finalBody = requestBody;
861
1097
  if (options.bodyMapper && requestBody) {
862
1098
  try {
@@ -1141,28 +1377,7 @@ var OpenapiAdapter = class extends DynamicAdapter {
1141
1377
  });
1142
1378
  }
1143
1379
  if (!validation.valid) {
1144
- throw new Error(
1145
- `[OpenAPI Adapter: ${this.options.name}] Invalid security configuration.
1146
- Missing auth provider mappings for security schemes: ${validation.missingMappings.join(", ")}
1147
-
1148
- Your OpenAPI spec requires these security schemes, but no auth configuration was provided.
1149
-
1150
- Add one of the following to your adapter configuration:
1151
-
1152
- 1. authProviderMapper (recommended):
1153
- authProviderMapper: {
1154
- ` + validation.missingMappings.map((s) => ` '${s}': (authInfo) => authInfo.user?.${s.toLowerCase()}Token,`).join("\n") + `
1155
- }
1156
-
1157
- 2. securityResolver:
1158
- securityResolver: (tool, authInfo) => ({ jwt: authInfo.token })
1159
-
1160
- 3. staticAuth:
1161
- staticAuth: { jwt: process.env.API_TOKEN }
1162
-
1163
- 4. Include security in input (NOT recommended for production):
1164
- generateOptions: { includeSecurityInInput: true }`
1165
- );
1380
+ throw new Error(formatMissingSecurityMappingsError(this.options.name, openapiTools, validation.missingMappings));
1166
1381
  }
1167
1382
  let transformedTools = openapiTools;
1168
1383
  if (this.options.descriptionMode && this.options.descriptionMode !== "summaryOnly") {